source: main/waeup.kofa/trunk/src/waeup/kofa/applicants/export.py @ 18021

Last change on this file since 18021 was 18021, checked in by Henrik Bettermann, 9 hours ago

Implement payment ticket exporter with date range filter.

  • Property svn:keywords set to Id
File size: 12.9 KB
Line 
1## $Id: export.py 18021 2025-02-13 11:36:26Z henrik $
2##
3## Copyright (C) 2012 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 applicant-related stuff.
19"""
20import grok
21from datetime import datetime, timedelta
22from zope.catalog.interfaces import ICatalog
23from zope.component import queryUtility, getUtility
24from waeup.kofa.applicants.interfaces import (
25    IApplicantBaseData, IApplicantsContainer, IApplicantOnlinePayment,
26    IApplicantRefereeReport, IApplicant)
27from waeup.kofa.interfaces import ICSVExporter, IKofaUtils
28from waeup.kofa.interfaces import MessageFactory as _
29from waeup.kofa.utils.batching import ExporterBase
30from waeup.kofa.utils.helpers import iface_names, to_timezone
31
32class ApplicantsContainerExporter(grok.GlobalUtility, ExporterBase):
33    """The Applicants Container Exporter exports container data. It does not
34    export applicants (application records) inside the container.
35    """
36    grok.implements(ICSVExporter)
37    grok.name('applicantscontainers')
38
39    fields = tuple(sorted(iface_names(IApplicantsContainer)))
40    title = _(u'Applicants Containers')
41
42    def mangle_value(self, value, name, context=None):
43        return super(
44            ApplicantsContainerExporter, self).mangle_value(
45            value, name, context=context)
46
47    def export(self, containers, filepath=None):
48        """Export `containers`, an iterable, as CSV file.
49
50        If `filepath` is ``None``, a raw string with CSV data is returned.
51        """
52        writer, outfile = self.get_csv_writer(filepath)
53        for container in containers:
54            self.write_item(container, writer)
55        return self.close_outfile(filepath, outfile)
56
57    def export_all(self, site, filepath=None):
58        """Export applicantscontainer into filepath as CSV data.
59
60        If `filepath` is ``None``, a raw string with CSV data is returned.
61        """
62        writer, outfile = self.get_csv_writer(filepath)
63        containers = site.get('applicants', {})
64        return self.export(containers.values(), filepath)
65
66
67class ApplicantExporter(grok.GlobalUtility, ExporterBase):
68    """The Applicant Exporter exports application records (= applicants)
69    stored in the database. In contrast to the exporters in the academic
70    section this exporter does not iterate over the items of containers
71    but searches the :class:`ApplicantsCatalog` instead.
72
73    The exporter exports all applicants if started in the Data Center
74    which means in the context of the `DataCenter` object. The exporter can also
75    be started 'locally' which means in the context of an `ApplicantsContainer`
76    container. Then the :meth:`export_filtered()` instead of the
77    :meth:`export_all()` method is applied which searches for applicants
78    in the respective container.
79    """
80    grok.implements(ICSVExporter)
81    grok.name('applicants')
82
83    fields = tuple(sorted(iface_names(IApplicant))) + (
84        'password', 'state', 'history', 'container_code', 'application_number',
85        'display_fullname', 'application_date')
86    title = _(u'Applicants')
87
88    def mangle_value(self, value, name, context=None):
89        """The mangler determines the codes of the atributes `course1`,
90        `course2` and `course_admitted`. It furthermore prepares the
91        history messages and adds a hash symbol at the end of the phone number
92        to avoid annoying automatic number transformation by Excel or Calc.
93        """
94        if name.startswith('course') and value is not None:
95            value = value.code
96        elif name == 'school_grades' and value is not None:
97            value = [eval(entry.to_string()) for entry in value]
98        elif name == 'referees' and value is not None:
99            value = [eval(entry.to_string()) for entry in value]
100        elif name == 'history':
101            value = getattr(value, 'messages', None)
102        elif name == 'phone' and value is not None:
103            # Append hash '#' to phone numbers to circumvent
104            # unwanted excel automatic
105            value = str('%s#' % value)
106        elif name == 'container_code':
107            value = value.strip('+')
108        return super(
109            ApplicantExporter, self).mangle_value(
110            value, name, context=context)
111
112    def export(self, applicants, filepath=None):
113        """Export `applicants`, an iterable, as CSV file.
114        If `filepath` is ``None``, a raw string with CSV data is returned.
115        """
116        writer, outfile = self.get_csv_writer(filepath)
117        for applicant in applicants:
118            self.write_item(applicant, writer)
119        return self.close_outfile(filepath, outfile)
120
121    def export_all(self, site, filepath=None):
122        """Export all applicants into filepath as CSV data.
123        If `filepath` is ``None``, a raw string with CSV data is returned.
124        Only used records are being exported.
125        """
126        catalog = queryUtility(
127            ICatalog, context=site, name='applicants_catalog', default=None)
128        if catalog is None:
129            return self.export([], filepath)
130        applicants = catalog.searchResults(
131            # reg_num might not be set and then would not be found.
132            # We therefore search for applicant_id.
133            applicant_id=(None, None))
134        used = [value for value in applicants
135                if value.container_code.endswith('+')]
136        return self.export(used, filepath=filepath)
137
138    def export_filtered(self, site, filepath=None, **kw):
139        """Export filtered applicants in container denoted by keywords (`kw`).
140        If `filepath` is ``None``, a raw string with CSV data should
141        be returned. Only used records are being exported.
142        """
143        container = grok.getSite()['applicants'][kw['container']]
144        container_values = container.values()
145        used = [value for value in container_values
146                if value.container_code.endswith('+')]
147        return self.export(used, filepath=filepath)
148
149
150class ApplicantPaymentExporter(grok.GlobalUtility, ExporterBase):
151    """The Applicant Payment Exporter exports all payments made by applicants.
152    In other words, it exports payment tickets in state 'paid'. The exporter
153    searches :class:`ApplicantsCatalog` and iterates over all payment tickets
154    which are stored in an applicant (container).
155
156    The exporter exports all applicant payments if started in the Data Center
157    which means in the context of the `DataCenter` object. The exporter can also
158    be started 'locally' which means in the context of an
159    `ApplicantsContainer` container, see `ApplicantExporter` above.
160    """
161    grok.implements(ICSVExporter)
162    grok.name('applicantpayments')
163
164    fields = tuple(sorted(iface_names(
165        IApplicantOnlinePayment,
166        exclude_attribs=False,
167        omit=['display_item', 'p_option']))) + (
168              'applicant_id',
169              'reg_number',
170              'display_fullname',)
171    title = _(u'Applicant Payments')
172
173    def mangle_value(self, value, name, context=None):
174        """The mangler determines the applicant's id.
175        """
176        if name in ('applicant_id', 'reg_number',
177            'display_fullname',) and context is not None:
178            applicant = context.__parent__
179            value = getattr(applicant, name, None)
180        return super(
181            ApplicantPaymentExporter, self).mangle_value(
182            value, name, context=context)
183
184    def export(self, payments, filepath=None):
185        """
186        """
187        writer, outfile = self.get_csv_writer(filepath)
188        for payment in payments:
189            self.write_item(payment, writer)
190        return self.close_outfile(filepath, outfile)
191
192    def export_all(self, site, filepath=None):
193        """
194        """
195        catalog = queryUtility(
196            ICatalog, context=site, name='applicants_catalog', default=None)
197        if catalog is None:
198            return self.export([], filepath)
199        applicants = catalog.searchResults(
200            # reg_num might not be set and then would not be found.
201            # We therefore search for applicant_id.
202            applicant_id=(None, None))
203        used = [value for value in applicants
204                if value.container_code.endswith('+')]
205        payments = []
206        for applicant in used:
207            for payment in applicant.payments:
208                if payment.p_state == 'paid':
209                    payments.append(payment)
210        return self.export(payments, filepath=filepath)
211
212    def export_filtered(self, site, filepath=None, **kw):
213        """
214        """
215        container = grok.getSite()['applicants'][kw['container']]
216        p_start = kw.get('p_start')
217        p_end = kw.get('p_end')
218        container_values = container.values()
219        used = [value for value in container_values
220                if value.container_code.endswith('+')]
221        payments = []
222        date_format = '%d/%m/%Y'
223        for applicant in used:
224            for payment in applicant.payments:
225                if p_start and p_end:
226                    if not payment.payment_date:
227                        continue
228                    payments_start = datetime.strptime(p_start, date_format)
229                    payments_end = datetime.strptime(p_end, date_format)
230                    tz = getUtility(IKofaUtils).tzinfo
231                    payments_start = tz.localize(payments_start)
232                    payments_end = tz.localize(payments_end) + timedelta(days=1)
233                    payment_date = to_timezone(payment.payment_date, tz)
234                    if payment_date < payments_start or payment_date > payments_end:
235                        continue
236                if payment.p_state == 'paid':
237                    payments.append(payment)
238        return self.export(payments, filepath=filepath)
239
240class ApplicantRefereeReportExporter(grok.GlobalUtility, ExporterBase):
241    """The Applicant Referee Report Exporter exports all referee reports.
242    The exportersearches :class:`ApplicantsCatalog` and iterates over all
243    referee reports which are stored in an applicant (container).
244
245    The exporter exports all referee reports if started in the Data Center
246    which means in the context of the `DataCenter` object. The exporter can also
247    be started 'locally' which means in the context of an
248    `ApplicantsContainer` container, see `ApplicantExporter` above.
249    """
250    grok.implements(ICSVExporter)
251    grok.name('applicantrefereereports')
252
253    fields = tuple(sorted(iface_names(
254        IApplicantRefereeReport,
255        exclude_attribs=False))) + (
256              'applicant_id',
257              'reg_number',
258              'display_fullname',)
259    title = _(u'Applicant Referee Reports')
260
261    def mangle_value(self, value, name, context=None):
262        """The mangler determines the applicant's id.
263        """
264        if name in ('applicant_id', 'reg_number',
265            'display_fullname',) and context is not None:
266            applicant = context.__parent__
267            value = getattr(applicant, name, None)
268        return super(
269            ApplicantRefereeReportExporter, self).mangle_value(
270            value, name, context=context)
271
272    def export(self, refereereports, filepath=None):
273        """
274        """
275        writer, outfile = self.get_csv_writer(filepath)
276        for refereereport in refereereports:
277            self.write_item(refereereport, writer)
278        return self.close_outfile(filepath, outfile)
279
280    def export_all(self, site, filepath=None):
281        """
282        """
283        catalog = queryUtility(
284            ICatalog, context=site, name='applicants_catalog', default=None)
285        if catalog is None:
286            return self.export([], filepath)
287        applicants = catalog.searchResults(
288            # reg_num might not be set and then would not be found.
289            # We therefore search for applicant_id.
290            applicant_id=(None, None))
291        refereereports = []
292        for applicant in applicants:
293            for refereereport in applicant.refereereports:
294                refereereports.append(refereereport)
295        return self.export(refereereports, filepath=filepath)
296
297    def export_filtered(self, site, filepath=None, **kw):
298        """
299        """
300        container = grok.getSite()['applicants'][kw['container']]
301        container_values = container.values()
302        refereereports = []
303        for applicant in container_values:
304            for refereereport in applicant.refereereports:
305                refereereports.append(refereereport)
306        return self.export(refereereports, filepath=filepath)
Note: See TracBrowser for help on using the repository browser.