source: main/waeup.kofa/trunk/src/waeup/kofa/students/export.py @ 9766

Last change on this file since 9766 was 9763, checked in by uli, 12 years ago

Make the student specific parts of StudentsExportFilter? class attributes and provide an implementation for filtered students.

  • Property svn:keywords set to Id
File size: 14.9 KB
Line 
1## $Id: export.py 9763 2012-12-03 16:30:10Z uli $
2##
3## Copyright (C) 2011 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 student related stuff.
19"""
20import grok
21from datetime import datetime
22from zope.catalog.interfaces import ICatalog
23from zope.component import queryUtility
24from waeup.kofa.interfaces import MessageFactory as _
25from waeup.kofa.students.interfaces import (
26    IStudent, IStudentStudyCourse, IStudentStudyLevel, ICourseTicket,
27    IStudentOnlinePayment, ICSVStudentExporter, IBedTicket)
28from waeup.kofa.utils.batching import ExporterBase
29from waeup.kofa.utils.helpers import iface_names
30
31#: A tuple containing all exporter names referring to students or
32#: subobjects thereof.
33EXPORTER_NAMES = ('students', 'studentstudycourses', 'studentstudylevels',
34                  'coursetickets', 'studentpayments')
35
36class StudentsExportFilter(object):
37    """A filter to find students that match certain criteria.
38
39    The filter is meant as a component to aggregate search parameters
40    for students. This way we can tell some exporter to use 'this
41    filter' instead of passing in all (probably different) parameters.
42
43    Just an idea right now. There might be better ways.
44    """
45    cat_name = 'students_catalog'
46    default_params = dict(student_id=None)
47
48    def __init__(self, **kw):
49        self._kw = dict(self.default_params)
50        self._kw.update(kw)
51        return
52
53    def query_catalog(self, catalog):
54        query = dict([(idx_name, (value, value))
55                     for idx_name, value in self._kw.items()])
56        result = catalog.searchResults(**query)
57        return result
58
59    def query(self, context=None):
60        catalog = queryUtility(
61            ICatalog, context=context, name=self.cat_name, default=None)
62        if catalog is None:
63            return []
64        return self.query_catalog(catalog)
65
66def get_students(site, stud_filter=StudentsExportFilter()):
67    """Get all students registered in catalog in `site`.
68    """
69    return stud_filter.query(context=site)
70
71def get_studycourses(students):
72    """Get studycourses of `students`.
73    """
74    return [x.get('studycourse', None) for x in students
75            if x is not None]
76
77def get_levels(students):
78    """Get all studylevels of `students`.
79    """
80    levels = []
81    for course in get_studycourses(students):
82        for level in course.values():
83            levels.append(level)
84    return levels
85
86def get_tickets(students):
87    """Get all course tickets of `students`.
88    """
89    tickets = []
90    for level in get_levels(students):
91        for ticket in level.values():
92            tickets.append(ticket)
93    return tickets
94
95def get_payments(students):
96    """Get all payments of `students`.
97    """
98    payments = []
99    for student in students:
100        for payment in student.get('payments', {}).values():
101            payments.append(payment)
102    return payments
103
104def get_bedtickets(students):
105    """Get all bedtickets of `students`.
106    """
107    tickets = []
108    for student in students:
109        for ticket in student.get('accommodation', {}).values():
110            tickets.append(ticket)
111    return tickets
112
113class StudentExporterBase(ExporterBase):
114    """Exporter for students or related objects.
115
116    This is a baseclass.
117    """
118    grok.baseclass()
119    grok.implements(ICSVStudentExporter)
120    grok.provides(ICSVStudentExporter)
121
122    def export(self, values, filepath=None):
123        """Export `values`, an iterable, as CSV file.
124
125        If `filepath` is ``None``, a raw string with CSV data is returned.
126        """
127        writer, outfile = self.get_csv_writer(filepath)
128        for value in values:
129            self.write_item(value, writer)
130        return self.close_outfile(filepath, outfile)
131
132    def export_filtered(self, site, session, level, faccode=None,
133                        depcode=None, filepath=None):
134        """Export all students denoted by the given parameters.
135        """
136        filter_inst = StudentsExportFilter(
137            current_session=session, current_level=level,
138            faccode=faccode, depcode=depcode)
139        result_set = get_students(site, stud_filter=filter_inst)
140        return self.export(result_set, filepath=filepath)
141
142
143class StudentsExporter(grok.GlobalUtility, StudentExporterBase):
144    """Exporter for Students.
145    """
146    grok.name('students')
147
148    #: Fieldnames considered by this exporter
149    fields = tuple(sorted(iface_names(
150        IStudent, omit=['loggerInfo']))) + (
151        'password', 'state', 'history', 'certcode', 'is_postgrad',
152        'current_level', 'current_session')
153
154    #: The title under which this exporter will be displayed
155    title = _(u'Students')
156
157    def mangle_value(self, value, name, context=None):
158        if name == 'history':
159            value = value.messages
160        if name == 'phone' and value is not None:
161            # Append hash '#' to phone numbers to circumvent
162            # unwanted excel automatic
163            value = str('%s#' % value)
164        return super(
165            StudentsExporter, self).mangle_value(
166            value, name, context=context)
167
168    def export_all(self, site, filepath=None):
169        """Export students into filepath as CSV data.
170
171        If `filepath` is ``None``, a raw string with CSV data is returned.
172        """
173        return self.export(get_students(site), filepath)
174
175    def export_student(self, student, filepath=None):
176        return self.export([student], filepath=filepath)
177
178
179class StudentStudyCourseExporter(grok.GlobalUtility, StudentExporterBase):
180    """Exporter for StudentStudyCourses.
181    """
182    grok.name('studentstudycourses')
183
184    #: Fieldnames considered by this exporter
185    fields = tuple(sorted(iface_names(IStudentStudyCourse))) + ('student_id',)
186
187    #: The title under which this exporter will be displayed
188    title = _(u'Student Study Courses')
189
190    def mangle_value(self, value, name, context=None):
191        """Treat location values special.
192        """
193        if name == 'certificate' and value is not None:
194            # XXX: hopefully cert codes are unique site-wide
195            value = value.code
196        if name == 'student_id' and context is not None:
197            student = context.student
198            value = getattr(student, name, None)
199        return super(
200            StudentStudyCourseExporter, self).mangle_value(
201            value, name, context=context)
202
203    def export_all(self, site, filepath=None):
204        """Export study courses into filepath as CSV data.
205
206        If `filepath` is ``None``, a raw string with CSV data is returned.
207        """
208        return self.export(get_studycourses(get_students(site)), filepath)
209
210    def export_student(self, student, filepath=None):
211        """Export studycourse of a single student object.
212        """
213        return self.export(get_studycourses([student]), filepath)
214
215
216class StudentStudyLevelExporter(grok.GlobalUtility, StudentExporterBase):
217    """Exporter for StudentStudyLevels.
218    """
219    grok.name('studentstudylevels')
220
221    #: Fieldnames considered by this exporter
222    fields = tuple(sorted(iface_names(
223        IStudentStudyLevel) + ['level'])) + (
224        'student_id', 'number_of_tickets','certcode')
225
226    #: The title under which this exporter will be displayed
227    title = _(u'Student Study Levels')
228
229    def mangle_value(self, value, name, context=None):
230        """Treat location values special.
231        """
232        if name == 'student_id' and context is not None:
233            student = context.student
234            value = getattr(student, name, None)
235        return super(
236            StudentStudyLevelExporter, self).mangle_value(
237            value, name, context=context)
238
239    def export_all(self, site, filepath=None):
240        """Export study levels into filepath as CSV data.
241
242        If `filepath` is ``None``, a raw string with CSV data is returned.
243        """
244        return self.export(get_levels(get_students(site)), filepath)
245
246    def export_student(self, student, filepath=None):
247        return self.export(get_levels([student]), filepath)
248
249class CourseTicketExporter(grok.GlobalUtility, StudentExporterBase):
250    """Exporter for CourseTickets.
251    """
252    grok.name('coursetickets')
253
254    #: Fieldnames considered by this exporter
255    fields = tuple(sorted(iface_names(ICourseTicket) +
256        ['level', 'code'])) + ('student_id', 'certcode')
257
258    #: The title under which this exporter will be displayed
259    title = _(u'Course Tickets')
260
261    def mangle_value(self, value, name, context=None):
262        """Treat location values special.
263        """
264        if context is not None:
265            student = context.student
266            if name == 'student_id' and student is not None:
267                value = getattr(student, name, None)
268            if name == 'level':
269                value = getattr(context, 'getLevel', lambda: None)()
270        return super(
271            CourseTicketExporter, self).mangle_value(
272            value, name, context=context)
273
274    def export_all(self, site, filepath=None):
275        """Export course tickets into filepath as CSV data.
276
277        If `filepath` is ``None``, a raw string with CSV data is returned.
278        """
279        return self.export(get_tickets(get_students(site)), filepath)
280
281    def export_student(self, student, filepath=None):
282        return self.export(get_tickets([student]), filepath)
283
284
285class PaymentsExporter(grok.GlobalUtility, StudentExporterBase):
286    """Exporter for OnlinePayment instances.
287    """
288    grok.name('studentpayments')
289
290    #: Fieldnames considered by this exporter
291    fields = tuple(
292        sorted(iface_names(
293            IStudentOnlinePayment, exclude_attribs=False))) + (
294                'student_id','student_state','current_session')
295
296    #: The title under which this exporter will be displayed
297    title = _(u'Student Payments')
298
299    def mangle_value(self, value, name, context=None):
300        """Treat location values special.
301        """
302        if context is not None:
303            student = context.student
304            if name in ['student_id'] and student is not None:
305                value = getattr(student, name, None)
306        return super(
307            PaymentsExporter, self).mangle_value(
308            value, name, context=context)
309
310    def export_all(self, site, filepath=None):
311        """Export payments into filepath as CSV data.
312
313        If `filepath` is ``None``, a raw string with CSV data is returned.
314        """
315        return self.export(get_payments(get_students(site)), filepath)
316
317    def export_student(self, student, filepath=None):
318        return self.export(get_payments([student]), filepath)
319
320class BedTicketsExporter(grok.GlobalUtility, StudentExporterBase):
321    """Exporter for BedTicket instances.
322    """
323    grok.name('bedtickets')
324
325    #: Fieldnames considered by this exporter
326    fields = tuple(
327        sorted(iface_names(
328            IBedTicket, exclude_attribs=False))) + (
329                'student_id', 'actual_bed_type')
330
331    #: The title under which this exporter will be displayed
332    title = _(u'Bed Tickets')
333
334    def mangle_value(self, value, name, context=None):
335        """Treat location values and others special.
336        """
337        if context is not None:
338            student = context.student
339            if name in ['student_id'] and student is not None:
340                value = getattr(student, name, None)
341        if name == 'bed' and value is not None:
342            value = getattr(value, 'bed_id', None)
343        if name == 'actual_bed_type':
344            value = getattr(getattr(context, 'bed', None), 'bed_type')
345        return super(
346            BedTicketsExporter, self).mangle_value(
347            value, name, context=context)
348
349    def export_all(self, site, filepath=None):
350        """Export payments into filepath as CSV data.
351
352        If `filepath` is ``None``, a raw string with CSV data is returned.
353        """
354        return self.export(get_bedtickets(get_students(site)), filepath)
355
356    def export_student(self, student, filepath=None):
357        return self.export(get_bedtickets([student]), filepath)
358
359class StudentPaymentsOverviewExporter(StudentsExporter):
360    """Exporter for students with payment overview.
361    """
362    grok.name('paymentsoverview')
363
364    curr_year = datetime.now().year
365    year_range = range(curr_year - 9, curr_year + 1)
366    year_range_tuple = tuple([str(year) for year in year_range])
367
368    #: Fieldnames considered by this exporter
369    fields = ('student_id', ) + (
370        'state', 'certcode', 'faccode', 'depcode', 'is_postgrad',
371        'current_level', 'current_session',
372        ) + year_range_tuple
373
374    #: The title under which this exporter will be displayed
375    title = _(u'Student Payments Overview')
376
377    def mangle_value(self, value, name, context=None):
378        if name in self.year_range_tuple and context is not None:
379            value = ''
380            for ticket in context['payments'].values():
381                if ticket.p_state == 'paid' and \
382                    ticket.p_category == 'schoolfee' and \
383                    ticket.p_session == int(name):
384                    value = ticket.amount_auth
385                    break
386        return super(
387            StudentsExporter, self).mangle_value(
388            value, name, context=context)
389
390class StudentStudyLevelsOverviewExporter(StudentsExporter):
391    """Exporter for students with study level overview.
392    """
393    grok.name('studylevelsoverview')
394
395    levels = ['10',]
396    levels += [str(level) for level in range(100,1000,10) if level % 100 < 30]
397    levels.append('999')
398    level_range_tuple = tuple(levels)
399
400    #: Fieldnames considered by this exporter
401    fields = ('student_id', ) + (
402        'state', 'certcode', 'faccode', 'depcode', 'is_postgrad',
403        'entry_session', 'current_level', 'current_session',
404        ) + level_range_tuple
405
406    #: The title under which this exporter will be displayed
407    title = _(u'Student Study Levels Overview')
408
409    def mangle_value(self, value, name, context=None):
410        if name in self.level_range_tuple and context is not None:
411            value = ''
412            for level in context['studycourse'].values():
413                if level.level == int(name):
414                    #value = '%s|%s|%s|%s' % (
415                    #    level.level_session,
416                    #    len(level),
417                    #    level.validated_by,
418                    #    level.level_verdict)
419                    value = '%s' % level.level_session
420                    break
421        return super(
422            StudentsExporter, self).mangle_value(
423            value, name, context=context)
Note: See TracBrowser for help on using the repository browser.