## $Id: export.py 9744 2012-11-30 12:24:13Z henrik $ ## ## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## """Exporters for student related stuff. """ import grok from datetime import datetime from zope.catalog.interfaces import ICatalog from zope.component import queryUtility from waeup.kofa.interfaces import MessageFactory as _ from waeup.kofa.students.interfaces import ( IStudent, IStudentStudyCourse, IStudentStudyLevel, ICourseTicket, IStudentOnlinePayment, ICSVStudentExporter, IBedTicket) from waeup.kofa.utils.batching import ExporterBase from waeup.kofa.utils.helpers import iface_names #: A tuple containing all exporter names referring to students or #: subobjects thereof. EXPORTER_NAMES = ('students', 'studentstudycourses', 'studentstudylevels', 'coursetickets', 'studentpayments') class StudentsExportFilter(object): """A filter to find students that match certain criteria. The filter is meant as a component to aggregate search parameters for students. This way we can tell some exporter to use 'this filter' instead of passing in all (probably different) parameters. Just an idea right now. There might be better ways. """ def __init__(self, session=None, level=None, faculty_code=None, department_code=None): self.session = session self.level = level self.faculty_code = faculty_code self.department_code = department_code return def get_students(self, catalog): students = catalog.searchResults( student_id=(None, None), current_session=(self.session, self.session), #level=(self.level, self.level), # would be nice faccode=(self.faculty_code, self.faculty_code), depcode=(self.department_code, self.department_code) ) # filter out by level if self.level is not None: return [x for x in students if x['studycourse'].current_level == self.level] return students def get_students(site, stud_filter=None): """Get all students registered in catalog in `site`. """ catalog = queryUtility( ICatalog, context=site, name='students_catalog', default=None) if catalog is None: return [] if stud_filter is None: students = catalog.searchResults(student_id=(None, None)) else: students = stud_filter.get_students(catalog) return students def get_studycourses(students): """Get studycourses of `students`. """ return [x.get('studycourse', None) for x in students if x is not None] def get_levels(students): """Get all studylevels of `students`. """ levels = [] for course in get_studycourses(students): for level in course.values(): levels.append(level) return levels def get_tickets(students): """Get all course tickets of `students`. """ tickets = [] for level in get_levels(students): for ticket in level.values(): tickets.append(ticket) return tickets def get_payments(students): """Get all payments of `students`. """ payments = [] for student in students: for payment in student.get('payments', {}).values(): payments.append(payment) return payments def get_bedtickets(students): """Get all bedtickets of `students`. """ tickets = [] for student in students: for ticket in student.get('accommodation', {}).values(): tickets.append(ticket) return tickets class StudentExporterBase(ExporterBase): """Exporter for students or related objects. This is a baseclass. """ grok.baseclass() grok.implements(ICSVStudentExporter) grok.provides(ICSVStudentExporter) def export(self, values, filepath=None): """Export `values`, an iterable, as CSV file. If `filepath` is ``None``, a raw string with CSV data is returned. """ writer, outfile = self.get_csv_writer(filepath) for value in values: self.write_item(value, writer) return self.close_outfile(filepath, outfile) def export_filtered(self, site, session, level, faculty_code=None, department_code=None, filepath=None): pass class StudentsExporter(grok.GlobalUtility, StudentExporterBase): """Exporter for Students. """ grok.name('students') #: Fieldnames considered by this exporter fields = tuple(sorted(iface_names( IStudent, omit=['loggerInfo']))) + ( 'password', 'state', 'history', 'certcode', 'is_postgrad', 'current_level', 'current_session') #: The title under which this exporter will be displayed title = _(u'Students') def mangle_value(self, value, name, context=None): if name == 'history': value = value.messages if name == 'phone' and value is not None: # Append hash '#' to phone numbers to circumvent # unwanted excel automatic value = str('%s#' % value) return super( StudentsExporter, self).mangle_value( value, name, context=context) def export_all(self, site, filepath=None): """Export students into filepath as CSV data. If `filepath` is ``None``, a raw string with CSV data is returned. """ return self.export(get_students(site), filepath) def export_student(self, student, filepath=None): return self.export([student], filepath=filepath) class StudentStudyCourseExporter(grok.GlobalUtility, StudentExporterBase): """Exporter for StudentStudyCourses. """ grok.name('studentstudycourses') #: Fieldnames considered by this exporter fields = tuple(sorted(iface_names(IStudentStudyCourse))) + ('student_id',) #: The title under which this exporter will be displayed title = _(u'Student Study Courses') def mangle_value(self, value, name, context=None): """Treat location values special. """ if name == 'certificate' and value is not None: # XXX: hopefully cert codes are unique site-wide value = value.code if name == 'student_id' and context is not None: student = context.student value = getattr(student, name, None) return super( StudentStudyCourseExporter, self).mangle_value( value, name, context=context) def export_all(self, site, filepath=None): """Export study courses into filepath as CSV data. If `filepath` is ``None``, a raw string with CSV data is returned. """ return self.export(get_studycourses(get_students(site)), filepath) def export_student(self, student, filepath=None): """Export studycourse of a single student object. """ return self.export(get_studycourses([student]), filepath) class StudentStudyLevelExporter(grok.GlobalUtility, StudentExporterBase): """Exporter for StudentStudyLevels. """ grok.name('studentstudylevels') #: Fieldnames considered by this exporter fields = tuple(sorted(iface_names( IStudentStudyLevel) + ['level'])) + ( 'student_id', 'number_of_tickets','certcode') #: The title under which this exporter will be displayed title = _(u'Student Study Levels') def mangle_value(self, value, name, context=None): """Treat location values special. """ if name == 'student_id' and context is not None: student = context.student value = getattr(student, name, None) return super( StudentStudyLevelExporter, self).mangle_value( value, name, context=context) def export_all(self, site, filepath=None): """Export study levels into filepath as CSV data. If `filepath` is ``None``, a raw string with CSV data is returned. """ return self.export(get_levels(get_students(site)), filepath) def export_student(self, student, filepath=None): return self.export(get_levels([student]), filepath) class CourseTicketExporter(grok.GlobalUtility, StudentExporterBase): """Exporter for CourseTickets. """ grok.name('coursetickets') #: Fieldnames considered by this exporter fields = tuple(sorted(iface_names(ICourseTicket) + ['level', 'code'])) + ('student_id', 'certcode') #: The title under which this exporter will be displayed title = _(u'Course Tickets') def mangle_value(self, value, name, context=None): """Treat location values special. """ if context is not None: student = context.student if name == 'student_id' and student is not None: value = getattr(student, name, None) if name == 'level': value = getattr(context, 'getLevel', lambda: None)() return super( CourseTicketExporter, self).mangle_value( value, name, context=context) def export_all(self, site, filepath=None): """Export course tickets into filepath as CSV data. If `filepath` is ``None``, a raw string with CSV data is returned. """ return self.export(get_tickets(get_students(site)), filepath) def export_student(self, student, filepath=None): return self.export(get_tickets([student]), filepath) class PaymentsExporter(grok.GlobalUtility, StudentExporterBase): """Exporter for OnlinePayment instances. """ grok.name('studentpayments') #: Fieldnames considered by this exporter fields = tuple( sorted(iface_names( IStudentOnlinePayment, exclude_attribs=False))) + ( 'student_id','student_state','current_session') #: The title under which this exporter will be displayed title = _(u'Student Payments') def mangle_value(self, value, name, context=None): """Treat location values special. """ if context is not None: student = context.student if name in ['student_id'] and student is not None: value = getattr(student, name, None) return super( PaymentsExporter, self).mangle_value( value, name, context=context) def export_all(self, site, filepath=None): """Export payments into filepath as CSV data. If `filepath` is ``None``, a raw string with CSV data is returned. """ return self.export(get_payments(get_students(site)), filepath) def export_student(self, student, filepath=None): return self.export(get_payments([student]), filepath) class BedTicketsExporter(grok.GlobalUtility, StudentExporterBase): """Exporter for BedTicket instances. """ grok.name('bedtickets') #: Fieldnames considered by this exporter fields = tuple( sorted(iface_names( IBedTicket, exclude_attribs=False))) + ( 'student_id', 'actual_bed_type') #: The title under which this exporter will be displayed title = _(u'Bed Tickets') def mangle_value(self, value, name, context=None): """Treat location values and others special. """ if context is not None: student = context.student if name in ['student_id'] and student is not None: value = getattr(student, name, None) if name == 'bed' and value is not None: value = getattr(value, 'bed_id', None) if name == 'actual_bed_type': value = getattr(getattr(context, 'bed', None), 'bed_type') return super( BedTicketsExporter, self).mangle_value( value, name, context=context) def export_all(self, site, filepath=None): """Export payments into filepath as CSV data. If `filepath` is ``None``, a raw string with CSV data is returned. """ return self.export(get_bedtickets(get_students(site)), filepath) def export_student(self, student, filepath=None): return self.export(get_bedtickets([student]), filepath) class StudentPaymentsOverviewExporter(StudentsExporter): """Exporter for students with payment overview. """ grok.name('paymentsoverview') curr_year = datetime.now().year year_range = range(curr_year - 9, curr_year + 1) year_range_tuple = tuple([str(year) for year in year_range]) #: Fieldnames considered by this exporter fields = ('student_id', ) + ( 'state', 'certcode', 'faccode', 'depcode', 'is_postgrad', 'current_level', 'current_session', ) + year_range_tuple #: The title under which this exporter will be displayed title = _(u'Student Payments Overview') def mangle_value(self, value, name, context=None): if name in self.year_range_tuple and context is not None: value = '' for ticket in context['payments'].values(): if ticket.p_state == 'paid' and \ ticket.p_category == 'schoolfee' and \ ticket.p_session == int(name): value = ticket.amount_auth break return super( StudentsExporter, self).mangle_value( value, name, context=context) class StudentStudyLevelsOverviewExporter(StudentsExporter): """Exporter for students with study level overview. """ grok.name('studylevelsoverview') levels = ['10',] levels += [str(level) for level in range(100,1000,10) if level % 100 < 30] levels.append('999') level_range_tuple = tuple(levels) #: Fieldnames considered by this exporter fields = ('student_id', ) + ( 'state', 'certcode', 'faccode', 'depcode', 'is_postgrad', 'current_level', 'current_session', ) + level_range_tuple #: The title under which this exporter will be displayed title = _(u'Student Study Levels Overview') def mangle_value(self, value, name, context=None): if name in self.level_range_tuple and context is not None: value = '' for level in context['studycourse'].values(): if level.level == int(name): value = '%s|%s|%s|%s' % ( level.level_session, len(level), level.validated_by, level.level_verdict) break return super( StudentsExporter, self).mangle_value( value, name, context=context)