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

Last change on this file since 17446 was 17446, checked in by Henrik Bettermann, 19 months ago

More old fees.

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