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

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

Implement matric number constructor for diploma students.

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