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

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

Extend getDegreeClassNumber.

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