- Timestamp:
- 22 Nov 2014, 10:14:38 (10 years ago)
- 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 780 780 form_fields = grok.AutoFields(ICustomerDocument) 781 781 pnav = 4 782 deletion_warning = _('Are you sure?') 782 783 783 784 #@property -
main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/documenteditpage.pt
r12018 r12035 10 10 <li> 11 11 <a href="#tab2" data-toggle="tab"> 12 <span i18n:translate=""> Scans</span>12 <span i18n:translate="">Files</span> 13 13 </a> 14 14 </li> -
main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/documentpage.pt
r12034 r12035 16 16 <tr tal:condition="python: files.replace('\n','')"> 17 17 <td class="separator" colspan=2 i18n:translate=""> 18 Uploaded Files18 Connected Files 19 19 </td> 20 20 </tr> -
main/waeup.ikoba/trunk/src/waeup/ikoba/customers/customer.py
r12032 r12035 20 20 """ 21 21 import os 22 import re23 22 import shutil 24 23 import grok … … 42 41 ICustomer, ICustomerNavigation, ICSVCustomerExporter, 43 42 ICustomersUtils) 44 from waeup.ikoba.customers.utils import generate_customer_id 43 from waeup.ikoba.customers.utils import generate_customer_id, path_from_custid 45 44 from waeup.ikoba.customers.documents import CustomerDocumentsContainer 46 45 from waeup.ikoba.utils.helpers import attrs_to_fields, now, copy_filesystem_tree 47 48 RE_CUSTID_NON_NUM = re.compile('[^\d]+')49 50 46 51 47 class Customer(grok.Container): … … 188 184 return 189 185 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 customer197 should be stored. This is a relative path, relative to any general198 customers folder with 5 zero-padded digits (except when customer_id199 is overlong).200 201 We normally map 1,000 different customer ids into one single202 path. For instance ``K1000000`` will give ``01000/K1000000``,203 ``K1234567`` will give ``0123/K1234567`` and ``K12345678`` will204 result in ``1234/K12345678``.205 206 For lower numbers < 10**6 we return the same path for up to 10,000207 customer_ids. So for instance ``KM123456`` will result in208 ``00120/KM123456`` (there will be no path starting with209 ``00123``).210 211 Works also with overlong number: here the leading zeros will be212 missing but ``K123456789`` will give reliably213 ``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 digits219 num = num / 10000 * 10220 else:221 # store max. of 1000 custs per folder222 num = num / 1000223 # format folder name to have 5 zero-padded digits224 folder_name = u'%05d' % num225 folder_name = os.path.join(folder_name, customer_id)226 return folder_name227 228 229 186 def move_customer_files(customer, del_dir): 230 187 """Move files belonging to `customer` to `del_dir`. … … 340 297 `context` is an :class:`Customer` instance. 341 298 342 The :class:`Customer ImageNameChooser` can build/check file ids for299 The :class:`CustomerFileNameChooser` can build/check file ids for 343 300 :class:`Customer` objects suitable for use with 344 301 :class:`ExtFileStore` instances. The delivered file_id contains … … 380 337 ``'__file-customer__customers/A/A123456/nice_image_A123456.jpeg'`` 381 338 382 meaning that the nice image of this applicantwould be339 meaning that the nice image of this customer would be 383 340 stored in the site-wide file storage in path: 384 341 -
main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py
r12018 r12035 19 19 Customer document components. 20 20 """ 21 import os 21 22 import grok 22 23 from zope.component import queryUtility, getUtility 23 24 from zope.component.interfaces import IFactory 24 25 from zope.interface import implementedBy 26 27 from waeup.ikoba.image import IkobaImageFile 28 from waeup.ikoba.imagestorage import DefaultFileStoreHandler 25 29 from waeup.ikoba.interfaces import MessageFactory as _ 30 from waeup.ikoba.interfaces import ( 31 IFileStoreNameChooser, IFileStoreHandler, 32 IIkobaUtils, IExtFileStore) 26 33 from waeup.ikoba.customers.interfaces import ( 27 34 ICustomerDocumentsContainer, ICustomerNavigation, ICustomerDocument, … … 30 37 from waeup.ikoba.documents.interfaces import IDocumentsUtils 31 38 from waeup.ikoba.utils.helpers import attrs_to_fields 39 40 from waeup.ikoba.customers.utils import path_from_custid 32 41 33 42 class CustomerDocumentsContainer(DocumentsContainer): … … 102 111 def getInterfaces(self): 103 112 return implementedBy(CustomerDocument) 113 114 #: The file id marker for customer files 115 CUSTOMERDOCUMENT_FILE_STORE_NAME = 'file-customerdocument' 116 117 118 class 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 180 class 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$ 2 2 ## 3 3 ## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann … … 31 31 default_fileupload_template) 32 32 33 from waeup.ikoba.customers.interfaces import ICustomer, ICustomersUtils 33 from waeup.ikoba.customers.interfaces import ( 34 ICustomer, ICustomersUtils, ICustomerDocument) 34 35 from waeup.ikoba.customers.browser import ( 35 36 CustomerBaseDisplayFormPage, CustomerBaseManageFormPage, 36 CustomerFilesUploadPage) 37 CustomerFilesUploadPage, 38 DocumentDisplayFormPage, DocumentManageFormPage) 37 39 38 40 grok.context(IIkobaObject) # Make IIkobaObject the default context … … 254 256 255 257 # File viewlets for customer documents 258 259 class 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 272 class 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 283 class 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 18 18 """General helper functions and utilities for the customer section. 19 19 """ 20 import re 21 import os 20 22 import grok 21 23 from waeup.ikoba.interfaces import MessageFactory as _ … … 23 25 from waeup.ikoba.customers.interfaces import ICustomersUtils 24 26 27 RE_CUSTID_NON_NUM = re.compile('[^\d]+') 25 28 26 29 def generate_customer_id(): … … 29 32 return new_id 30 33 34 def 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 31 70 32 71 class CustomersUtils(grok.GlobalUtility): … … 43 82 DOCMANAGE_STATES = (APPROVED,) 44 83 84 SKIP_UPLOAD_VIEWLETS = () 85 45 86 TRANSLATED_STATES = { 46 87 CREATED: _('created'),
Note: See TracChangeset for help on using the changeset viewer.