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

Last change on this file since 13847 was 13846, checked in by Henrik Bettermann, 9 years ago

IOE doesn't have a department.

  • Property svn:keywords set to Id
File size: 16.7 KB
Line 
1## $Id: utils.py 13846 2016-04-21 14:17:19Z 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','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':
184            amount = academic_session.welfare_fee
185        elif category == 'union':
186            amount = academic_session.union_fee
187        elif category == 'lapel':
188            amount = academic_session.lapel_fee
189        elif category == 'matric_gown':
190            amount = academic_session.matric_gown_fee
191        elif category == 'concessional':
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.faccode == 'FP':
200                amount = academic_session.clearance_fee_fp
201            elif student.current_mode.endswith('_pt'):
202                if student.is_postgrad:
203                    amount = academic_session.clearance_fee_pg_pt
204                else:
205                    amount = academic_session.clearance_fee_ug_pt
206            elif student.faccode == 'FCS':
207                # Students in clinical medical sciences pay the medical
208                # acceptance fee
209                amount = academic_session.clearance_fee_med
210            elif student.is_postgrad:  # and not part-time
211                amount = academic_session.clearance_fee_pg
212            else:
213                amount = academic_session.clearance_fee
214            p_item = student['studycourse'].certificate.code
215            if amount in (0.0, None):
216                return _(u'Amount could not be determined.'), None
217            # Add Matric Gown Fee and Lapel Fee
218            if category == 'clearance_incl':
219                amount += gateway_net_amt(academic_session.matric_gown_fee) + \
220                    gateway_net_amt(academic_session.lapel_fee)
221        elif category == 'late_registration':
222            amount = academic_session.late_registration_fee
223        elif category.startswith('schoolfee'):
224            try:
225                certificate = student['studycourse'].certificate
226                p_item = certificate.code
227            except (AttributeError, TypeError):
228                return _('Study course data are incomplete.'), None
229            if category in ('schoolfee_1', 'schoolfee_2'):
230                if student.current_mode == 'ug_pt':
231                    return _("Part-time students are not allowed "
232                             "to pay by instalments."), None
233                if category == 'schoolfee_1' and student.state != CLEARED:
234                    return _("Wrong state. Only students in state 'cleared' "
235                             "are allowed to pay by instalments."), None
236            if student.state == CLEARED or category == 'schoolfee_2':
237                if student.is_foreigner:
238                    amount = getattr(certificate, 'school_fee_3', 0.0)
239                else:
240                    amount = getattr(certificate, 'school_fee_1', 0.0)
241                # Cut school fee by 50%
242                if category in ('schoolfee_1', 'schoolfee_2'):
243                    if amount:
244                        amount = gateway_net_amt(amount) / 2 + GATEWAY_AMT
245            elif student.state == RETURNING:
246                if student.is_postgrad and category == 'schoolfee_incl':
247                    return _("No additional fees required."), None
248                if not student.father_name:
249                    return _("Personal data form is not properly filled."), None
250                # In case of returning school fee payment the payment session
251                # and level contain the values of the session the student
252                # has paid for.
253                p_session, p_level = self.getReturningData(student)
254                try:
255                    academic_session = grok.getSite()[
256                        'configuration'][str(p_session)]
257                except KeyError:
258                    return _(u'Session configuration object is not available.'), None
259                if student.is_foreigner:
260                    amount = getattr(certificate, 'school_fee_4', 0.0)
261                else:
262                    amount = getattr(certificate, 'school_fee_2', 0.0)
263            else:
264                return _('Wrong state.'), None
265            if amount in (0.0, None):
266                return _(u'Amount could not be determined.'), None
267            # Add Student Union Fee and Welfare Assurance
268            if category in ('schoolfee_incl', 'schoolfee_1'):
269                amount += gateway_net_amt(academic_session.welfare_fee) + \
270                    gateway_net_amt(academic_session.union_fee)
271            # Add non-indigenous fee and session specific penalty fees
272            if student.is_postgrad:
273                amount += academic_session.penalty_pg
274                if not student.lga.startswith('edo'):
275                    amount += 20000.0
276            else:
277                amount += academic_session.penalty_ug
278        if amount in (0.0, None):
279            return _(u'Amount could not be determined.'), None
280        # Create ticket.
281        for key in student['payments'].keys():
282            ticket = student['payments'][key]
283            if ticket.p_state == 'paid' and\
284               ticket.p_category == category and \
285               ticket.p_item == p_item and \
286               ticket.p_session == p_session:
287                  return _('This type of payment has already been made.'), None
288            # Additional condition in AAUE
289            if category in ('schoolfee', 'schoolfee_incl', 'schoolfee_1'):
290                if ticket.p_state == 'paid' and \
291                   ticket.p_category in ('schoolfee',
292                                         'schoolfee_incl',
293                                         'schoolfee_1') and \
294                   ticket.p_item == p_item and \
295                   ticket.p_session == p_session:
296                      return _(
297                          'Another school fee payment for this '
298                          'session has already been made.'), None
299
300        if self._isPaymentDisabled(p_session, category, student):
301            return _('This category of payments has been disabled.'), None
302        payment = createObject(u'waeup.StudentOnlinePayment')
303        timestamp = ("%d" % int(time()*10000))[1:]
304        payment.p_id = "p%s" % timestamp
305        payment.p_category = category
306        payment.p_item = p_item
307        payment.p_session = p_session
308        payment.p_level = p_level
309        payment.p_current = p_current
310        payment.amount_auth = amount
311        return None, payment
312
313    def _admissionText(self, student, portal_language):
314        inst_name = grok.getSite()['configuration'].name
315        entry_session = student['studycourse'].entry_session
316        entry_session = academic_sessions_vocab.getTerm(entry_session).title
317        text = trans(_(
318            'This is to inform you that you have been offered provisional'
319            ' admission into ${a} for the ${b} academic session as follows:',
320            mapping = {'a': inst_name, 'b': entry_session}),
321            portal_language)
322        return text
323
324    def maxCredits(self, studylevel):
325        """Return maximum credits.
326
327        """
328        return 48
329
330    def getBedCoordinates(self, bedticket):
331        """Return descriptive bed coordinates.
332        This method can be used to customize the `display_coordinates`
333        property method in order to  display a
334        customary description of the bed space.
335        """
336        bc = bedticket.bed_coordinates.split(',')
337        if len(bc) == 4:
338            return bc[0]
339        return bedticket.bed_coordinates
340
341    def getAccommodationDetails(self, student):
342        """Determine the accommodation data of a student.
343        """
344        d = {}
345        d['error'] = u''
346        hostels = grok.getSite()['hostels']
347        d['booking_session'] = hostels.accommodation_session
348        d['allowed_states'] = hostels.accommodation_states
349        d['startdate'] = hostels.startdate
350        d['enddate'] = hostels.enddate
351        d['expired'] = hostels.expired
352        # Determine bed type
353        bt = 'all'
354        if student.sex == 'f':
355            sex = 'female'
356        else:
357            sex = 'male'
358        special_handling = 'regular'
359        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
360        return d
361
362    def checkAccommodationRequirements(self, student, acc_details):
363        super(CustomStudentsUtils, self).checkAccommodationRequirements(
364            student, acc_details)
365        if student.current_mode not in ('ug_ft', 'de_ft', 'mug_ft', 'mde_ft'):
366            return _('You are not eligible to book accommodation.')
367        return
368
369    # AAUE prefix
370    STUDENT_ID_PREFIX = u'E'
Note: See TracBrowser for help on using the repository browser.