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

Last change on this file since 16667 was 16667, checked in by Henrik Bettermann, 3 years ago

Extend PORTRAIT_CHANGE_STATES.

Fix ICT fee.

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