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

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

No probation levels in AAUE.

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