source: main/kofacustom.edopoly/trunk/src/kofacustom/edopoly/students/utils.py @ 17957

Last change on this file since 17957 was 17943, checked in by Henrik Bettermann, 5 weeks ago

Show different message when students are in state admitted.

  • Property svn:keywords set to Id
File size: 14.4 KB
Line 
1## $Id: utils.py 17943 2024-10-16 12:21:57Z 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##
18from time import time
19import grok
20from zope.component import createObject, getUtility
21from waeup.kofa.interfaces import (IKofaUtils,
22    ADMITTED, CLEARED, RETURNING, PAID, REGISTERED, VALIDATED)
23from kofacustom.nigeria.students.utils import NigeriaStudentsUtils
24from kofacustom.edopoly.interfaces import MessageFactory as _
25
26class CustomStudentsUtils(NigeriaStudentsUtils):
27    """A collection of customized methods.
28
29    """
30
31    # prefix
32    STUDENT_ID_PREFIX = u'U'
33
34    def warnCreditsOOR(self, studylevel, course=None):
35        """No maximum credits.
36        """
37        return
38
39    def GPABoundaries(self, faccode=None, depcode=None,
40                            certcode=None, student=None):
41        if student and student.current_mode.startswith('dp'):
42                return ((1, 'IRNS / NER / NYV'),
43                        (2, 'Pass'),
44                        (2.5, 'Lower Credit'),
45                        (3, 'Upper Credit'),
46                        (3.5, 'Distinction'))
47        elif student:
48            return ((1, 'FRNS / NER / NYV'),
49                    (1.5, 'Pass'),
50                    (2.4, '3rd Class Honours'),
51                    (3.5, '2nd Class Honours Lower Division'),
52                    (4.5, '2nd Class Honours Upper Division'),
53                    (5, '1st Class Honours'))
54        # Session Results Presentations depend on certificate
55        results = None
56        if certcode:
57            cat = queryUtility(ICatalog, name='certificates_catalog')
58            results = list(
59                cat.searchResults(code=(certcode, certcode)))
60        if results and results[0].study_mode.startswith('dp'):
61            return ((1, 'IRNS / NER / NYV'),
62                    (2, 'Pass'),
63                    (2.5, 'Lower Credit'),
64                    (3, 'Upper Credit'),
65                    (3.5, 'Distinction'))
66        else:
67            return ((1, 'FRNS / NER / NYV'),
68                    (1.5, 'Pass'),
69                    (2.4, '3rd Class Honours'),
70                    (3.5, '2nd Class Honours Lower Division'),
71                    (4.5, '2nd Class Honours Upper Division'),
72                    (5, '1st Class Honours'))
73
74    def getClassFromCGPA(self, gpa, student):
75        gpa_boundaries = self.GPABoundaries(student=student)
76        if gpa < gpa_boundaries[0][0]:
77            return 0, gpa_boundaries[0][1]
78        if gpa < gpa_boundaries[1][0]:
79            return 1, gpa_boundaries[1][1]
80        if gpa < gpa_boundaries[2][0]:
81            return 2, gpa_boundaries[2][1]
82        if gpa < gpa_boundaries[3][0]:
83            return 3, gpa_boundaries[3][1]
84        if gpa < gpa_boundaries[4][0]:
85            return 4, gpa_boundaries[4][1]
86        if gpa <= gpa_boundaries[5][0]:
87            return 5, gpa_boundaries[5][1]
88        return 'N/A'
89
90    def _requiredPaymentsMade(self, student, session):
91        # SIWESS fee requirement removed on 18/01/2019
92        req_payments = ('ict_entre', 'logbook_combo','union')
93        req_payments_titles = 'ICT, Student Union and Logbook'
94        # All ND and HND part time do not pay for logbook and union
95        if student.current_mode.endswith('_pt'):
96            req_payments = ('ict_entre',)
97            req_payments_titles = 'ICT'
98        # HND1 and HND2 full time do not pay for LOGBOOK
99        elif student.current_mode == 'hnd_ft' and student.state in (
100            CLEARED, RETURNING):
101            req_payments = ('union', 'ict_entre')
102            req_payments_titles = 'Student Union and ICT'
103        # ND2 FULL TIME do not pay LOGBOOK
104        elif student.current_mode == 'nd_ft' and student.state == RETURNING:
105            req_payments = ('ict_entre', 'union')
106            req_payments_titles = 'Student Union and ICT'
107        num = 0
108        if len(student['payments']):
109            for ticket in student['payments'].values():
110                if ticket.p_state == 'paid' and \
111                    ticket.p_category in req_payments and \
112                    ticket.p_session == session:
113                    num += 1
114            if num == len(req_payments):
115                return True, None
116        return False, req_payments_titles
117
118    def samePaymentMade(self, student, category, p_item, p_session):
119        if category in ('transcript',):
120            return False
121        for key in student['payments'].keys():
122            ticket = student['payments'][key]
123            if ticket.p_state == 'paid' and\
124               ticket.p_category == category and \
125               ticket.p_item == p_item and \
126               ticket.p_session == p_session:
127                  return True
128        return False
129
130    def setPaymentDetails(self, category, student,
131            previous_session=None, previous_level=None, combi=[]):
132        """Create a payment ticket and set the payment data of a
133        student for the payment category specified.
134        """
135        p_item = u''
136        amount = 0.0
137        if previous_session:
138            if previous_session < student['studycourse'].entry_session:
139                return _('The previous session must not fall below '
140                         'your entry session.'), None
141            if category == 'schoolfee':
142                # School fee is always paid for the following session
143                if previous_session > student['studycourse'].current_session:
144                    return _('This is not a previous session.'), None
145            else:
146                if previous_session > student['studycourse'].current_session - 1:
147                    return _('This is not a previous session.'), None
148            p_session = previous_session
149            p_level = previous_level
150            p_current = False
151        else:
152            p_session = student['studycourse'].current_session
153            p_level = student['studycourse'].current_level
154            p_current = True
155        # The following fout fees are part of the school fee which must be
156        # paid before tuition fee. They are paid for next session.
157        if category in ('ict_entre', 'logbook_combo', 'siwess_combo', 'union') and \
158            student.state == RETURNING and not previous_session:
159            p_session, p_level = self.getReturningData(student)
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 == 'schoolfee':
165            try:
166                certificate = student['studycourse'].certificate
167                p_item = certificate.code
168            except (AttributeError, TypeError):
169                return _('Study course data are incomplete.'), None
170            if previous_session:
171                amount = getattr(certificate, 'school_fee_1', 0.0)
172            else:
173                if student.state == CLEARED:
174                    amount = getattr(certificate, 'school_fee_1', 0.0)
175                elif student.state == RETURNING:
176                    amount = getattr(certificate, 'school_fee_1', 0.0)
177                    # In case of returning school fee payment the
178                    # payment session and level contain the values of
179                    # the session the student has paid for. Payment
180                    # session is always next session.
181                    p_session, p_level = self.getReturningData(student)
182                    academic_session = self._getSessionConfiguration(p_session)
183                    if academic_session == None:
184                        return _(
185                            u'Session configuration object is not available.'
186                            ), None
187                elif student.is_postgrad and student.state == PAID:
188                    # Returning postgraduate students also pay for the
189                    # next session but their level always remains the
190                    # same.
191                    amount = getattr(certificate, 'school_fee_1', 0.0)
192                    p_session += 1
193                    academic_session = self._getSessionConfiguration(p_session)
194                    if academic_session == None:
195                        return _(
196                            u'Session configuration object is not available.'
197                            ), None
198                elif student.state == ADMITTED:
199                        return _(
200                        u'Please proceed for clearance before paying school fees.'
201                        ), None
202            rpm, rpt = self._requiredPaymentsMade(student, p_session)
203            if not rpm:
204                return 'Pay %s fee(s) first.' % rpt, None
205        elif category == 'clearance':
206            try:
207                p_item = student['studycourse'].certificate.code
208            except (AttributeError, TypeError):
209                return _('Study course data are incomplete.'), None
210            amount = academic_session.clearance_fee
211        elif category == 'bed_allocation':
212            acco_details = self.getAccommodationDetails(student)
213            p_session = acco_details['booking_session']
214            p_item = acco_details['bt']
215            amount = academic_session.booking_fee
216        elif category == 'hostel_maintenance':
217            amount = 0.0
218            booking_session = grok.getSite()['hostels'].accommodation_session
219            bedticket = student['accommodation'].get(str(booking_session), None)
220            if bedticket is not None and bedticket.bed is not None:
221                p_session = booking_session
222                p_item = bedticket.bed_coordinates
223                if bedticket.bed.__parent__.maint_fee > 0:
224                    amount = bedticket.bed.__parent__.maint_fee
225                else:
226                    # fallback
227                    amount = academic_session.maint_fee
228            else:
229                return _(u'No bed allocated.'), None
230        else:
231            fee_name = category + '_fee'
232            amount = getattr(academic_session, fee_name, 0.0)
233        if amount in (0.0, None):
234            return _('Amount could not be determined.'), None
235        if self.samePaymentMade(student, category, p_item, p_session):
236            return _('This type of payment has already been made.'), None
237        if self._isPaymentDisabled(p_session, category, student):
238            return _('This category of payments has been disabled.'), None
239        payment = createObject(u'waeup.StudentOnlinePayment')
240        timestamp = ("%d" % int(time()*10000))[1:]
241        payment.p_id = "p%s" % timestamp
242        payment.p_category = category
243        payment.p_item = p_item
244        payment.p_session = p_session
245        payment.p_level = p_level
246        payment.p_current = p_current
247        payment.amount_auth = amount
248        return None, payment
249
250    def constructMatricNumber(self, student):
251        faccode = student.faccode
252        #depcode = student.depcode
253        #certcode = student.certcode
254        clearance_paid = False
255        if len(student['payments']):
256            for ticket in student['payments'].values():
257                if ticket.p_state == 'paid' and \
258                    ticket.p_category == 'clearance' and \
259                    ticket.p_session == student.entry_session:
260                        clearance_paid = True
261                        break
262        if not clearance_paid or not student.is_fresh:
263            return _('Matriculation number cannot be set.'), None
264        year = unicode(student.entry_session)[2:]
265        # SASND1809001
266        if student.current_mode == 'nd_ft':
267            next_integer = grok.getSite()['configuration'].next_matric_integer
268            if next_integer == 0:
269                return _('Matriculation number cannot be set.'), None
270            return None, "%s/ND/%s/%05d" % (faccode, year, next_integer)
271
272        # SASNH1809001
273        if student.current_mode == 'hnd_ft':
274            next_integer = grok.getSite()['configuration'].next_matric_integer_2
275            if next_integer == 0:
276                return _('Matriculation number cannot be set.'), None
277            return None, "%s/HD/%s/%05d" % (faccode, year, next_integer)
278
279        # SASPT1809001
280        if student.current_mode in ('nd_pt', 'hnd_pt'):
281            next_integer = grok.getSite()['configuration'].next_matric_integer_3
282            if next_integer == 0:
283                return _('Matriculation number cannot be set.'), None
284            return None, "%s/PT/%s/%05d" % (faccode, year, next_integer)
285
286        return _('Matriculation number cannot be set.'), None
287
288
289    def increaseMatricInteger(self, student):
290        """Increase counter for matric numbers.
291        """
292        if student.current_mode == 'nd_ft':
293            grok.getSite()['configuration'].next_matric_integer += 1
294            return
295        elif student.current_mode == 'hnd_ft':
296            grok.getSite()['configuration'].next_matric_integer_2 += 1
297            return
298        elif student.current_mode in ('nd_pt', 'hnd_pt'):
299            grok.getSite()['configuration'].next_matric_integer_3 += 1
300            return
301        return
302
303    def getAccommodationDetails(self, student):
304        """Determine the accommodation data of a student.
305        """
306        d = {}
307        d['error'] = u''
308        hostels = grok.getSite()['hostels']
309        d['booking_session'] = hostels.accommodation_session
310        d['allowed_states'] = hostels.accommodation_states
311        d['startdate'] = hostels.startdate
312        d['enddate'] = hostels.enddate
313        d['expired'] = hostels.expired
314        # Determine bed type
315        studycourse = student['studycourse']
316        certificate = getattr(studycourse,'certificate',None)
317        entry_session = studycourse.entry_session
318        current_level = studycourse.current_level
319        if None in (entry_session, current_level, certificate):
320            return d
321        bt = 'all'
322        if student.sex == 'f':
323            sex = 'female'
324        else:
325            sex = 'male'
326        special_handling = 'regular'
327        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
328        return d
Note: See TracBrowser for help on using the repository browser.