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

Last change on this file since 9744 was 9744, checked in by Henrik Bettermann, 12 years ago

Add StudentStudyLevelsOverviewExporter?.

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