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

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

Use keywords not only to filter students but also to restrict access to coursetickets which belong to the course so that lecturers (who gain access to courses) can download only coursetickets of their own course.

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