source: main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py @ 13100

Last change on this file since 13100 was 13091, checked in by Henrik Bettermann, 9 years ago

Make student email field required.

A trick was needed to make test_manage_contact_student work again.

  • Property svn:keywords set to Id
File size: 26.0 KB
Line 
1## $Id: interfaces.py 13091 2015-06-23 05:58:58Z 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#from datetime import datetime
19from zope.component import getUtility
20from zope.interface import Attribute, Interface
21from zope import schema
22from zc.sourcefactory.contextual import BasicContextualSourceFactory
23from waeup.kofa.browser.interfaces import IStudentNavigationBase
24from waeup.kofa.interfaces import (
25    IKofaObject, academic_sessions_vocab, validate_email, ICSVExporter,
26    ContextualDictSourceFactoryBase)
27from waeup.kofa.interfaces import MessageFactory as _
28from waeup.kofa.schema import TextLineChoice, FormattedDate, PhoneNumber
29from waeup.kofa.students.vocabularies import (
30    StudyLevelSource, contextual_reg_num_source, contextual_mat_num_source,
31    GenderSource, nats_vocab
32    )
33from waeup.kofa.payments.interfaces import (
34    IPaymentsContainer, IOnlinePayment)
35from waeup.kofa.university.vocabularies import (
36    CourseSource, StudyModeSource, CertificateSource, SemesterSource,
37    )
38
39class PreviousPaymentCategorySource(ContextualDictSourceFactoryBase):
40    """A source that delivers all selectable categories of previous session
41    payments.
42    """
43    #: name of dict to deliver from kofa utils.
44    DICT_NAME = 'PREVIOUS_PAYMENT_CATEGORIES'
45
46class BalancePaymentCategorySource(ContextualDictSourceFactoryBase):
47    """A source that delivers all selectable items of balance payments.
48    """
49    #: name of dict to deliver from kofa utils.
50    DICT_NAME = 'BALANCE_PAYMENT_CATEGORIES'
51
52# VerdictSource can't be placed into the vocabularies module because it
53# requires importing IStudentsUtils which then leads to circular imports.
54class VerdictSource(BasicContextualSourceFactory):
55    """A verdicts source delivers all verdicts provided
56    in the portal.
57    """
58    def getValues(self, context):
59        verdicts_dict = getUtility(IStudentsUtils).VERDICTS_DICT
60        return sorted(verdicts_dict.keys())
61
62    def getToken(self, context, value):
63        return value
64
65    def getTitle(self, context, value):
66        verdicts_dict = getUtility(IStudentsUtils).VERDICTS_DICT
67        if value != '0':
68            return verdicts_dict[value] + ' (%s)' % value
69        return verdicts_dict[value]
70
71
72class IStudentsUtils(Interface):
73    """A collection of methods which are subject to customization.
74    """
75    def setReturningData(student):
76        """ This method defines what happens after school fee payment
77        depending on the student's senate verdict.
78
79        In the base configuration current level is always increased
80        by 100 no matter which verdict has been assigned.
81        """
82
83    def setPaymentDetails(category, student, previous_session=None,
84            previous_level=None,):
85        """Create Payment object and set the payment data of a student for
86        the payment category specified.
87        """
88
89    def increaseMatricInteger(student):
90        """Increase counter for matric numbers.
91
92        This counter can be a centrally stored attribute or an attribute of
93        faculties, departments or certificates. In the base package the counter
94        is as an attribute of the site configuration object.
95        """
96
97    def constructMatricNumber(student):
98        """Fetch the matric number counter which fits the student and
99        construct the new matric number of the student.
100
101        In the base package the counter is returned which is as an attribute
102        of the site configuration object.
103        """
104
105    def setMatricNumber(student):
106        """Set matriculation number of student.
107
108        If the student's matric number is unset a new matric number is
109        constructed according to the matriculation number construction rules
110        defined in the constructMatricNumber method. The new matric number is
111        set, the students catalog updated. The corresponding matric number
112        counter is increased by one.
113
114        This method is tested but not used in the base package. It can
115        be used in custom packages by adding respective views
116        and by customizing increaseMatricInteger and constructMatricNumber
117        according to the university's matriculation number construction rules.
118
119        The method can be disabled by setting the counter to zero.
120        """
121
122    def getAccommodation_details(student):
123        """Determine the accommodation dates of a student.
124        """
125
126    def selectBed(available_beds):
127        """Select a bed from a list of available beds.
128        In the standard configuration we select the first bed found,
129        but can also randomize the selection if we like.
130        """
131
132    def getPDFCreator(context):
133        """Get some IPDFCreator instance suitable for use with `context`.
134        """
135
136    def renderPDF(view, subject='', filename='slip.pdf',):
137        """Render pdf slips for various pages.
138        """
139
140class IStudentsContainer(IKofaObject):
141    """A students container contains university students.
142    """
143    def addStudent(student):
144        """Add an IStudent object and subcontainers.
145        """
146
147    def archive(id=None):
148        """Create on-dist archive of students.
149
150        If id is `None`, all students are archived.
151
152        If id contains a single id string, only the respective
153        students are archived.
154
155        If id contains a list of id strings all of the respective
156        students types are saved to disk.
157        """
158
159    def clear(id=None, archive=True):
160        """Remove students of type given by 'id'.
161
162        Optionally archive the students.
163
164        If id is `None`, all students are archived.
165
166        If id contains a single id string, only the respective
167        students are archived.
168
169        If id contains a list of id strings all of the respective
170        student types are saved to disk.
171
172        If `archive` is ``False`` none of the archive-handling is done
173        and respective students are simply removed from the
174        database.
175        """
176
177    unique_student_id = Attribute("""A unique student id.""")
178
179class IStudentNavigation(IStudentNavigationBase):
180    """Interface needed for navigation and logging. This interface is
181    implemented by all content classes in the students section.
182    """
183    student = Attribute('Student object of context')
184
185    def writeLogMessage(view, message):
186        """Write a view specific log message into students.log.
187        """
188
189class IStudentBase(IKofaObject):
190    """Representation of student base data.
191    """
192    history = Attribute('Object history, a list of messages')
193    state = Attribute('Registration state')
194    translated_state = Attribute('Real name of the registration state')
195    certcode = Attribute('Certificate code of any chosen study course')
196    depcode = Attribute('Department code of any chosen study course')
197    faccode = Attribute('Faculty code of any chosen study course')
198    entry_session = Attribute('Entry session')
199    current_session = Attribute('Current session')
200    current_level = Attribute('Current level')
201    current_mode = Attribute('Current mode')
202    current_verdict = Attribute('Current verdict')
203    fullname = Attribute('All name parts separated by hyphens')
204    display_fullname = Attribute('Fullname as displayed on pages')
205    is_postgrad = Attribute('True if postgraduate student')
206    is_special_postgrad = Attribute('True if special postgraduate student')
207    is_fresh = Attribute('True if fresh student')
208    before_payment = Attribute('True if no previous payment has to be made')
209    personal_data_expired = Attribute('True if personal data expired')
210    transcript_enabled = Attribute('True if transcript processing is enabled')
211
212    password = Attribute('Encrypted password')
213    temp_password = Attribute('Dictionary with user name, timestamp and encrypted password')
214
215    suspended = schema.Bool(
216        title = _(u'Account suspended'),
217        default = False,
218        required = False,
219        )
220
221    suspended_comment = schema.Text(
222        title = _(u"Reasons for Deactivation"),
223        required = False,
224        description = _(
225            u'This message will be shown if and only if deactivated '
226            'students try to login.'),
227        )
228
229    student_id = schema.TextLine(
230        title = _(u'Student Id'),
231        required = False,
232        )
233
234    firstname = schema.TextLine(
235        title = _(u'First Name'),
236        required = True,
237        )
238
239    middlename = schema.TextLine(
240        title = _(u'Middle Name'),
241        required = False,
242        )
243
244    lastname = schema.TextLine(
245        title = _(u'Last Name (Surname)'),
246        required = True,
247        )
248
249    sex = schema.Choice(
250        title = _(u'Sex'),
251        source = GenderSource(),
252        required = True,
253        )
254
255    reg_number = TextLineChoice(
256        title = _(u'Registration Number'),
257        required = True,
258        readonly = False,
259        source = contextual_reg_num_source,
260        )
261
262    matric_number = TextLineChoice(
263        title = _(u'Matriculation Number'),
264        required = False,
265        readonly = False,
266        source = contextual_mat_num_source,
267        )
268
269    adm_code = schema.TextLine(
270        title = _(u'PWD Activation Code'),
271        required = False,
272        readonly = False,
273        )
274
275    email = schema.ASCIILine(
276        title = _(u'Email'),
277        required = True,
278        constraint=validate_email,
279        )
280    phone = PhoneNumber(
281        title = _(u'Phone'),
282        description = u'',
283        required = False,
284        )
285
286    def setTempPassword(user, password):
287        """Set a temporary password (LDAP-compatible) SSHA encoded for
288        officers.
289        """
290
291    def getTempPassword():
292        """Check if a temporary password has been set and if it
293        is not expired. Return the temporary password if valid,
294        None otherwise. Unset the temporary password if expired.
295        """
296
297    def transfer(certificate, current_session,
298        current_level, current_verdict):
299        """ Creates a new studycourse and backups the old one.
300        """
301
302    def revert_transfer():
303        """ Revert previous transfer.
304        """
305
306class IUGStudentClearance(IKofaObject):
307    """Representation of undergraduate student clearance data.
308    """
309    officer_comment = schema.Text(
310        title = _(u"Officer's Comment"),
311        required = False,
312        )
313
314    clearance_locked = schema.Bool(
315        title = _(u'Clearance form locked'),
316        default = False,
317        required = False,
318        )
319
320    clr_code = schema.TextLine(
321        title = _(u'CLR Activation Code'),
322        required = False,
323        readonly = False,
324        )
325
326    date_of_birth = FormattedDate(
327        title = _(u'Date of Birth'),
328        required = True,
329        show_year = True,
330        )
331
332    nationality = schema.Choice(
333        vocabulary = nats_vocab,
334        title = _(u'Nationality'),
335        required = False,
336        )
337
338class IPGStudentClearance(IUGStudentClearance):
339    """Representation of postgraduate student clearance data.
340    """
341    employer = schema.TextLine(
342        title = _(u'Employer'),
343        required = False,
344        readonly = False,
345        )
346
347class IStudentPersonal(IKofaObject):
348    """Representation of student personal data.
349    """
350    personal_updated = schema.Datetime(
351        title = _(u'Updated'),
352        required = False,
353        readonly = False,
354        )
355
356    perm_address = schema.Text(
357        title = _(u'Permanent Address'),
358        required = False,
359        )
360
361class IStudentTranscript(IKofaObject):
362    """Representation of student transcript data.
363    """
364
365    transcript_comment = schema.Text(
366        title = _(u'Comment'),
367        required = False,
368        )
369
370
371class IStudent(IStudentBase,IUGStudentClearance,IPGStudentClearance,
372    IStudentPersonal, IStudentTranscript):
373    """Representation of a student.
374    """
375
376class IStudentPersonalEdit(IStudentPersonal):
377    """Interface for editing personal data by students.
378    Here we can repeat the fields from IStudentPersonal and set the
379    `required` if necessary.
380    """
381
382    perm_address = schema.Text(
383        title = _(u'Permanent Address'),
384        required = True,
385        )
386
387class IStudentUpdateByRegNo(IStudent):
388    """Representation of a student. Skip regular reg_number validation.
389    """
390    reg_number = schema.TextLine(
391        title = _(u'Registration Number'),
392        required = False,
393        )
394
395class IStudentUpdateByMatricNo(IStudent):
396    """Representation of a student. Skip regular matric_number validation.
397    """
398    matric_number = schema.TextLine(
399        title = _(u'Matriculation Number'),
400        required = False,
401        )
402
403class IStudentRequestPW(IStudent):
404    """Representation of a student for first-time password request.
405    This interface is used when students use the requestpw page to
406    login for the the first time.
407    """
408    number = schema.TextLine(
409        title = _(u'Registr. or Matric. Number'),
410        required = True,
411        )
412
413    firstname = schema.TextLine(
414        title = _(u'First Name'),
415        required = True,
416        )
417
418    email = schema.ASCIILine(
419        title = _(u'Email Address'),
420        required = True,
421        constraint=validate_email,
422        )
423
424class IStudentStudyCourse(IKofaObject):
425    """Representation of student study course data.
426    """
427    next_session_allowed = Attribute('True if the student can proceed to next session')
428    is_postgrad = Attribute('True if student is postgraduate student')
429    is_current = Attribute('True if the study course is the current course of studies')
430    is_previous = Attribute('True if the study course is the previous course of studies')
431
432    certificate = schema.Choice(
433        title = _(u'Certificate'),
434        source = CertificateSource(),
435        required = False,
436        )
437
438    entry_mode = schema.Choice(
439        title = _(u'Entry Mode'),
440        source = StudyModeSource(),
441        required = True,
442        readonly = False,
443        )
444
445    entry_session = schema.Choice(
446        title = _(u'Entry Session'),
447        source = academic_sessions_vocab,
448        #default = datetime.now().year,
449        required = True,
450        readonly = False,
451        )
452
453    current_session = schema.Choice(
454        title = _(u'Current Session'),
455        source = academic_sessions_vocab,
456        required = True,
457        readonly = False,
458        )
459
460    current_level = schema.Choice(
461        title = _(u'Current Level'),
462        source = StudyLevelSource(),
463        required = False,
464        readonly = False,
465        )
466
467    current_verdict = schema.Choice(
468        title = _(u'Current Verdict'),
469        source = VerdictSource(),
470        default = '0',
471        required = False,
472        )
473
474    previous_verdict = schema.Choice(
475        title = _(u'Previous Verdict'),
476        source = VerdictSource(),
477        default = '0',
478        required = False,
479        )
480
481    def addStudentStudyLevel(cert, studylevel):
482        """Add a study level object.
483        """
484
485    def getTranscriptData():
486        """Get a sorted list of dicts with level and course ticket data.
487        This method is used for transcripts.
488        """
489
490class IStudentStudyCourseTransfer(IStudentStudyCourse):
491    """An interface used for student transfers.
492    """
493    certificate = schema.Choice(
494        title = _(u'Certificate'),
495        source = CertificateSource(),
496        required = True,
497        )
498
499    current_level = schema.Choice(
500        title = _(u'Current Level'),
501        source = StudyLevelSource(),
502        required = True,
503        readonly = False,
504        )
505
506    entry_session = schema.Choice(
507        title = _(u'Entry Session'),
508        source = academic_sessions_vocab,
509        #default = datetime.now().year,
510        required = False,
511        readonly = False,
512        )
513
514
515IStudentStudyCourseTransfer['certificate'].order = IStudentStudyCourse[
516    'certificate'].order
517IStudentStudyCourseTransfer['current_level'].order = IStudentStudyCourse[
518    'current_level'].order
519
520class IStudentStudyCourseTranscript(IKofaObject):
521    """An interface for student transcripts.
522    """
523    entry_mode = schema.Choice(
524        title = _(u'Entry Mode'),
525        source = StudyModeSource(),
526        required = True,
527        readonly = False,
528        )
529
530    entry_session = schema.Choice(
531        title = _(u'Entry Session'),
532        source = academic_sessions_vocab,
533        #default = datetime.now().year,
534        required = True,
535        readonly = False,
536        )
537
538class IStudentVerdictUpdate(IKofaObject):
539    """A interface for verdict imports.
540    """
541    current_verdict = schema.Choice(
542        title = _(u'Current Verdict'),
543        source = VerdictSource(),
544        required = True,
545        )
546
547    current_session = schema.Choice(
548        title = _(u'Current Session'),
549        source = academic_sessions_vocab,
550        required = True,
551        )
552
553    current_level = schema.Choice(
554        title = _(u'Current Level'),
555        source = StudyLevelSource(),
556        required = True,
557        )
558
559    bypass_validation = schema.Bool(
560        title = _(u'Bypass validation'),
561        required = False,
562        )
563
564    validated_by = schema.TextLine(
565        title = _(u'Validated by'),
566        required = False,
567        )
568
569class IStudentStudyLevel(IKofaObject):
570    """A representation of student study level data.
571    """
572    certcode = Attribute('The certificate code of the study course')
573    is_current_level = Attribute('True if level is current level of the student')
574    level_title = Attribute('Level title from source')
575    getSessionString = Attribute('Session title from source')
576    number_of_tickets = Attribute('Number of tickets contained in this level')
577    passed_params = Attribute('Information about passed and failed courses')
578    gpa_params_rectified = Attribute('Corrected sessional GPA parameters')
579    gpa_params = Attribute('GPA parameters for this level.')
580    cumulative_params = Attribute('Cumulative GPA and other cumulative parameters for this level')
581    course_registration_allowed = Attribute('True if registerin courses is allowed')
582
583    level = schema.Choice(
584        title = _(u'Level'),
585        source = StudyLevelSource(),
586        required = True,
587        readonly = False,
588        )
589
590    level_session = schema.Choice(
591        title = _(u'Session'),
592        source = academic_sessions_vocab,
593        required = True,
594        )
595
596    level_verdict = schema.Choice(
597        title = _(u'Verdict'),
598        source = VerdictSource(),
599        default = '0',
600        required = False,
601        )
602
603    validated_by = schema.TextLine(
604        title = _(u'Validated by'),
605        default = None,
606        required = False,
607        )
608
609    validation_date = schema.Datetime(
610        title = _(u'Validation Date'),
611        required = False,
612        readonly = False,
613        )
614
615    total_credits = schema.Int(
616        title = _(u'Total Credits'),
617        required = False,
618        readonly = True,
619        )
620
621    gpa = schema.Int(
622        title = _(u'Unrectified GPA'),
623        required = False,
624        readonly = True,
625        )
626
627    def addCourseTicket(ticket, course):
628        """Add a course ticket object.
629        """
630
631    def addCertCourseTickets(cert):
632        """Collect all certificate courses and create course
633        tickets automatically.
634        """
635
636class ICourseTicket(IKofaObject):
637    """A representation of course ticket data.
638    """
639    certcode = Attribute('Certificate code of the study course')
640    level_session = Attribute('Session of the study level the ticket has been added to')
641    level = Attribute('Level value of the study level the ticket has been added to')
642    grade = Attribute('Grade calculated from score')
643    weight = Attribute('Weight calculated from score')
644    removable_by_student = Attribute('True if student is allowed to remove the ticket')
645    editable_by_lecturer = Attribute('True if lecturer is allowed to edit the ticket')
646
647    code = Attribute('Code of the original course')
648
649    title = schema.TextLine(
650        title = _(u'Title'),
651        required = False,
652        )
653
654    fcode = schema.TextLine(
655        title = _(u'Faculty Code'),
656        required = False,
657        )
658
659    dcode = schema.TextLine(
660        title = _(u'Department Code'),
661        required = False,
662        )
663
664    semester = schema.Choice(
665        title = _(u'Semester/Term'),
666        source = SemesterSource(),
667        required = False,
668        )
669
670    passmark = schema.Int(
671        title = _(u'Passmark'),
672        required = False,
673        )
674
675    credits = schema.Int(
676        title = _(u'Credits'),
677        required = False,
678        )
679
680    mandatory = schema.Bool(
681        title = _(u'Required'),
682        default = False,
683        required = False,
684        )
685
686    score = schema.Int(
687        title = _(u'Score'),
688        default = None,
689        required = False,
690        missing_value = None,
691        )
692
693    carry_over = schema.Bool(
694        title = _(u'Carry-over Course'),
695        default = False,
696        required = False,
697        )
698
699    automatic = schema.Bool(
700        title = _(u'Automatical Creation'),
701        default = False,
702        required = False,
703        )
704
705class ICourseTicketAdd(IKofaObject):
706    """An interface for adding course tickets.
707    """
708    course = schema.Choice(
709        title = _(u'Course'),
710        source = CourseSource(),
711        readonly = False,
712        )
713
714class ICourseTicketImport(ICourseTicket):
715    """An interface for importing course results and nothing more.
716    """
717    score = schema.Int(
718        title = _(u'Score'),
719        required = False,
720        readonly = False,
721        )
722
723    level_session = schema.Choice(
724        title = _(u'Level Session'),
725        source = academic_sessions_vocab,
726        required = False,
727        readonly = False,
728        )
729
730class IStudentAccommodation(IKofaObject):
731    """A container for student accommodation objects.
732    """
733
734    def addBedTicket(bedticket):
735        """Add a bed ticket object.
736        """
737
738
739class IBedTicket(IKofaObject):
740    """A representation of accommodation booking data.
741    """
742    bed = Attribute('The bed object.')
743    booking_date = Attribute('Date of booking the bed')
744
745    display_coordinates = schema.TextLine(
746        title = _(u'Allocated Bed'),
747        required = False,
748        readonly = True,
749        )
750
751    bed_coordinates = schema.TextLine(
752        title = u'',
753        required = True,
754        readonly = False,
755        )
756
757    bed_type = schema.TextLine(
758        title = _(u'Requested Bed Type'),
759        required = True,
760        readonly = False,
761        )
762
763    booking_session = schema.Choice(
764        title = _(u'Session'),
765        source = academic_sessions_vocab,
766        required = True,
767        readonly = True,
768        )
769
770    booking_date = schema.Datetime(
771        title = _(u'Booking Date'),
772        required = False,
773        readonly = True,
774        )
775
776    booking_code = schema.TextLine(
777        title = _(u'Booking Activation Code'),
778        required = False,
779        readonly = True,
780        )
781
782    def getSessionString():
783        """Returns the title of academic_sessions_vocab term of the session
784        when the bed was booked.
785        """
786
787class IStudentPaymentsContainer(IPaymentsContainer):
788    """A container for student payment objects.
789    """
790
791class IStudentOnlinePayment(IOnlinePayment):
792    """A student payment via payment gateways.
793    """
794
795    p_current = schema.Bool(
796        title = _(u'Current Session Payment'),
797        default = True,
798        required = False,
799        )
800
801    p_level = schema.Int(
802        title = _(u'Payment Level'),
803        required = False,
804        )
805
806    def doAfterStudentPayment():
807        """Process student after payment was made.
808        """
809
810    def doAfterStudentPaymentApproval():
811        """Process student after payment was approved.
812        """
813
814    def approveStudentPayment():
815        """Approve payment and process student.
816        """
817
818IStudentOnlinePayment['p_level'].order = IStudentOnlinePayment[
819    'p_session'].order
820
821class IStudentPreviousPayment(IKofaObject):
822    """An interface for adding previous session payments.
823    """
824
825    p_category = schema.Choice(
826        title = _(u'Payment Category'),
827        default = u'schoolfee',
828        source = PreviousPaymentCategorySource(),
829        required = True,
830        )
831
832    p_session = schema.Choice(
833        title = _(u'Payment Session'),
834        source = academic_sessions_vocab,
835        required = True,
836        )
837
838    p_level = schema.Choice(
839        title = _(u'Payment Level'),
840        source = StudyLevelSource(),
841        required = True,
842        )
843
844class IStudentBalancePayment(IKofaObject):
845    """An interface for adding balances.
846    """
847
848    p_category = schema.Choice(
849        title = _(u'Payment Category'),
850        default = u'schoolfee',
851        required = True,
852        source = BalancePaymentCategorySource(),
853        )
854
855    balance_session = schema.Choice(
856        title = _(u'Payment Session'),
857        source = academic_sessions_vocab,
858        required = True,
859        )
860
861    balance_level = schema.Choice(
862        title = _(u'Payment Level'),
863        source = StudyLevelSource(),
864        required = True,
865        )
866
867    balance_amount = schema.Float(
868        title = _(u'Balance Amount'),
869        default = None,
870        required = True,
871        readonly = False,
872        description = _(
873            u'Balance in Naira '),
874        )
875
876class ICSVStudentExporter(ICSVExporter):
877    """A regular ICSVExporter that additionally supports exporting
878      data from a given student object.
879    """
880    def get_filtered(site, **kw):
881        """Get a filtered set of students.
882        """
883
884    def get_selected(site, selected):
885        """Get set of selected students.
886        """
887
888    def export_student(student, filepath=None):
889        """Export data for a given student.
890        """
891
892    def export_filtered(site, filepath=None, **kw):
893        """Export data for filtered set of students.
894        """
895
896    def export_selected(site, filepath=None, **kw):
897        """Export data for selected set of students.
898        """
Note: See TracBrowser for help on using the repository browser.