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

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

Add second customer document class.
Select document factory when adding documents.
Add last_transition_date attribute and further property attributes to documents.

  • Property svn:keywords set to Id
File size: 8.6 KB
Line 
1## $Id: documents.py 12053 2014-11-25 08:19:54Z henrik $
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"""
21import os
22import grok
23from zope.component import queryUtility, getUtility
24from zope.component.interfaces import IFactory
25from zope.interface import implementedBy
26
27from waeup.ikoba.image import IkobaImageFile
28from waeup.ikoba.imagestorage import DefaultFileStoreHandler
29from waeup.ikoba.interfaces import MessageFactory as _
30from waeup.ikoba.interfaces import (
31    IFileStoreNameChooser, IFileStoreHandler,
32    IIkobaUtils, IExtFileStore)
33from waeup.ikoba.customers.interfaces import (
34    ICustomerDocumentsContainer, ICustomerNavigation, ICustomerDocument,
35    ICustomersUtils, ICustomerPDFDocument)
36from waeup.ikoba.documents import DocumentsContainer, Document
37from waeup.ikoba.documents.interfaces import IDocumentsUtils
38from waeup.ikoba.utils.helpers import attrs_to_fields
39
40from waeup.ikoba.customers.utils import path_from_custid
41
42class CustomerDocumentsContainer(DocumentsContainer):
43    """This is a container for customer documents.
44    """
45    grok.implements(ICustomerDocumentsContainer, ICustomerNavigation)
46    grok.provides(ICustomerDocumentsContainer)
47
48    def __init__(self):
49        super(CustomerDocumentsContainer, self).__init__()
50        return
51
52    @property
53    def customer(self):
54        return self.__parent__
55
56    def writeLogMessage(self, view, message):
57        return self.__parent__.writeLogMessage(view, message)
58
59CustomerDocumentsContainer = attrs_to_fields(CustomerDocumentsContainer)
60
61class CustomerDocument(Document):
62    """This is a customer document.
63    """
64    grok.implements(ICustomerDocument, ICustomerNavigation)
65    grok.provides(ICustomerDocument)
66
67    def __init__(self):
68        super(CustomerDocument, self).__init__()
69        return
70
71    @property
72    def customer(self):
73        try:
74            return self.__parent__.__parent__
75        except AttributeError:
76            return None
77
78    def writeLogMessage(self, view, message):
79        return self.__parent__.__parent__.writeLogMessage(view, message)
80
81    @property
82    def is_editable(self):
83        try:
84            # Customer must be approved
85            cond1 = self.customer.state in getUtility(
86                ICustomersUtils).DOCMANAGE_STATES
87            # Document must be in state created
88            cond2 = self.state in getUtility(
89                IDocumentsUtils).DOCMANAGE_STATES
90            if not (cond1 and cond2):
91                return False
92        except AttributeError:
93            pass
94        return True
95
96    @property
97    def translated_classname(self):
98        try:
99            DOCTYPES_DICT = getUtility(ICustomersUtils).DOCTYPES_DICT
100            return DOCTYPES_DICT[self.classname]
101        except KeyError:
102            return
103
104CustomerDocument = attrs_to_fields(CustomerDocument)
105
106class CustomerPDFDocument(CustomerDocument):
107    """This is a customer document for a single pdf upload file only.
108    """
109    grok.implements(ICustomerPDFDocument, ICustomerNavigation)
110    grok.provides(ICustomerPDFDocument)
111
112CustomerPDFDocument = attrs_to_fields(CustomerPDFDocument)
113
114
115# Customer documents must be importable. So we might need a factory.
116class CustomerDocumentFactory(grok.GlobalUtility):
117    """A factory for customer documents.
118    """
119    grok.implements(IFactory)
120    grok.name(u'waeup.CustomerDocument')
121    title = u"Create a new document.",
122    description = u"This factory instantiates new sample document instances."
123
124    def __call__(self, *args, **kw):
125        return CustomerDocument(*args, **kw)
126
127    def getInterfaces(self):
128        return implementedBy(CustomerDocument)
129
130# Customer documents must be importable. So we might need a factory.
131class CustomerPDFDocumentFactory(grok.GlobalUtility):
132    """A factory for customer pdf documents.
133    """
134    grok.implements(IFactory)
135    grok.name(u'waeup.CustomerPDFDocument')
136    title = u"Create a new document.",
137    description = u"This factory instantiates new pdf document instances."
138
139    def __call__(self, *args, **kw):
140        return CustomerPDFDocument(*args, **kw)
141
142    def getInterfaces(self):
143        return implementedBy(CustomerPDFDocument)
144
145#: The file id marker for customer files
146CUSTOMERDOCUMENT_FILE_STORE_NAME = 'file-customerdocument'
147
148
149class CustomerDocumentFileNameChooser(grok.Adapter):
150    """A file id chooser for :class:`CustomerDocument` objects.
151
152    `context` is an :class:`CustomerDocument` instance.
153
154    The :class:`CustomerDocumentFileNameChooser` can build/check file ids for
155    :class:`Customer` objects suitable for use with
156    :class:`ExtFileStore` instances. The delivered file_id contains
157    the file id marker for :class:`CustomerDocument` object and the customer id
158    of the context customer.
159
160    This chooser is registered as an adapter providing
161    :class:`waeup.ikoba.interfaces.IFileStoreNameChooser`.
162
163    File store name choosers like this one are only convenience
164    components to ease the task of creating file ids for customer document
165    objects. You are nevertheless encouraged to use them instead of
166    manually setting up filenames for customer documents.
167
168    .. seealso:: :mod:`waeup.ikoba.imagestorage`
169
170    """
171
172    grok.context(ICustomerDocument)
173    grok.implements(IFileStoreNameChooser)
174
175    def checkName(self, name=None, attr=None):
176        """Check whether the given name is a valid file id for the context.
177
178        Returns ``True`` only if `name` equals the result of
179        :meth:`chooseName`.
180
181        """
182        return name == self.chooseName()
183
184    def chooseName(self, attr, name=None):
185        """Get a valid file id for customer document context.
186
187        *Example:*
188
189        For a customer with customer id ``'A123456'``
190        and document with id 'd123'
191        with attr ``'nice_image.jpeg'`` stored in
192        the customers container this chooser would create:
193
194          ``'__file-customerdocument__customers/A/A123456/nice_image_d123_A123456.jpeg'``
195
196        meaning that the nice image of this customer document would be
197        stored in the site-wide file storage in path:
198
199          ``customers/A/A123456/nice_image_d123_A123456.jpeg``
200
201        """
202        basename, ext = os.path.splitext(attr)
203        cust_id = self.context.customer.customer_id
204        doc_id = self.context.document_id
205        marked_filename = '__%s__%s/%s_%s_%s%s' % (
206            CUSTOMERDOCUMENT_FILE_STORE_NAME, path_from_custid(cust_id),
207            basename, doc_id, cust_id, ext)
208        return marked_filename
209
210
211class CustomerDocumentFileStoreHandler(DefaultFileStoreHandler, grok.GlobalUtility):
212    """Customer document specific file handling.
213
214    This handler knows in which path in a filestore to store customer document
215    files and how to turn this kind of data into some (browsable)
216    file object.
217
218    It is called from the global file storage, when it wants to
219    get/store a file with a file id starting with
220    ``__file-customerdocument__`` (the marker string for customer files).
221
222    Like each other file store handler it does not handle the files
223    really (this is done by the global file store) but only computes
224    paths and things like this.
225    """
226    grok.implements(IFileStoreHandler)
227    grok.name(CUSTOMERDOCUMENT_FILE_STORE_NAME)
228
229    def pathFromFileID(self, store, root, file_id):
230        """All customer document files are put in directory ``customers``.
231        """
232        marker, filename, basename, ext = store.extractMarker(file_id)
233        sub_root = os.path.join(root, 'customers')
234        return super(CustomerDocumentFileStoreHandler, self).pathFromFileID(
235            store, sub_root, basename)
236
237    def createFile(self, store, root, filename, file_id, file):
238        """Create a browsable file-like object.
239        """
240        # call super method to ensure that any old files with
241        # different filename extension are deleted.
242        file, path, file_obj = super(
243            CustomerDocumentFileStoreHandler, self).createFile(
244            store, root,  filename, file_id, file)
245        return file, path, IkobaImageFile(
246            file_obj.filename, file_obj.data)
247
Note: See TracBrowser for help on using the repository browser.