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

Last change on this file since 14462 was 14462, checked in by Henrik Bettermann, 8 years ago

Customize getClassFromCGPA.

  • Property svn:keywords set to Id
File size: 20.8 KB
Line 
1## $Id: utils.py 14462 2017-01-26 08:52:42Z 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
21from waeup.kofa.interfaces import (
22    ADMITTED, CLEARANCE, REQUESTED, CLEARED, RETURNING, PAID,
23    academic_sessions_vocab)
24from kofacustom.nigeria.students.utils import NigeriaStudentsUtils
25from waeup.kofa.accesscodes import create_accesscode
26from waeup.kofa.students.utils import trans
27from waeup.aaue.interswitch.browser import gateway_net_amt, GATEWAY_AMT
28from waeup.aaue.interfaces import MessageFactory as _
29
30class CustomStudentsUtils(NigeriaStudentsUtils):
31    """A collection of customized methods.
32
33    """
34
35    PORTRAIT_CHANGE_STATES = (ADMITTED,)
36
37    gpa_boundaries = ((1, 'FRNS / NEOR / NEOV'),
38                      (1.5, 'Pass'),
39                      (2.4, '3rd Class Honours'),
40                      (3.5, '2nd Class Honours Lower Division'),
41                      (4.5, '2nd Class Honours Upper Division'),
42                      (5, '1st Class Honours'))
43
44    def getClassFromCGPA(self, gpa, student):
45        if gpa < self.gpa_boundaries[0][0]:
46            # FRNS
47            return 0, self.gpa_boundaries[0][1]
48        if student.entry_session < 2013:
49            if gpa < self.gpa_boundaries[1][0]:
50                # Pass
51                return 1, self.gpa_boundaries[1][1]
52        else:
53            if gpa < self.gpa_boundaries[1][0]:
54                # FRNS (Pass degree has been phased out in 2013)
55                return 0, self.gpa_boundaries[0][1]
56        if gpa < self.gpa_boundaries[2][0]:
57            # 3rd
58            return 2, self.gpa_boundaries[2][1]
59        if gpa < self.gpa_boundaries[3][0]:
60            # 2nd L
61            return 3, self.gpa_boundaries[3][1]
62        if gpa < self.gpa_boundaries[4][0]:
63            # 2nd U
64            return 4, self.gpa_boundaries[4][1]
65        if gpa <= self.gpa_boundaries[5][0]:
66            # 1st
67            return 5, self.gpa_boundaries[5][1]
68        return 'N/A'
69
70    def getDegreeClassNumber(self, level_obj):
71        """Get degree class number (used for SessionResultsPresentation
72        reports).
73        """
74        if level_obj.gpa_params[1] == 0:
75            # No credits weighted
76            return 0
77        if level_obj.level_verdict in ('FRNS', 'NEOR', 'NEOV'):
78            return 0
79        certificate = getattr(level_obj.__parent__,'certificate', None)
80        end_level = getattr(certificate, 'end_level', None)
81        if end_level and level_obj.level >= end_level:
82            failed_courses = level_obj.passed_params[4]
83            not_taken_courses = level_obj.passed_params[5]
84            if '_m' in failed_courses:
85                return 0
86            if len(not_taken_courses) \
87                and not not_taken_courses == 'NIL':
88                return 0
89        # use gpa_boundaries above
90        return self.getClassFromCGPA(
91            level_obj.cumulative_params[0], level_obj.student)[0]
92
93    def increaseMatricInteger(self, student):
94        """Increase counter for matric numbers.
95        This counter can be a centrally stored attribute or an attribute of
96        faculties, departments or certificates. In the base package the counter
97        is as an attribute of the site configuration container.
98        """
99        if student.current_mode in ('ug_pt', 'de_pt'):
100            grok.getSite()['configuration'].next_matric_integer += 1
101            return
102        elif student.is_postgrad:
103            grok.getSite()['configuration'].next_matric_integer_3 += 1
104            return
105        elif student.current_mode in ('dp_ft',):
106            grok.getSite()['configuration'].next_matric_integer_4 += 1
107            return
108        grok.getSite()['configuration'].next_matric_integer_2 += 1
109        return
110
111    def _concessionalPaymentMade(self, student):
112        if len(student['payments']):
113            for ticket in student['payments'].values():
114                if ticket.p_state == 'paid' and \
115                    ticket.p_category == 'concessional':
116                    return True
117        return False
118
119    def constructMatricNumber(self, student):
120        faccode = student.faccode
121        depcode = student.depcode
122        certcode = student.certcode
123        degree = getattr(
124            getattr(student.get('studycourse', None), 'certificate', None),
125                'degree', None)
126        year = unicode(student.entry_session)[2:]
127        if not student.state in (PAID, ) or not student.is_fresh or \
128            student.current_mode == 'found':
129            return _('Matriculation number cannot be set.'), None
130        #if student.current_mode not in ('mug_ft', 'mde_ft') and \
131        #    not self._concessionalPaymentMade(student):
132        #    return _('Matriculation number cannot be set.'), None
133        if student.is_postgrad:
134            next_integer = grok.getSite()['configuration'].next_matric_integer_3
135            if not degree or next_integer == 0:
136                return _('Matriculation number cannot be set.'), None
137            if student.faccode in ('IOE'):
138                return None, "AAU/SPS/%s/%s/%s/%05d" % (
139                    faccode, year, degree, next_integer)
140            return None, "AAU/SPS/%s/%s/%s/%s/%05d" % (
141                faccode, depcode, year, degree, next_integer)
142        if student.current_mode in ('ug_pt', 'de_pt'):
143            next_integer = grok.getSite()['configuration'].next_matric_integer
144            if next_integer == 0:
145                return _('Matriculation number cannot be set.'), None
146            return None, "PTP/%s/%s/%s/%05d" % (
147                faccode, depcode, year, next_integer)
148        if student.current_mode in ('dp_ft',):
149            next_integer = grok.getSite()['configuration'].next_matric_integer_4
150            if next_integer == 0:
151                return _('Matriculation number cannot be set.'), None
152            return None, "IOE/DIP/%s/%05d" % (year, next_integer)
153        next_integer = grok.getSite()['configuration'].next_matric_integer_2
154        if next_integer == 0:
155            return _('Matriculation number cannot be set.'), None
156        if student.faccode in ('FBM', 'FCS'):
157            return None, "CMS/%s/%s/%s/%05d" % (
158                faccode, depcode, year, next_integer)
159        return None, "%s/%s/%s/%05d" % (faccode, depcode, year, next_integer)
160
161    def getReturningData(self, student):
162        """ This method defines what happens after school fee payment
163        of returning students depending on the student's senate verdict.
164        """
165        prev_level = student['studycourse'].current_level
166        cur_verdict = student['studycourse'].current_verdict
167        if cur_verdict in ('A','B','C', 'L','M','N','Z',):
168            # Successful student
169            new_level = divmod(int(prev_level),100)[0]*100 + 100
170        #elif cur_verdict == 'C':
171        #    # Student on probation
172        #    new_level = int(prev_level) + 10
173        else:
174            # Student is somehow in an undefined state.
175            # Level has to be set manually.
176            new_level = prev_level
177        new_session = student['studycourse'].current_session + 1
178        return new_session, new_level
179
180    def _isPaymentDisabled(self, p_session, category, student):
181        academic_session = self._getSessionConfiguration(p_session)
182        if category.startswith('schoolfee'):
183            if 'sf_all' in academic_session.payment_disabled:
184                return True
185            if 'sf_pg' in academic_session.payment_disabled and \
186                student.is_postgrad:
187                return True
188            if 'sf_pt' in academic_session.payment_disabled and \
189                student.current_mode.endswith('_pt'):
190                return True
191            if 'sf_found' in academic_session.payment_disabled and \
192                student.current_mode == 'found':
193                return True
194        if category.startswith('clearance') and \
195            'cl_regular' in academic_session.payment_disabled and \
196            student.current_mode in ('ug_ft', 'de_ft', 'mug_ft', 'mde_ft'):
197            return True
198        if category == 'hostel_maintenance' and \
199            'maint_all' in academic_session.payment_disabled:
200            return True
201        return False
202
203    def setPaymentDetails(self, category, student,
204            previous_session=None, previous_level=None):
205        """Create Payment object and set the payment data of a student for
206        the payment category specified.
207
208        """
209        details = {}
210        p_item = u''
211        amount = 0.0
212        error = u''
213        if previous_session:
214            return _('Previous session payment not yet implemented.'), None
215        p_session = student['studycourse'].current_session
216        p_level = student['studycourse'].current_level
217        p_current = True
218        academic_session = self._getSessionConfiguration(p_session)
219        if academic_session == None:
220            return _(u'Session configuration object is not available.'), None
221        # Determine fee.
222        if category == 'transfer':
223            amount = academic_session.transfer_fee
224        elif category == 'transcript_local':
225            amount = academic_session.transcript_fee_local
226        elif category == 'transcript_inter':
227            amount = academic_session.transcript_fee_inter
228        elif category == 'bed_allocation':
229            amount = academic_session.booking_fee
230        elif category == 'restitution':
231            if student.entry_session == 2016 \
232                and student.current_session == 2016 \
233                or student.current_mode != 'ug_ft':
234                return _(u'Restitution fee payment not required.'), None
235            amount = academic_session.restitution_fee
236        elif category == 'hostel_maintenance':
237            amount = 0.0
238            bedticket = student['accommodation'].get(
239                str(student.current_session), None)
240            if bedticket is not None and bedticket.bed is not None:
241                p_item = bedticket.display_coordinates
242                if bedticket.bed.__parent__.maint_fee > 0:
243                    amount = bedticket.bed.__parent__.maint_fee
244                else:
245                    # fallback
246                    amount = academic_session.maint_fee
247            else:
248                return _(u'No bed allocated.'), None
249        elif student.current_mode == 'found' and category not in (
250            'schoolfee', 'clearance', 'late_registration'):
251            return _('Not allowed.'), None
252        elif category.startswith('clearance'):
253            if student.state not in (ADMITTED, CLEARANCE, REQUESTED, CLEARED):
254                return _(u'Acceptance Fee payments not allowed.'), None
255            if student.current_mode in (
256                'ug_ft', 'ug_pt', 'de_ft', 'de_pt',
257                'transfer', 'mug_ft', 'mde_ft') \
258                and category != 'clearance_incl':
259                    return _("Additional fees must be included."), None
260            if student.faccode == 'FP':  # includes IJMBE
261                amount = academic_session.clearance_fee_fp
262            elif student.current_mode.endswith('_pt'):
263                if student.is_postgrad:
264                    amount = academic_session.clearance_fee_pg_pt
265                else:
266                    amount = academic_session.clearance_fee_ug_pt
267            elif student.faccode == 'FCS':
268                # Students in clinical medical sciences pay the medical
269                # acceptance fee
270                amount = academic_session.clearance_fee_med
271            elif student.is_postgrad:  # and not part-time
272                if category != 'clearance':
273                    return _("No additional fees required."), None
274                amount = academic_session.clearance_fee_pg
275            else:
276                amount = academic_session.clearance_fee
277            p_item = student['studycourse'].certificate.code
278            if amount in (0.0, None):
279                return _(u'Amount could not be determined.'), None
280            # Add Matric Gown Fee and Lapel Fee
281            if category == 'clearance_incl':
282                amount += gateway_net_amt(academic_session.matric_gown_fee) + \
283                    gateway_net_amt(academic_session.lapel_fee)
284        elif category == 'late_registration':
285            if student.is_postgrad:
286                amount = academic_session.late_pg_registration_fee
287            else:
288                amount = academic_session.late_registration_fee
289        elif category.startswith('schoolfee'):
290            try:
291                certificate = student['studycourse'].certificate
292                p_item = certificate.code
293            except (AttributeError, TypeError):
294                return _('Study course data are incomplete.'), None
295            if student.is_postgrad and category != 'schoolfee':
296                return _("No additional fees required."), None
297            if student.current_mode in (
298                'ug_ft', 'ug_pt', 'de_ft', 'de_pt',
299                'transfer', 'mug_ft', 'mde_ft') \
300                and not category in (
301                'schoolfee_incl', 'schoolfee_1', 'schoolfee_2'):
302                    return _("You must choose a payment which includes "
303                             "additional fees."), None
304            if category in ('schoolfee_1', 'schoolfee_2'):
305                if student.current_mode == 'ug_pt':
306                    return _("Part-time students are not allowed "
307                             "to pay by instalments."), None
308                if student.entry_session < 2015:
309                    return _("You are not allowed "
310                             "to pay by instalments."), None
311            if student.state == CLEARED and category != 'schoolfee_2':
312                amount = getattr(certificate, 'school_fee_1', 0.0)
313                # Cut school fee by 50%
314                if category == 'schoolfee_1' and amount:
315                    amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
316            elif student.is_fresh and category == 'schoolfee_2':
317                amount = getattr(certificate, 'school_fee_1', 0.0)
318                # Cut school fee by 50%
319                if amount:
320                    amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
321            elif student.state == RETURNING and category != 'schoolfee_2':
322                if not student.father_name:
323                    return _("Personal data form is not properly filled."), None
324                # In case of returning school fee payment the payment session
325                # and level contain the values of the session the student
326                # has paid for.
327                p_session, p_level = self.getReturningData(student)
328                try:
329                    academic_session = grok.getSite()[
330                        'configuration'][str(p_session)]
331                except KeyError:
332                    return _(u'Session configuration object is not available.'), None
333                if student.entry_session >= 2015:
334                    amount = getattr(certificate, 'school_fee_2', 0.0)
335                    # Cut school fee by 50%
336                    if category == 'schoolfee_1' and amount:
337                        amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
338                else:
339                    amount = getattr(certificate, 'school_fee_3', 0.0)
340            elif category == 'schoolfee_2':
341                amount = getattr(certificate, 'school_fee_2', 0.0)
342                # Cut school fee by 50%
343                if amount:
344                    amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
345            else:
346                return _('Wrong state.'), None
347            if amount in (0.0, None):
348                return _(u'Amount could not be determined.'), None
349            # Add Student Union Fee , Student Id Card Fee and Welfare Assurance
350            if category in ('schoolfee_incl', 'schoolfee_1'):
351                amount += gateway_net_amt(academic_session.welfare_fee) + \
352                    gateway_net_amt(academic_session.union_fee)
353                if student.entry_session == 2016 \
354                    and student.entry_mode == 'ug_ft' \
355                    and student.state == CLEARED:
356                    amount += gateway_net_amt(academic_session.id_card_fee)
357            # Add non-indigenous fee and session specific penalty fees
358            if student.is_postgrad:
359                amount += academic_session.penalty_pg
360                if student.lga and not student.lga.startswith('edo'):
361                    amount += 20000.0
362            else:
363                amount += academic_session.penalty_ug
364        elif not student.is_postgrad:
365            fee_name = category + '_fee'
366            amount = getattr(academic_session, fee_name, 0.0)
367        if amount in (0.0, None):
368            return _(u'Amount could not be determined.'), None
369        # Create ticket.
370        for key in student['payments'].keys():
371            ticket = student['payments'][key]
372            if ticket.p_state == 'paid' and\
373               ticket.p_category == category and \
374               ticket.p_item == p_item and \
375               ticket.p_session == p_session:
376                  return _('This type of payment has already been made.'), None
377            # Additional condition in AAUE
378            if category in ('schoolfee', 'schoolfee_incl', 'schoolfee_1'):
379                if ticket.p_state == 'paid' and \
380                   ticket.p_category in ('schoolfee',
381                                         'schoolfee_incl',
382                                         'schoolfee_1') and \
383                   ticket.p_item == p_item and \
384                   ticket.p_session == p_session:
385                      return _(
386                          'Another school fee payment for this '
387                          'session has already been made.'), None
388
389        if self._isPaymentDisabled(p_session, category, student):
390            return _('This category of payments has been disabled.'), None
391        payment = createObject(u'waeup.StudentOnlinePayment')
392        timestamp = ("%d" % int(time()*10000))[1:]
393        payment.p_id = "p%s" % timestamp
394        payment.p_category = category
395        payment.p_item = p_item
396        payment.p_session = p_session
397        payment.p_level = p_level
398        payment.p_current = p_current
399        payment.amount_auth = amount
400        return None, payment
401
402    def _admissionText(self, student, portal_language):
403        inst_name = grok.getSite()['configuration'].name
404        entry_session = student['studycourse'].entry_session
405        entry_session = academic_sessions_vocab.getTerm(entry_session).title
406        text = trans(_(
407            'This is to inform you that you have been offered provisional'
408            ' admission into ${a} for the ${b} academic session as follows:',
409            mapping = {'a': inst_name, 'b': entry_session}),
410            portal_language)
411        return text
412
413    def maxCredits(self, studylevel):
414        """Return maximum credits.
415
416        """
417        return 48
418
419    def getBedCoordinates(self, bedticket):
420        """Return descriptive bed coordinates.
421        This method can be used to customize the `display_coordinates`
422        property method in order to  display a
423        customary description of the bed space.
424        """
425        bc = bedticket.bed_coordinates.split(',')
426        if len(bc) == 4:
427            return bc[0]
428        return bedticket.bed_coordinates
429
430    def getAccommodationDetails(self, student):
431        """Determine the accommodation data of a student.
432        """
433        d = {}
434        d['error'] = u''
435        hostels = grok.getSite()['hostels']
436        d['booking_session'] = hostels.accommodation_session
437        d['allowed_states'] = hostels.accommodation_states
438        d['startdate'] = hostels.startdate
439        d['enddate'] = hostels.enddate
440        d['expired'] = hostels.expired
441        # Determine bed type
442        bt = 'all'
443        if student.sex == 'f':
444            sex = 'female'
445        else:
446            sex = 'male'
447        special_handling = 'regular'
448        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
449        return d
450
451    def checkAccommodationRequirements(self, student, acc_details):
452        msg = super(CustomStudentsUtils, self).checkAccommodationRequirements(
453            student, acc_details)
454        if msg:
455            return msg
456        if student.current_mode not in ('ug_ft', 'de_ft', 'mug_ft', 'mde_ft'):
457            return _('You are not eligible to book accommodation.')
458        return
459
460    # AAUE prefix
461    STUDENT_ID_PREFIX = u'E'
Note: See TracBrowser for help on using the repository browser.