Ignore:
Timestamp:
22 Nov 2014, 10:14:38 (10 years ago)
Author:
Henrik Bettermann
Message:

Add document file viewlets. Tests will follow.

Location:
main/waeup.ikoba/trunk/src/waeup/ikoba/customers
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser.py

    r12034 r12035  
    780780    form_fields = grok.AutoFields(ICustomerDocument)
    781781    pnav = 4
     782    deletion_warning = _('Are you sure?')
    782783
    783784    #@property
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/documenteditpage.pt

    r12018 r12035  
    1010    <li>
    1111      <a href="#tab2" data-toggle="tab">
    12         <span i18n:translate="">Scans</span>
     12        <span i18n:translate="">Files</span>
    1313      </a>
    1414    </li>
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/documentpage.pt

    r12034 r12035  
    1616    <tr tal:condition="python: files.replace('\n','')">
    1717      <td class="separator" colspan=2 i18n:translate="">
    18         Uploaded Files
     18        Connected Files
    1919      </td>
    2020    </tr>
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/customer.py

    r12032 r12035  
    2020"""
    2121import os
    22 import re
    2322import shutil
    2423import grok
     
    4241    ICustomer, ICustomerNavigation, ICSVCustomerExporter,
    4342    ICustomersUtils)
    44 from waeup.ikoba.customers.utils import generate_customer_id
     43from waeup.ikoba.customers.utils import generate_customer_id, path_from_custid
    4544from waeup.ikoba.customers.documents import CustomerDocumentsContainer
    4645from waeup.ikoba.utils.helpers import attrs_to_fields, now, copy_filesystem_tree
    47 
    48 RE_CUSTID_NON_NUM = re.compile('[^\d]+')
    49 
    5046
    5147class Customer(grok.Container):
     
    188184    return
    189185
    190 
    191 def path_from_custid(customer_id):
    192     """Convert a customer_id into a predictable relative folder path.
    193 
    194     Used for storing files.
    195 
    196     Returns the name of folder in which files for a particular customer
    197     should be stored. This is a relative path, relative to any general
    198     customers folder with 5 zero-padded digits (except when customer_id
    199     is overlong).
    200 
    201     We normally map 1,000 different customer ids into one single
    202     path. For instance ``K1000000`` will give ``01000/K1000000``,
    203     ``K1234567`` will give ``0123/K1234567`` and ``K12345678`` will
    204     result in ``1234/K12345678``.
    205 
    206     For lower numbers < 10**6 we return the same path for up to 10,000
    207     customer_ids. So for instance ``KM123456`` will result in
    208     ``00120/KM123456`` (there will be no path starting with
    209     ``00123``).
    210 
    211     Works also with overlong number: here the leading zeros will be
    212     missing but ``K123456789`` will give reliably
    213     ``12345/K123456789`` as expected.
    214     """
    215     # remove all non numeric characters and turn this into an int.
    216     num = int(RE_CUSTID_NON_NUM.sub('', customer_id))
    217     if num < 10**6:
    218         # store max. of 10000 custs per folder and correct num for 5 digits
    219         num = num / 10000 * 10
    220     else:
    221         # store max. of 1000 custs per folder
    222         num = num / 1000
    223     # format folder name to have 5 zero-padded digits
    224     folder_name = u'%05d' % num
    225     folder_name = os.path.join(folder_name, customer_id)
    226     return folder_name
    227 
    228 
    229186def move_customer_files(customer, del_dir):
    230187    """Move files belonging to `customer` to `del_dir`.
     
    340297    `context` is an :class:`Customer` instance.
    341298
    342     The :class:`CustomerImageNameChooser` can build/check file ids for
     299    The :class:`CustomerFileNameChooser` can build/check file ids for
    343300    :class:`Customer` objects suitable for use with
    344301    :class:`ExtFileStore` instances. The delivered file_id contains
     
    380337          ``'__file-customer__customers/A/A123456/nice_image_A123456.jpeg'``
    381338
    382         meaning that the nice image of this applicant would be
     339        meaning that the nice image of this customer would be
    383340        stored in the site-wide file storage in path:
    384341
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py

    r12018 r12035  
    1919Customer document components.
    2020"""
     21import os
    2122import grok
    2223from zope.component import queryUtility, getUtility
    2324from zope.component.interfaces import IFactory
    2425from zope.interface import implementedBy
     26
     27from waeup.ikoba.image import IkobaImageFile
     28from waeup.ikoba.imagestorage import DefaultFileStoreHandler
    2529from waeup.ikoba.interfaces import MessageFactory as _
     30from waeup.ikoba.interfaces import (
     31    IFileStoreNameChooser, IFileStoreHandler,
     32    IIkobaUtils, IExtFileStore)
    2633from waeup.ikoba.customers.interfaces import (
    2734    ICustomerDocumentsContainer, ICustomerNavigation, ICustomerDocument,
     
    3037from waeup.ikoba.documents.interfaces import IDocumentsUtils
    3138from waeup.ikoba.utils.helpers import attrs_to_fields
     39
     40from waeup.ikoba.customers.utils import path_from_custid
    3241
    3342class CustomerDocumentsContainer(DocumentsContainer):
     
    102111    def getInterfaces(self):
    103112        return implementedBy(CustomerDocument)
     113
     114#: The file id marker for customer files
     115CUSTOMERDOCUMENT_FILE_STORE_NAME = 'file-customerdocument'
     116
     117
     118class CustomerDocumentFileNameChooser(grok.Adapter):
     119    """A file id chooser for :class:`CustomerDocument` objects.
     120
     121    `context` is an :class:`CustomerDocument` instance.
     122
     123    The :class:`CustomerDocumentFileNameChooser` can build/check file ids for
     124    :class:`Customer` objects suitable for use with
     125    :class:`ExtFileStore` instances. The delivered file_id contains
     126    the file id marker for :class:`CustomerDocument` object and the customer id
     127    of the context customer.
     128
     129    This chooser is registered as an adapter providing
     130    :class:`waeup.ikoba.interfaces.IFileStoreNameChooser`.
     131
     132    File store name choosers like this one are only convenience
     133    components to ease the task of creating file ids for customer document
     134    objects. You are nevertheless encouraged to use them instead of
     135    manually setting up filenames for customer documents.
     136
     137    .. seealso:: :mod:`waeup.ikoba.imagestorage`
     138
     139    """
     140
     141    grok.context(ICustomerDocument)
     142    grok.implements(IFileStoreNameChooser)
     143
     144    def checkName(self, name=None, attr=None):
     145        """Check whether the given name is a valid file id for the context.
     146
     147        Returns ``True`` only if `name` equals the result of
     148        :meth:`chooseName`.
     149
     150        """
     151        return name == self.chooseName()
     152
     153    def chooseName(self, attr, name=None):
     154        """Get a valid file id for customer document context.
     155
     156        *Example:*
     157
     158        For a customer with customer id ``'A123456'``
     159        and document with id 'd123'
     160        with attr ``'nice_image.jpeg'`` stored in
     161        the customers container this chooser would create:
     162
     163          ``'__file-customerdocument__customers/A/A123456/nice_image_d123_A123456.jpeg'``
     164
     165        meaning that the nice image of this customer document would be
     166        stored in the site-wide file storage in path:
     167
     168          ``customers/A/A123456/nice_image_d123_A123456.jpeg``
     169
     170        """
     171        basename, ext = os.path.splitext(attr)
     172        cust_id = self.context.customer.customer_id
     173        doc_id = self.context.document_id
     174        marked_filename = '__%s__%s/%s_%s_%s%s' % (
     175            CUSTOMERDOCUMENT_FILE_STORE_NAME, path_from_custid(cust_id),
     176            basename, doc_id, cust_id, ext)
     177        return marked_filename
     178
     179
     180class CustomerDocumentFileStoreHandler(DefaultFileStoreHandler, grok.GlobalUtility):
     181    """Customer document specific file handling.
     182
     183    This handler knows in which path in a filestore to store customer document
     184    files and how to turn this kind of data into some (browsable)
     185    file object.
     186
     187    It is called from the global file storage, when it wants to
     188    get/store a file with a file id starting with
     189    ``__file-customerdocument__`` (the marker string for customer files).
     190
     191    Like each other file store handler it does not handle the files
     192    really (this is done by the global file store) but only computes
     193    paths and things like this.
     194    """
     195    grok.implements(IFileStoreHandler)
     196    grok.name(CUSTOMERDOCUMENT_FILE_STORE_NAME)
     197
     198    def pathFromFileID(self, store, root, file_id):
     199        """All customer document files are put in directory ``customers``.
     200        """
     201        marker, filename, basename, ext = store.extractMarker(file_id)
     202        sub_root = os.path.join(root, 'customers')
     203        return super(CustomerDocumentFileStoreHandler, self).pathFromFileID(
     204            store, sub_root, basename)
     205
     206    def createFile(self, store, root, filename, file_id, file):
     207        """Create a browsable file-like object.
     208        """
     209        # call super method to ensure that any old files with
     210        # different filename extension are deleted.
     211        file, path, file_obj = super(
     212            CustomerDocumentFileStoreHandler, self).createFile(
     213            store, root,  filename, file_id, file)
     214        return file, path, IkobaImageFile(
     215            file_obj.filename, file_obj.data)
     216
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/files.py

    r11997 r12035  
    1 ## $Id: viewlets.py 11772 2014-07-31 04:38:23Z henrik $
     1## $Id$
    22##
    33## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
     
    3131    default_fileupload_template)
    3232
    33 from waeup.ikoba.customers.interfaces import ICustomer, ICustomersUtils
     33from waeup.ikoba.customers.interfaces import (
     34    ICustomer, ICustomersUtils, ICustomerDocument)
    3435from waeup.ikoba.customers.browser import (
    3536    CustomerBaseDisplayFormPage, CustomerBaseManageFormPage,
    36     CustomerFilesUploadPage)
     37    CustomerFilesUploadPage,
     38    DocumentDisplayFormPage, DocumentManageFormPage)
    3739
    3840grok.context(IIkobaObject)  # Make IIkobaObject the default context
     
    254256
    255257# File viewlets for customer documents
     258
     259class GenericUploadManage(FileUpload):
     260    """Genric document upload viewlet for officers.
     261    """
     262    grok.order(1)
     263    grok.context(ICustomerDocument)
     264    grok.view(DocumentManageFormPage)
     265    grok.require('waeup.manageCustomer')
     266    label = _(u'Generic Document')
     267    title = _(u'Generic Document')
     268    mus = 1024 * 50
     269    download_name = u'generic'
     270    tab_redirect = '#tab2'
     271
     272class GenericDisplay(FileDisplay):
     273    """Genreric document display viewlet.
     274    """
     275    grok.order(1)
     276    grok.context(ICustomerDocument)
     277    grok.require('waeup.viewCustomer')
     278    grok.view(DocumentDisplayFormPage)
     279    label = _(u'Generic Document')
     280    title = _(u'Generic Document')
     281    download_name = u'generic'
     282
     283class GenericImage(Image):
     284    """Generic document.
     285    """
     286    grok.name('generic')
     287    grok.context(ICustomerDocument)
     288    grok.require('waeup.viewCustomer')
     289    download_name = u'generic'
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/utils.py

    r12032 r12035  
    1818"""General helper functions and utilities for the customer section.
    1919"""
     20import re
     21import os
    2022import grok
    2123from waeup.ikoba.interfaces import MessageFactory as _
     
    2325from waeup.ikoba.customers.interfaces import ICustomersUtils
    2426
     27RE_CUSTID_NON_NUM = re.compile('[^\d]+')
    2528
    2629def generate_customer_id():
     
    2932    return new_id
    3033
     34def path_from_custid(customer_id):
     35    """Convert a customer_id into a predictable relative folder path.
     36
     37    Used for storing files.
     38
     39    Returns the name of folder in which files for a particular customer
     40    should be stored. This is a relative path, relative to any general
     41    customers folder with 5 zero-padded digits (except when customer_id
     42    is overlong).
     43
     44    We normally map 1,000 different customer ids into one single
     45    path. For instance ``K1000000`` will give ``01000/K1000000``,
     46    ``K1234567`` will give ``0123/K1234567`` and ``K12345678`` will
     47    result in ``1234/K12345678``.
     48
     49    For lower numbers < 10**6 we return the same path for up to 10,000
     50    customer_ids. So for instance ``KM123456`` will result in
     51    ``00120/KM123456`` (there will be no path starting with
     52    ``00123``).
     53
     54    Works also with overlong number: here the leading zeros will be
     55    missing but ``K123456789`` will give reliably
     56    ``12345/K123456789`` as expected.
     57    """
     58    # remove all non numeric characters and turn this into an int.
     59    num = int(RE_CUSTID_NON_NUM.sub('', customer_id))
     60    if num < 10**6:
     61        # store max. of 10000 custs per folder and correct num for 5 digits
     62        num = num / 10000 * 10
     63    else:
     64        # store max. of 1000 custs per folder
     65        num = num / 1000
     66    # format folder name to have 5 zero-padded digits
     67    folder_name = u'%05d' % num
     68    folder_name = os.path.join(folder_name, customer_id)
     69    return folder_name
    3170
    3271class CustomersUtils(grok.GlobalUtility):
     
    4382    DOCMANAGE_STATES = (APPROVED,)
    4483
     84    SKIP_UPLOAD_VIEWLETS = ()
     85
    4586    TRANSLATED_STATES = {
    4687        CREATED: _('created'),
Note: See TracChangeset for help on using the changeset viewer.