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

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

Change category title.

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