source: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/export.py @ 12279

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

Export only customer documents and contracts which match the exporter's class_name attribute.

Reorganize exporters to ease customization. Let's always use baseclasses.

  • Property svn:keywords set to Id
File size: 8.1 KB
Line 
1## $Id: export.py 12279 2014-12-21 10:13:45Z 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"""Exporters for customer related stuff.
19"""
20import os
21import grok
22from datetime import datetime
23from zope.component import getUtility
24from waeup.ikoba.interfaces import (
25    IExtFileStore, IFileStoreNameChooser)
26from waeup.ikoba.interfaces import MessageFactory as _
27from waeup.ikoba.customers.catalog import CustomersQuery
28from waeup.ikoba.customers.interfaces import (
29    ICustomer, ICSVCustomerExporter,
30    ICustomerSampleDocument, ISampleContract)
31from waeup.ikoba.utils.batching import ExporterBase
32from waeup.ikoba.utils.helpers import iface_names, to_timezone
33
34#: A tuple containing all exporter names referring to customers or
35#: subobjects thereof.
36EXPORTER_NAMES = ('customers', 'customersampledocuments', 'samplecontracts')
37
38
39def get_customers(site, cust_filter=CustomersQuery()):
40    """Get all customers registered in catalog in `site`.
41    """
42    return cust_filter.query()
43
44def get_documents(customers, class_name):
45    """Get all documents of `customers`.
46    """
47    documents = []
48    for customer in customers:
49        for document in customer.get('documents', {}).values():
50            if document.class_name == class_name:
51                documents.append(document)
52    return documents
53
54def get_contracts(customers, class_name):
55    """Get all contracts of `customers`.
56    """
57    contracts = []
58    for customer in customers:
59        for contract in customer.get('contracts', {}).values():
60            if contract.class_name == class_name:
61                contracts.append(contract)
62    return contracts
63
64class CustomerExporterBase(ExporterBase):
65    """Exporter for customers or related objects.
66
67    This is a baseclass.
68    """
69    grok.baseclass()
70    grok.implements(ICSVCustomerExporter)
71    grok.provides(ICSVCustomerExporter)
72
73    def filter_func(self, x, **kw):
74        return x
75
76    def get_filtered(self, site, **kw):
77        """Get customers from a catalog filtered by keywords.
78
79        customers_catalog is the default catalog. The keys must be valid
80        catalog index names.
81        Returns a simple empty list, a list with `Customer`
82        objects or a catalog result set with `Customer`
83        objects.
84
85        .. seealso:: `waeup.ikoba.customers.catalog.CustomersCatalog`
86
87        """
88        # Pass only given keywords to create FilteredCatalogQuery objects.
89        # This way we avoid
90        # trouble with `None` value ambivalences and queries are also
91        # faster (normally less indexes to ask). Drawback is, that
92        # developers must look into catalog to see what keywords are
93        # valid.
94        query = CustomersQuery(**kw)
95        return query.query()
96
97    def export(self, values, filepath=None):
98        """Export `values`, an iterable, as CSV file.
99
100        If `filepath` is ``None``, a raw string with CSV data is returned.
101        """
102        writer, outfile = self.get_csv_writer(filepath)
103        for value in values:
104            self.write_item(value, writer)
105        return self.close_outfile(filepath, outfile)
106
107    def export_all(self, site, filepath=None):
108        """Export customers into filepath as CSV data.
109
110        If `filepath` is ``None``, a raw string with CSV data is returned.
111        """
112        return self.export(self.filter_func(get_customers(site)), filepath)
113
114    def export_customer(self, customer, filepath=None):
115        return self.export(self.filter_func([customer]), filepath=filepath)
116
117    def export_filtered(self, site, filepath=None, **kw):
118        """Export items denoted by `kw`.
119
120        If `filepath` is ``None``, a raw string with CSV data should
121        be returned.
122        """
123        data = self.get_filtered(site, **kw)
124        return self.export(self.filter_func(data, **kw), filepath=filepath)
125
126
127class CustomerExporter(grok.GlobalUtility, CustomerExporterBase):
128    """Exporter for Customers.
129    """
130    grok.name('customers')
131
132    iface = ICustomer
133
134    #: The title under which this exporter will be displayed
135    title = _(u'Customers')
136
137    #: Fieldnames considered by this exporter
138    @property
139    def fields(self):
140        return tuple(sorted(iface_names(self.iface))) + (
141            'password', 'state', 'history',)
142
143    def mangle_value(self, value, name, context=None):
144        if name == 'history':
145            value = value.messages
146        if name == 'phone' and value is not None:
147            # Append hash '#' to phone numbers to circumvent
148            # unwanted excel automatic
149            value = str('%s#' % value)
150        return super(
151            CustomerExporter, self).mangle_value(
152            value, name, context=context)
153
154
155class CustomerDocumentExporterBase(grok.GlobalUtility, CustomerExporterBase):
156    """Exporter for Customer Document instances.
157
158    This is a baseclass.
159    """
160    grok.baseclass()
161    iface = None
162    class_name = None
163    title = None
164
165    #: Fieldnames considered by this exporter
166    @property
167    def fields(self):
168        return tuple(
169            sorted(iface_names(
170                self.iface, exclude_attribs=False,
171                omit=['is_editable_by_customer',
172                      'is_editable_by_manager',
173                      'is_verifiable',
174                      'translated_state',
175                      'formatted_transition_date',
176                      'translated_class_name',
177                      'connected_files',   # Could be used to export file URLs
178                      ])))
179
180    def filter_func(self, x, **kw):
181        return get_documents(x, self.class_name)
182
183    def mangle_value(self, value, name, context=None):
184
185        if name == 'history':
186            value = value.messages
187        return super(
188            CustomerDocumentExporterBase, self).mangle_value(
189            value, name, context=context)
190
191
192class CustomerSampleDocumentExporter(CustomerDocumentExporterBase):
193    """Exporter for CustomerSampleDocument instances.
194    """
195    grok.name('customersampledocuments')
196
197    iface = ICustomerSampleDocument
198    class_name = 'CustomerSampleDocument'
199    title = _(u'Customer Sample Documents')
200
201
202class SampleContractExporterBase(grok.GlobalUtility, CustomerExporterBase):
203    """Exporter for Contract instances.
204
205    This is a baseclass.
206    """
207    grok.baseclass()
208    iface = None
209    class_name = None
210    title = None
211
212    #: Fieldnames considered by this exporter
213    @property
214    def fields(self):
215        return tuple(
216            sorted(iface_names(
217                self.iface, exclude_attribs=False,
218                omit=['translated_state',
219                      'formatted_transition_date',
220                      'translated_class_name',
221                      'is_editable_by_customer',
222                      'is_approvable'])))
223
224    def filter_func(self, x, **kw):
225        return get_contracts(x, self.class_name)
226
227    def mangle_value(self, value, name, context=None):
228
229        if name == 'history':
230            value = value.messages
231        if name.endswith('_object'):
232            mangled_value = getattr(value, 'document_id', None)
233            if mangled_value:
234                return mangled_value
235            mangled_value = getattr(value, 'product_id', None)
236            if mangled_value:
237                return mangled_value
238        return super(
239            SampleContractExporterBase, self).mangle_value(
240            value, name, context=context)
241
242
243class SampleContractExporter(SampleContractExporterBase):
244    """Exporter for sample contracts.
245    """
246    grok.name('samplecontracts')
247    iface = ISampleContract
248    class_name = 'SampleContract'
249    title = _(u'Sample Contracts')
Note: See TracBrowser for help on using the repository browser.