source: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py @ 12161

Last change on this file since 12161 was 12161, checked in by Henrik Bettermann, 10 years ago

Define connected_files and getMD5 for all kind of customer documents.

  • Property svn:keywords set to Id
File size: 9.7 KB
RevLine 
[12015]1## $Id: documents.py 12161 2014-12-07 10:07:29Z henrik $
[11989]2##
3## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""
19Customer document components.
20"""
[12035]21import os
[11989]22import grok
[12161]23from hashlib import md5
[12018]24from zope.component import queryUtility, getUtility
[11989]25from zope.component.interfaces import IFactory
26from zope.interface import implementedBy
[12035]27
28from waeup.ikoba.image import IkobaImageFile
29from waeup.ikoba.imagestorage import DefaultFileStoreHandler
[11989]30from waeup.ikoba.interfaces import MessageFactory as _
[12035]31from waeup.ikoba.interfaces import (
32    IFileStoreNameChooser, IFileStoreHandler,
33    IIkobaUtils, IExtFileStore)
[11989]34from waeup.ikoba.customers.interfaces import (
[12018]35    ICustomerDocumentsContainer, ICustomerNavigation, ICustomerDocument,
[12053]36    ICustomersUtils, ICustomerPDFDocument)
[11989]37from waeup.ikoba.documents import DocumentsContainer, Document
[12018]38from waeup.ikoba.documents.interfaces import IDocumentsUtils
[11989]39from waeup.ikoba.utils.helpers import attrs_to_fields
40
[12035]41from waeup.ikoba.customers.utils import path_from_custid
42
[11989]43class CustomerDocumentsContainer(DocumentsContainer):
44    """This is a container for customer documents.
45    """
46    grok.implements(ICustomerDocumentsContainer, ICustomerNavigation)
47    grok.provides(ICustomerDocumentsContainer)
48
49    def __init__(self):
50        super(CustomerDocumentsContainer, self).__init__()
51        return
52
53    @property
54    def customer(self):
55        return self.__parent__
56
57    def writeLogMessage(self, view, message):
58        return self.__parent__.writeLogMessage(view, message)
59
60CustomerDocumentsContainer = attrs_to_fields(CustomerDocumentsContainer)
61
[12057]62class CustomerDocumentBase(Document):
63    """This is a customer document baseclass.
[11989]64    """
65    grok.implements(ICustomerDocument, ICustomerNavigation)
66    grok.provides(ICustomerDocument)
[12057]67    grok.baseclass()
[11989]68
[12161]69    filenames = ()
70
[11989]71    @property
72    def customer(self):
73        try:
74            return self.__parent__.__parent__
75        except AttributeError:
76            return None
77
[12128]78    @property
79    def user_id(self):
80        if self.customer is not None:
81            return self.customer.customer_id
82        return
83
[11989]84    def writeLogMessage(self, view, message):
85        return self.__parent__.__parent__.writeLogMessage(view, message)
86
[12018]87    @property
88    def is_editable(self):
89        try:
90            # Customer must be approved
91            cond1 = self.customer.state in getUtility(
[12088]92                ICustomersUtils).DOCMANAGE_CUSTOMER_STATES
[12018]93            # Document must be in state created
94            cond2 = self.state in getUtility(
[12088]95                ICustomersUtils).DOCMANAGE_DOCUMENT_STATES
[12018]96            if not (cond1 and cond2):
97                return False
98        except AttributeError:
99            pass
100        return True
101
[12053]102    @property
[12056]103    def translated_class_name(self):
[12053]104        try:
105            DOCTYPES_DICT = getUtility(ICustomersUtils).DOCTYPES_DICT
[12056]106            return DOCTYPES_DICT[self.class_name]
[12053]107        except KeyError:
108            return
109
[12161]110    @property
111    def connected_files(self):
112        store = getUtility(IExtFileStore)
113        files = []
114        try:
115            for filename in self.filenames:
116                attrname = filename.replace('.','_')
117                file = store.getFileByContext(self, attr=filename)
118                files.append((attrname, file))
119        except AttributeError:
120            # In unit tests we don't have a customer to
121            # determine the file path.
122            return
123        return files
[11989]124
[12161]125    def setMD5(self):
126        """Set md5 checksum attribute for all files connected to this document.
127        """
128        for file in self.connected_files:
129            attrname = '%s_md5' % file[0]
130            checksum = md5(file[1].read()).hexdigest()
131            setattr(self, attrname, checksum)
132        return
133
134
[12057]135class CustomerSampleDocument(CustomerDocumentBase):
136    """This is a sample customer document.
[12053]137    """
[12057]138
[12161]139    filenames = ('sample',)
140
[12057]141CustomerSampleDocument = attrs_to_fields(CustomerSampleDocument)
142
143
144class CustomerPDFDocument(CustomerDocumentBase):
145    """This is a customer document for a single pdf upload file.
146    """
[12053]147    grok.implements(ICustomerPDFDocument, ICustomerNavigation)
148    grok.provides(ICustomerPDFDocument)
[11989]149
[12053]150CustomerPDFDocument = attrs_to_fields(CustomerPDFDocument)
151
152
[11989]153# Customer documents must be importable. So we might need a factory.
154class CustomerDocumentFactory(grok.GlobalUtility):
155    """A factory for customer documents.
156    """
157    grok.implements(IFactory)
[12057]158    grok.name(u'waeup.CustomerSampleDocument')
[11989]159    title = u"Create a new document.",
[12053]160    description = u"This factory instantiates new sample document instances."
[11989]161
162    def __call__(self, *args, **kw):
[12057]163        return CustomerSampleDocument(*args, **kw)
[11989]164
165    def getInterfaces(self):
[12057]166        return implementedBy(CustomerSampleDocument)
[12035]167
[12053]168# Customer documents must be importable. So we might need a factory.
169class CustomerPDFDocumentFactory(grok.GlobalUtility):
170    """A factory for customer pdf documents.
171    """
172    grok.implements(IFactory)
173    grok.name(u'waeup.CustomerPDFDocument')
174    title = u"Create a new document.",
175    description = u"This factory instantiates new pdf document instances."
176
177    def __call__(self, *args, **kw):
178        return CustomerPDFDocument(*args, **kw)
179
180    def getInterfaces(self):
181        return implementedBy(CustomerPDFDocument)
182
[12035]183#: The file id marker for customer files
184CUSTOMERDOCUMENT_FILE_STORE_NAME = 'file-customerdocument'
185
186
187class CustomerDocumentFileNameChooser(grok.Adapter):
188    """A file id chooser for :class:`CustomerDocument` objects.
189
190    `context` is an :class:`CustomerDocument` instance.
191
192    The :class:`CustomerDocumentFileNameChooser` can build/check file ids for
193    :class:`Customer` objects suitable for use with
194    :class:`ExtFileStore` instances. The delivered file_id contains
195    the file id marker for :class:`CustomerDocument` object and the customer id
196    of the context customer.
197
198    This chooser is registered as an adapter providing
199    :class:`waeup.ikoba.interfaces.IFileStoreNameChooser`.
200
201    File store name choosers like this one are only convenience
202    components to ease the task of creating file ids for customer document
203    objects. You are nevertheless encouraged to use them instead of
204    manually setting up filenames for customer documents.
205
206    .. seealso:: :mod:`waeup.ikoba.imagestorage`
207
208    """
209
210    grok.context(ICustomerDocument)
211    grok.implements(IFileStoreNameChooser)
212
213    def checkName(self, name=None, attr=None):
214        """Check whether the given name is a valid file id for the context.
215
216        Returns ``True`` only if `name` equals the result of
217        :meth:`chooseName`.
218
219        """
220        return name == self.chooseName()
221
222    def chooseName(self, attr, name=None):
223        """Get a valid file id for customer document context.
224
225        *Example:*
226
227        For a customer with customer id ``'A123456'``
228        and document with id 'd123'
229        with attr ``'nice_image.jpeg'`` stored in
230        the customers container this chooser would create:
231
232          ``'__file-customerdocument__customers/A/A123456/nice_image_d123_A123456.jpeg'``
233
234        meaning that the nice image of this customer document would be
235        stored in the site-wide file storage in path:
236
237          ``customers/A/A123456/nice_image_d123_A123456.jpeg``
238
239        """
240        basename, ext = os.path.splitext(attr)
241        cust_id = self.context.customer.customer_id
242        doc_id = self.context.document_id
243        marked_filename = '__%s__%s/%s_%s_%s%s' % (
244            CUSTOMERDOCUMENT_FILE_STORE_NAME, path_from_custid(cust_id),
245            basename, doc_id, cust_id, ext)
246        return marked_filename
247
248
249class CustomerDocumentFileStoreHandler(DefaultFileStoreHandler, grok.GlobalUtility):
250    """Customer document specific file handling.
251
252    This handler knows in which path in a filestore to store customer document
253    files and how to turn this kind of data into some (browsable)
254    file object.
255
256    It is called from the global file storage, when it wants to
257    get/store a file with a file id starting with
258    ``__file-customerdocument__`` (the marker string for customer files).
259
260    Like each other file store handler it does not handle the files
261    really (this is done by the global file store) but only computes
262    paths and things like this.
263    """
264    grok.implements(IFileStoreHandler)
265    grok.name(CUSTOMERDOCUMENT_FILE_STORE_NAME)
266
267    def pathFromFileID(self, store, root, file_id):
268        """All customer document files are put in directory ``customers``.
269        """
270        marker, filename, basename, ext = store.extractMarker(file_id)
271        sub_root = os.path.join(root, 'customers')
272        return super(CustomerDocumentFileStoreHandler, self).pathFromFileID(
273            store, sub_root, basename)
274
275    def createFile(self, store, root, filename, file_id, file):
276        """Create a browsable file-like object.
277        """
278        # call super method to ensure that any old files with
279        # different filename extension are deleted.
280        file, path, file_obj = super(
281            CustomerDocumentFileStoreHandler, self).createFile(
282            store, root,  filename, file_id, file)
283        return file, path, IkobaImageFile(
284            file_obj.filename, file_obj.data)
285
Note: See TracBrowser for help on using the repository browser.