source: main/waeup.aaue/trunk/src/waeup/aaue/students/utils.py @ 16617

Last change on this file since 16617 was 16179, checked in by Henrik Bettermann, 4 years ago

ICT/Affidavit Dues still remains.

  • Property svn:keywords set to Id
File size: 27.6 KB
Line 
1## $Id: utils.py 16179 2020-07-23 07:56:01Z 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##
18import grok
19from time import time
20from zope.component import createObject, queryUtility
21from zope.catalog.interfaces import ICatalog
22from waeup.kofa.interfaces import (
23    ADMITTED, CLEARANCE, REQUESTED, CLEARED, RETURNING, PAID,
24    academic_sessions_vocab)
25from kofacustom.nigeria.students.utils import NigeriaStudentsUtils
26from waeup.kofa.accesscodes import create_accesscode
27from waeup.kofa.students.utils import trans
28from waeup.aaue.interswitch.browser import gateway_net_amt, GATEWAY_AMT
29from waeup.aaue.interfaces import MessageFactory as _
30
31MINIMUM_UNITS_THRESHOLD = 15
32
33class CustomStudentsUtils(NigeriaStudentsUtils):
34    """A collection of customized methods.
35
36    """
37
38    PORTRAIT_CHANGE_STATES = (ADMITTED,)
39
40    def GPABoundaries(self, faccode=None, depcode=None,
41                            certcode=None, student=None):
42        if student and student.current_mode.startswith('dp'):
43            return ((1.5, 'IRNS / NER / NYV'),
44                    (2.4, 'Pass'),
45                    (3.5, 'Merit'),
46                    (4.5, 'Credit'),
47                    (5, 'Distinction'))
48        elif student:
49            return ((1, 'FRNS / NER / NYV'),
50                    (1.5, 'Pass'),
51                    (2.4, '3rd Class Honours'),
52                    (3.5, '2nd Class Honours Lower Division'),
53                    (4.5, '2nd Class Honours Upper Division'),
54                    (5, '1st Class Honours'))
55        # Session Results Presentations depend on certificate
56        results = None
57        if certcode:
58            cat = queryUtility(ICatalog, name='certificates_catalog')
59            results = list(
60                cat.searchResults(code=(certcode, certcode)))
61        if results and results[0].study_mode.startswith('dp'):
62            return ((1.5, 'IRNS / NER / NYV'),
63                    (2.4, 'Pass'),
64                    (3.5, 'Merit'),
65                    (4.5, 'Credit'),
66                    (5, 'Distinction'))
67        else:
68            return ((1, 'FRNS / NER / NYV'),
69                    (1.5, 'Pass'),
70                    (2.4, '3rd Class Honours'),
71                    (3.5, '2nd Class Honours Lower Division'),
72                    (4.5, '2nd Class Honours Upper Division'),
73                    (5, '1st Class Honours'))
74
75    def getClassFromCGPA(self, gpa, student):
76        gpa_boundaries = self.GPABoundaries(student=student)
77
78        try:
79            certificate = getattr(student['studycourse'],'certificate',None)
80            end_level = getattr(certificate, 'end_level', None)
81            final_level = max([studylevel.level for studylevel in student['studycourse'].values()])
82            if end_level and final_level >= end_level:
83                dummy, repeat = divmod(final_level, 100)
84                if gpa <= 5.1 and repeat == 20:
85                    # Irrespective of the CGPA of a student, if the He/She has
86                    # 3rd Extension, such student will be graduated with a "Pass".
87                    return 1, gpa_boundaries[1][1]
88        except ValueError:
89            pass
90
91        if gpa < gpa_boundaries[0][0]:
92            # FRNS / Fail
93            return 0, gpa_boundaries[0][1]
94        if student.entry_session < 2013 or \
95            student.current_mode.startswith('dp'):
96            if gpa < gpa_boundaries[1][0]:
97                # Pass
98                return 1, gpa_boundaries[1][1]
99        else:
100            if gpa < gpa_boundaries[1][0]:
101                # FRNS
102                # Pass degree has been phased out in 2013 for non-diploma
103                # students
104                return 0, gpa_boundaries[0][1]
105        if gpa < gpa_boundaries[2][0]:
106            # 3rd / Merit
107            return 2, gpa_boundaries[2][1]
108        if gpa < gpa_boundaries[3][0]:
109            # 2nd L / Credit
110            return 3, gpa_boundaries[3][1]
111        if gpa < gpa_boundaries[4][0]:
112            # 2nd U / Distinction
113            return 4, gpa_boundaries[4][1]
114        if gpa <= gpa_boundaries[5][0]:
115            # 1st
116            return 5, gpa_boundaries[5][1]
117        return
118
119    def getDegreeClassNumber(self, level_obj):
120        """Get degree class number (used for SessionResultsPresentation
121        reports).
122        """
123        certificate = getattr(level_obj.__parent__,'certificate', None)
124        end_level = getattr(certificate, 'end_level', None)
125        if end_level and level_obj.level >= end_level:
126            if level_obj.level > end_level:
127                # spill-over level
128                if level_obj.gpa_params[1] == 0:
129                    # no credits taken
130                    return 0
131            elif level_obj.gpa_params[1] < MINIMUM_UNITS_THRESHOLD:
132                # credits taken below limit
133                return 0
134            failed_courses = level_obj.passed_params[4]
135            not_taken_courses = level_obj.passed_params[5]
136            if '_m' in failed_courses:
137                return 0
138            if len(not_taken_courses) \
139                and not not_taken_courses == 'Nil':
140                return 0
141        elif level_obj.gpa_params[1] < MINIMUM_UNITS_THRESHOLD:
142            # credits taken below limit
143            return 0
144        if level_obj.level_verdict in ('FRNS', 'NER', 'NYV'):
145            return 0
146        # use gpa_boundaries above
147        return self.getClassFromCGPA(
148            level_obj.cumulative_params[0], level_obj.student)[0]
149
150    def increaseMatricInteger(self, student):
151        """Increase counter for matric numbers.
152        This counter can be a centrally stored attribute or an attribute of
153        faculties, departments or certificates. In the base package the counter
154        is as an attribute of the site configuration container.
155        """
156        if student.current_mode in ('ug_pt', 'de_pt', 'ug_dsh', 'de_dsh'):
157            grok.getSite()['configuration'].next_matric_integer += 1
158            return
159        elif student.is_postgrad:
160            grok.getSite()['configuration'].next_matric_integer_3 += 1
161            return
162        elif student.current_mode in ('dp_ft',):
163            grok.getSite()['configuration'].next_matric_integer_4 += 1
164            return
165        grok.getSite()['configuration'].next_matric_integer_2 += 1
166        return
167
168    def _concessionalPaymentMade(self, student):
169        if len(student['payments']):
170            for ticket in student['payments'].values():
171                if ticket.p_state == 'paid' and \
172                    ticket.p_category == 'concessional':
173                    return True
174        return False
175
176    def constructMatricNumber(self, student):
177        faccode = student.faccode
178        depcode = student.depcode
179        certcode = student.certcode
180        degree = getattr(
181            getattr(student.get('studycourse', None), 'certificate', None),
182                'degree', None)
183        year = unicode(student.entry_session)[2:]
184        if not student.state in (PAID, ) or not student.is_fresh or \
185            student.current_mode in ('found', 'ijmbe'):
186            return _('Matriculation number cannot be set.'), None
187        #if student.current_mode not in ('mug_ft', 'mde_ft') and \
188        #    not self._concessionalPaymentMade(student):
189        #    return _('Matriculation number cannot be set.'), None
190        if student.is_postgrad:
191            next_integer = grok.getSite()['configuration'].next_matric_integer_3
192            if not degree or next_integer == 0:
193                return _('Matriculation number cannot be set.'), None
194            if student.faccode in ('IOE'):
195                return None, "AAU/SPS/%s/%s/%s/%05d" % (
196                    faccode, year, degree, next_integer)
197            return None, "AAU/SPS/%s/%s/%s/%s/%05d" % (
198                faccode, depcode, year, degree, next_integer)
199        if student.current_mode in ('ug_dsh', 'de_dsh'):
200            next_integer = grok.getSite()['configuration'].next_matric_integer
201            if next_integer == 0:
202                return _('Matriculation number cannot be set.'), None
203            return None, "DSH/%s/%s/%s/%05d" % (
204                faccode, depcode, year, next_integer)
205        if student.current_mode in ('ug_pt', 'de_pt'):
206            next_integer = grok.getSite()['configuration'].next_matric_integer
207            if next_integer == 0:
208                return _('Matriculation number cannot be set.'), None
209            return None, "PTP/%s/%s/%s/%05d" % (
210                faccode, depcode, year, next_integer)
211        if student.current_mode in ('dp_ft',):
212            next_integer = grok.getSite()['configuration'].next_matric_integer_4
213            if next_integer == 0:
214                return _('Matriculation number cannot be set.'), None
215            return None, "IOE/DIP/%s/%05d" % (year, next_integer)
216        next_integer = grok.getSite()['configuration'].next_matric_integer_2
217        if next_integer == 0:
218            return _('Matriculation number cannot be set.'), None
219        if student.faccode in ('FBM', 'FCS'):
220            return None, "CMS/%s/%s/%s/%05d" % (
221                faccode, depcode, year, next_integer)
222        return None, "%s/%s/%s/%05d" % (faccode, depcode, year, next_integer)
223
224    def getReturningData(self, student):
225        """ This method defines what happens after school fee payment
226        of returning students depending on the student's senate verdict.
227        """
228        prev_level = student['studycourse'].current_level
229        cur_verdict = student['studycourse'].current_verdict
230        if cur_verdict in ('A','B','L','M','N','Z',):
231            # Successful student
232            new_level = divmod(int(prev_level),100)[0]*100 + 100
233        elif cur_verdict == 'C':
234            # Student on probation
235            new_level = int(prev_level) + 10
236        else:
237            # Student is somehow in an undefined state.
238            # Level has to be set manually.
239            new_level = prev_level
240        new_session = student['studycourse'].current_session + 1
241        return new_session, new_level
242
243    def _isPaymentDisabled(self, p_session, category, student):
244        academic_session = self._getSessionConfiguration(p_session)
245        if category.startswith('schoolfee'):
246            if 'sf_all' in academic_session.payment_disabled:
247                return True
248            if 'sf_pg' in academic_session.payment_disabled and \
249                student.is_postgrad:
250                return True
251            if 'sf_ug_pt' in academic_session.payment_disabled and \
252                student.current_mode in ('ug_pt', 'de_pt'):
253                return True
254            if 'sf_found' in academic_session.payment_disabled and \
255                student.current_mode == 'found':
256                return True
257        if category.startswith('clearance') and \
258            'cl_regular' in academic_session.payment_disabled and \
259            student.current_mode in ('ug_ft', 'de_ft', 'mug_ft', 'mde_ft'):
260            return True
261        if category == 'hostel_maintenance' and \
262            'maint_all' in academic_session.payment_disabled:
263            return True
264        return False
265
266    def setPaymentDetails(self, category, student,
267            previous_session=None, previous_level=None, combi=[]):
268        """Create Payment object and set the payment data of a student for
269        the payment category specified.
270
271        """
272        details = {}
273        p_item = u''
274        amount = 0.0
275        error = u''
276        if previous_session:
277            if previous_session < student['studycourse'].entry_session:
278                return _('The previous session must not fall below '
279                         'your entry session.'), None
280            if category == 'schoolfee':
281                # School fee is always paid for the following session
282                if previous_session > student['studycourse'].current_session:
283                    return _('This is not a previous session.'), None
284            else:
285                if previous_session > student['studycourse'].current_session - 1:
286                    return _('This is not a previous session.'), None
287            p_session = previous_session
288            p_level = previous_level
289            p_current = False
290        else:
291            p_session = student['studycourse'].current_session
292            p_level = student['studycourse'].current_level
293            p_current = True
294        academic_session = self._getSessionConfiguration(p_session)
295        if academic_session == None:
296            return _(u'Session configuration object is not available.'), None
297        # Determine fee.
298        if category == 'transfer':
299            amount = academic_session.transfer_fee
300        elif category == 'transcript_local':
301            amount = academic_session.transcript_fee_local
302        elif category == 'transcript_inter':
303            amount = academic_session.transcript_fee_inter
304        elif category == 'bed_allocation':
305            amount = academic_session.booking_fee
306        elif category == 'restitution':
307            if student.current_session != 2016 \
308                or student.current_mode not in ('ug_ft', 'dp_ft') \
309                or student.is_fresh:
310                return _(u'Restitution fee payment not required.'), None
311            amount = academic_session.restitution_fee
312        elif category == 'hostel_maintenance':
313            amount = 0.0
314            bedticket = student['accommodation'].get(
315                str(student.current_session), None)
316            if bedticket is not None and bedticket.bed is not None:
317                p_item = bedticket.display_coordinates
318                if bedticket.bed.__parent__.maint_fee > 0:
319                    amount = bedticket.bed.__parent__.maint_fee
320                else:
321                    # fallback
322                    amount = academic_session.maint_fee
323            else:
324                return _(u'No bed allocated.'), None
325        elif student.current_mode == 'found' and category not in (
326            'schoolfee', 'clearance', 'late_registration'):
327            return _('Not allowed.'), None
328        elif category.startswith('clearance'):
329            if student.state not in (ADMITTED, CLEARANCE, REQUESTED, CLEARED):
330                return _(u'Acceptance Fee payments not allowed.'), None
331            if student.current_mode in (
332                'ug_ft', 'ug_pt', 'de_ft', 'de_pt',
333                'transfer', 'mug_ft', 'mde_ft') \
334                and category != 'clearance_incl':
335                    return _("Additional fees must be included."), None
336            if student.current_mode == 'ijmbe':
337                amount = academic_session.clearance_fee_ijmbe
338            elif student.current_mode == 'bridge':
339                amount = academic_session.clearance_fee_bridge
340            elif student.current_mode == 'dp_ft':
341                amount = academic_session.clearance_fee_dp
342            elif student.faccode == 'FP':
343                amount = academic_session.clearance_fee_fp
344            elif student.current_mode in ('ug_pt', 'de_pt', 'ug_dsh', 'de_dsh'):
345                amount = academic_session.clearance_fee_ug_pt
346            elif student.current_mode == 'special_pg_pt':
347                amount = academic_session.clearance_fee_pg_pt
348            elif student.faccode == 'FCS':
349                # Students in clinical medical sciences pay the medical
350                # acceptance fee
351                amount = academic_session.clearance_fee_med
352            elif student.current_mode == 'special_pg_ft':
353                if category != 'clearance':
354                    return _("No additional fees required."), None
355                amount = academic_session.clearance_fee_pg
356            else:
357                amount = academic_session.clearance_fee
358            p_item = student['studycourse'].certificate.code
359            if amount in (0.0, None):
360                return _(u'Amount could not be determined.'), None
361            # Add Matric Gown Fee and Lapel Fee
362            if category == 'clearance_incl':
363                amount += gateway_net_amt(academic_session.matric_gown_fee) + \
364                    gateway_net_amt(academic_session.lapel_fee)
365        elif category == 'late_registration':
366            if student.is_postgrad:
367                amount = academic_session.late_pg_registration_fee
368            else:
369                amount = academic_session.late_registration_fee
370        elif category.startswith('schoolfee'):
371            try:
372                certificate = student['studycourse'].certificate
373                p_item = certificate.code
374            except (AttributeError, TypeError):
375                return _('Study course data are incomplete.'), None
376            if student.is_postgrad and category != 'schoolfee':
377                return _("No additional fees required."), None
378            if not previous_session and student.current_mode in (
379                'ug_ft', 'ug_pt', 'de_ft', 'de_pt',
380                'transfer', 'mug_ft', 'mde_ft') \
381                and not category in (
382                'schoolfee_incl', 'schoolfee_1', 'schoolfee_2'):
383                    return _("You must choose a payment which includes "
384                             "additional fees."), None
385            if category in ('schoolfee_1', 'schoolfee_2'):
386                if student.current_mode == 'ug_pt':
387                    return _("Part-time students are not allowed "
388                             "to pay by instalments."), None
389                if student.entry_session < 2015:
390                    return _("You are not allowed "
391                             "to pay by instalments."), None
392            if previous_session:
393                # Students can pay for previous sessions in all
394                # workflow states.  Fresh students are excluded by the
395                # update method of the PreviousPaymentAddFormPage.
396                if previous_level == 100:
397                    amount = getattr(certificate, 'school_fee_1', 0.0)
398                else:
399                    if student.entry_session in (2015, 2016):
400                        amount = getattr(certificate, 'school_fee_2', 0.0)
401                    else:
402                        amount = getattr(certificate, 'school_fee_3', 0.0)
403            elif student.state == CLEARED and category != 'schoolfee_2':
404                amount = getattr(certificate, 'school_fee_1', 0.0)
405                # Cut school fee by 50%
406                if category == 'schoolfee_1' and amount:
407                    amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
408            elif student.is_fresh and category == 'schoolfee_2':
409                amount = getattr(certificate, 'school_fee_1', 0.0)
410                # Cut school fee by 50%
411                if amount:
412                    amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
413            elif student.state == RETURNING and category != 'schoolfee_2':
414                if not student.father_name:
415                    return _("Personal data form is not properly filled."), None
416                # In case of returning school fee payment the payment session
417                # and level contain the values of the session the student
418                # has paid for.
419                p_session, p_level = self.getReturningData(student)
420                try:
421                    academic_session = grok.getSite()[
422                        'configuration'][str(p_session)]
423                except KeyError:
424                    return _(u'Session configuration object is not available.'), None
425                if student.entry_session >= 2015:
426                    amount = getattr(certificate, 'school_fee_2', 0.0)
427                else:
428                    amount = getattr(certificate, 'school_fee_3', 0.0)
429                # Cut school fee by 50%
430                if category == 'schoolfee_1' and amount:
431                    amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
432            elif category == 'schoolfee_2':
433                amount = getattr(certificate, 'school_fee_2', 0.0)
434                # Cut school fee by 50%
435                if amount:
436                    amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
437            else:
438                return _('Wrong state.'), None
439            if amount in (0.0, None):
440                return _(u'Amount could not be determined.'), None
441            # Add Student Union Fee, Student Id Card Fee, Sports Dev. Fee,
442            # Library Dev. Fee and Welfare Assurance
443            if category in ('schoolfee_incl', 'schoolfee_1') \
444                and student.current_mode != 'ijmbe':
445                amount += gateway_net_amt(academic_session.welfare_fee) + \
446                    gateway_net_amt(academic_session.union_fee)
447                if student.entry_session >= 2018 and student.is_fresh:
448                    amount += gateway_net_amt(academic_session.sports_fee)
449                    if student.is_postgrad:
450                        amount += gateway_net_amt(academic_session.library_pg_fee)
451                    else:
452                        amount += gateway_net_amt(academic_session.library_fee)
453            # Add non-indigenous fee and session specific penalty fees
454            if student.is_postgrad:
455                amount += academic_session.penalty_pg
456                if student.lga and not student.lga.startswith('edo'):
457                    amount += 20000.0
458            else:
459                amount += academic_session.penalty_ug
460        #elif category == ('fac_dep'):
461        #    if student.faccode in ('FAT', 'FED', 'FLW', 'FMS', 'FSS'):
462        #        amount = 4200.0
463        #    elif student.faccode in ('FAG', 'FBM', 'FCS',
464        #                             'FES', 'FET', 'FLS', 'FPS'):
465        #        amount = 5200.0
466        #    elif student.faccode == 'FAG' and student.current_level == 400:
467        #        amount = 10200.0
468        #    elif student.depcode == 'VTE':
469        #        amount = 5200.0
470        #    if student.entry_session >= 2019:
471        #        amount += gateway_net_amt(academic_session.ict_fee)
472        elif category == 'sports_library': # temporarily in 2020
473            if student.is_postgrad:
474                amount = academic_session.sports_fee + gateway_net_amt(
475                    academic_session.library_pg_fee)
476            else:
477                amount = academic_session.sports_fee + gateway_net_amt(
478                    academic_session.library_fee)
479        elif not student.is_postgrad:
480            fee_name = category + '_fee'
481            amount = getattr(academic_session, fee_name, 0.0)
482        if amount in (0.0, None):
483            return _(u'Amount could not be determined.'), None
484        # Create ticket.
485        for key in student['payments'].keys():
486            ticket = student['payments'][key]
487            if ticket.p_state == 'paid' and\
488               ticket.p_category == category and \
489               not ticket.p_category.startswith('transcript') and \
490               ticket.p_item == p_item and \
491               ticket.p_session == p_session:
492                  return _('This type of payment has already been made.'), None
493            # Additional condition in AAUE
494            if category in ('schoolfee', 'schoolfee_incl', 'schoolfee_1'):
495                if ticket.p_state == 'paid' and \
496                   ticket.p_category in ('schoolfee',
497                                         'schoolfee_incl',
498                                         'schoolfee_1') and \
499                   ticket.p_item == p_item and \
500                   ticket.p_session == p_session:
501                      return _(
502                          'Another school fee payment for this '
503                          'session has already been made.'), None
504
505        if self._isPaymentDisabled(p_session, category, student):
506            return _('This category of payments has been disabled.'), None
507        payment = createObject(u'waeup.StudentOnlinePayment')
508        timestamp = ("%d" % int(time()*10000))[1:]
509        payment.p_id = "p%s" % timestamp
510        payment.p_category = category
511        payment.p_item = p_item
512        payment.p_session = p_session
513        payment.p_level = p_level
514        payment.p_current = p_current
515        payment.amount_auth = amount
516        return None, payment
517
518    def _admissionText(self, student, portal_language):
519        inst_name = grok.getSite()['configuration'].name
520        entry_session = student['studycourse'].entry_session
521        entry_session = academic_sessions_vocab.getTerm(entry_session).title
522        text = trans(_(
523            'This is to inform you that you have been offered provisional'
524            ' admission into ${a} for the ${b} academic session as follows:',
525            mapping = {'a': inst_name, 'b': entry_session}),
526            portal_language)
527        return text
528
529    def warnCreditsOOR(self, studylevel, course=None):
530        studycourse = studylevel.__parent__
531        certificate = getattr(studycourse,'certificate', None)
532        current_level = studycourse.current_level
533        if None in (current_level, certificate):
534            return
535        end_level = certificate.end_level
536        if current_level >= end_level:
537            limit = 52
538        else:
539            limit = 48
540        if course and studylevel.total_credits + course.credits > limit:
541            return  _('Maximum credits exceeded.')
542        elif studylevel.total_credits > limit:
543            return _('Maximum credits exceeded.')
544        return
545
546    def getBedCoordinates(self, bedticket):
547        """Return descriptive bed coordinates.
548        This method can be used to customize the `display_coordinates`
549        property method in order to  display a
550        customary description of the bed space.
551        """
552        bc = bedticket.bed_coordinates.split(',')
553        if len(bc) == 4:
554            return bc[0]
555        return bedticket.bed_coordinates
556
557    def getAccommodationDetails(self, student):
558        """Determine the accommodation data of a student.
559        """
560        d = {}
561        d['error'] = u''
562        hostels = grok.getSite()['hostels']
563        d['booking_session'] = hostels.accommodation_session
564        d['allowed_states'] = hostels.accommodation_states
565        d['startdate'] = hostels.startdate
566        d['enddate'] = hostels.enddate
567        d['expired'] = hostels.expired
568        # Determine bed type
569        bt = 'all'
570        if student.sex == 'f':
571            sex = 'female'
572        else:
573            sex = 'male'
574        special_handling = 'regular'
575        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
576        return d
577
578    def checkAccommodationRequirements(self, student, acc_details):
579        msg = super(CustomStudentsUtils, self).checkAccommodationRequirements(
580            student, acc_details)
581        if msg:
582            return msg
583        if student.current_mode not in ('ug_ft', 'de_ft', 'mug_ft', 'mde_ft'):
584            return _('You are not eligible to book accommodation.')
585        return
586
587    # AAUE prefix
588    STUDENT_ID_PREFIX = u'E'
589
590    STUDENT_EXPORTER_NAMES = (
591            'students',
592            'studentstudycourses',
593            'studentstudylevels',
594            'coursetickets',
595            'studentpayments',
596            'bedtickets',
597            'unpaidpayments',
598            'sfpaymentsoverview',
599            'studylevelsoverview',
600            'combocard',
601            'bursary',
602            'levelreportdata',
603            'outstandingcourses',
604            'sessionpaymentsoverview',
605            'accommodationpayments',
606            'transcriptdata',
607            'trimmedpayments',
608            'trimmed',
609            )
610
611    # Maximum size of upload files in kB
612    MAX_KB = 500
Note: See TracBrowser for help on using the repository browser.