source: main/waeup.uniben/trunk/src/waeup/uniben/students/utils.py @ 17607

Last change on this file since 17607 was 17606, checked in by Henrik Bettermann, 15 months ago

Define setCDLPaymentDetails.

  • Property svn:keywords set to Id
File size: 36.8 KB
Line 
1## $Id: utils.py 17606 2023-10-10 16:52:33Z 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
19import os
20import csv
21from time import time
22from reportlab.platypus import Paragraph, Table
23from zope.component import createObject, getUtility
24from reportlab.lib.styles import getSampleStyleSheet
25from waeup.kofa.browser.pdf import ENTRY1_STYLE
26from waeup.kofa.interfaces import (IKofaUtils, ADMITTED, CLEARANCE,
27    CLEARED, REQUESTED, RETURNING, PAID, REGISTERED, VALIDATED, GRADUATED)
28from waeup.kofa.utils.helpers import to_timezone
29from waeup.kofa.students.utils import (
30    trans, render_student_data, formatted_text, render_transcript_data,
31    SLIP_STYLE)
32from kofacustom.nigeria.students.utils import NigeriaStudentsUtils
33from waeup.uniben.interfaces import MessageFactory as _
34
35SCHOOLFEES = dict()
36
37schoolfees_path = os.path.join(
38    os.path.dirname(__file__), 'schoolfees_22.csv')
39reader = csv.DictReader(open(schoolfees_path, 'rb'))
40SCHOOLFEES[22] = {line['code']: {item[0]:item[1] for item in line.items()}
41    for line in reader}
42
43
44schoolfees_path = os.path.join(
45    os.path.dirname(__file__), 'schoolfees_20.csv')
46reader = csv.DictReader(open(schoolfees_path, 'rb'))
47SCHOOLFEES[20] = {line['code']: {item[0]:item[1] for item in line.items()}
48    for line in reader}
49
50
51schoolfees_path = os.path.join(
52    os.path.dirname(__file__), 'schoolfees_17.csv')
53reader = csv.DictReader(open(schoolfees_path, 'rb'))
54SCHOOLFEES[17] = {line['code']: {item[0]:item[1] for item in line.items()}
55    for line in reader}
56
57schoolfees_path = os.path.join(
58    os.path.dirname(__file__), 'schoolfees_12.csv')
59reader = csv.DictReader(open(schoolfees_path, 'rb'))
60SCHOOLFEES[12] = {line['code']: {item[0]:item[1] for item in line.items()}
61    for line in reader}
62
63class CustomStudentsUtils(NigeriaStudentsUtils):
64    """A collection of customized methods.
65
66    """
67
68    SEPARATORS_DICT = {
69        'form.fst_sit_fname': _(u'First Sitting Record'),
70        'form.scd_sit_fname': _(u'Second Sitting Record'),
71        'form.alr_fname': _(u'Advanced Level Record'),
72        'form.hq_type': _(u'Higher Education Record'),
73        'form.hq2_type': _(u'Second Higher Education Record'),
74        'form.nysc_year': _(u'NYSC Information'),
75        'form.employer': _(u'Employment History'),
76        'form.former_matric': _(u'Former Student'),
77        'form.fever': _(u'History of Symptoms'),
78        'form.asthma': _(u'Medical History'),
79        'form.lagos_abuja': _(u'Travel History'),
80        'form.company_suspected': _(u'History of Contact/Infection'),
81        'form.genotype': _(u'TISHIP Registration Form Data'),
82        }
83
84    def getReturningData(self, student):
85        """ This method defines what happens after school fee payment
86        of returning students depending on the student's senate verdict.
87        """
88        prev_level = student['studycourse'].current_level
89        cur_verdict = student['studycourse'].current_verdict
90        if cur_verdict == 'N' and prev_level in (100, 200):
91            new_level = prev_level
92        elif cur_verdict in ('A','B','M','N','Z',):
93            # Successful student
94            new_level = divmod(int(prev_level),100)[0]*100 + 100
95        elif cur_verdict in ('C', 'L',):
96            # Student on probation
97            new_level = int(prev_level) + 10
98        else:
99            # Student is somehow in an undefined state.
100            # Level has to be set manually.
101            new_level = prev_level
102        new_session = student['studycourse'].current_session + 1
103        return new_session, new_level
104
105
106    def checkAccommodationRequirements(self, student, acc_details):
107        if acc_details.get('expired', False):
108            startdate = acc_details.get('startdate')
109            enddate = acc_details.get('enddate')
110            if startdate and enddate:
111                tz = getUtility(IKofaUtils).tzinfo
112                startdate = to_timezone(
113                    startdate, tz).strftime("%d/%m/%Y %H:%M:%S")
114                enddate = to_timezone(
115                    enddate, tz).strftime("%d/%m/%Y %H:%M:%S")
116                return _("Outside booking period: ${a} - ${b}",
117                         mapping = {'a': startdate, 'b': enddate})
118            else:
119                return _("Outside booking period.")
120        if not student.is_postgrad and student.current_mode != 'ug_ft':
121            return _("Only undergraduate full-time students are eligible to book accommodation.")
122        bt = acc_details.get('bt')
123        if not bt:
124            return _("Your data are incomplete.")
125        if not student.state in acc_details['allowed_states']:
126            return _("You are in the wrong registration state.")
127        #if student['studycourse'].current_session != acc_details[
128        #    'booking_session']:
129        #    return _('Your current session does not '
130        #             'match accommodation session.')
131        if  acc_details['booking_session'] - student[
132            'studycourse'].current_session > self.ACCOMMODATION_SPAN:
133            return _('Your current session does not allow ' + \
134                    'to book accommodation.')
135        stage = bt.split('_')[2]
136        if not student.is_postgrad and stage != 'fr' and not student[
137            'studycourse'].previous_verdict in (
138                'A', 'B', 'F', 'J', 'L', 'M', 'C', 'Z'):
139            return _("Your are not eligible to book accommodation.")
140        bsession = str(acc_details['booking_session'])
141        if bsession in student['accommodation'].keys() \
142            and not 'booking expired' in \
143            student['accommodation'][bsession].bed_coordinates:
144            return _('You already booked a bed space in '
145                     'current accommodation session.')
146        return
147
148    def getAccommodationDetails(self, student):
149        """Determine the accommodation data of a student.
150        """
151        d = {}
152        d['error'] = u''
153        hostels = grok.getSite()['hostels']
154        d['booking_session'] = hostels.accommodation_session
155        d['allowed_states'] = hostels.accommodation_states
156        d['startdate'] = hostels.startdate
157        d['enddate'] = hostels.enddate
158        d['expired'] = hostels.expired
159        # Determine bed type
160        studycourse = student['studycourse']
161        certificate = getattr(studycourse,'certificate',None)
162        entry_session = studycourse.entry_session
163        current_level = studycourse.current_level
164        if None in (entry_session, current_level, certificate):
165            return d
166        if student.sex == 'f':
167            sex = 'female'
168        else:
169            sex = 'male'
170        if student.is_postgrad:
171            bt = 'all'
172            special_handling = 'pg'
173        else:
174            end_level = certificate.end_level
175            if current_level == 10:
176                bt = 'pr'
177            elif entry_session == grok.getSite()['hostels'].accommodation_session:
178                bt = 'fr'
179            elif current_level >= end_level:
180                bt = 'fi'
181            else:
182                bt = 're'
183            special_handling = 'regular'
184            desired_hostel = student['accommodation'].desired_hostel
185            if student.faccode in ('MED', 'DEN') and (
186                not desired_hostel or desired_hostel.startswith('clinical')):
187                special_handling = 'clinical'
188            elif student.certcode in ('BARTMAS', 'BARTTHR', 'BARTFAA',
189                                      'BAEDFAA', 'BSCEDECHED', 'BAFAA',
190                                      'BARTMUS', 'BSCEDECHED', 'BEDECHEDU'):
191                special_handling = 'ekenwan'
192        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
193        return d
194
195    def _paymentMade(self, student, session):
196        if len(student['payments']):
197            for ticket in student['payments'].values():
198                if ticket.p_state == 'paid' and \
199                    ticket.p_category == 'schoolfee' and \
200                    ticket.p_session == session:
201                    return True
202        return False
203
204    def _isPaymentDisabled(self, p_session, category, student):
205        academic_session = self._getSessionConfiguration(p_session)
206        if category == 'schoolfee':
207            if 'sf_all' in academic_session.payment_disabled:
208                return True
209            if student.state == RETURNING and \
210                'sf_return' in academic_session.payment_disabled:
211                return True
212            if student.current_mode == 'found' and \
213                'sf_found' in academic_session.payment_disabled:
214                return True
215            if student.is_postgrad:
216                if 'sf_pg' in academic_session.payment_disabled:
217                    return True
218                return False
219            if student.current_mode.endswith('ft') and \
220                'sf_ft' in academic_session.payment_disabled:
221                return True
222            if student.current_mode.endswith('pt') and \
223                'sf_pt' in academic_session.payment_disabled:
224                return True
225            if student.current_mode.startswith('dp') and \
226                'sf_dp' in academic_session.payment_disabled:
227                return True
228            if student.current_mode.endswith('sw') and \
229                'sf_sw' in academic_session.payment_disabled:
230                return True
231        if category == 'hostel_maintenance' and \
232            'maint_all' in academic_session.payment_disabled:
233            return True
234        if category == 'clearance':
235            if 'cl_all' in academic_session.payment_disabled:
236                return True
237            if student.is_jupeb and \
238                'cl_jupeb' in academic_session.payment_disabled:
239                return True
240            if not student.is_jupeb and \
241                'cl_allexj' in academic_session.payment_disabled:
242                return True
243        return False
244
245    #def _hostelApplicationPaymentMade(self, student, session):
246    #    if len(student['payments']):
247    #        for ticket in student['payments'].values():
248    #            if ticket.p_state == 'paid' and \
249    #                ticket.p_category == 'hostel_application' and \
250    #                ticket.p_session == session:
251    #                return True
252    #    return False
253
254    def _pharmdInstallments(self, student):
255        installments = 0.0
256        if len(student['payments']):
257            for ticket in student['payments'].values():
258                if ticket.p_state == 'paid' and \
259                    ticket.p_category.startswith('pharmd') and \
260                    ticket.p_session == student.current_session:
261                    installments += ticket.amount_auth
262        return installments
263
264    def samePaymentMade(self, student, category, p_item, p_session):
265        if category in ('bed_allocation', 'transcript'):
266            return False
267        for key in student['payments'].keys():
268            ticket = student['payments'][key]
269            if ticket.p_state == 'paid' and\
270               ticket.p_category == category and \
271               ticket.p_item == p_item and \
272               ticket.p_session == p_session:
273                  return True
274        return False
275
276    def setCDLPaymentDetails(self, category, student,
277            previous_session, previous_level, combi):
278        """Create Payment object and set the payment data of a CDL student for
279        the payment category specified.
280        """
281        p_item = u''
282        amount = 0.0
283        if previous_session:
284            if previous_session < student['studycourse'].entry_session:
285                return _('The previous session must not fall below '
286                         'your entry session.'), None
287            if category == 'schoolfee':
288                # School fee is always paid for the following session
289                if previous_session > student['studycourse'].current_session:
290                    return _('This is not a previous session.'), None
291            else:
292                if previous_session > student['studycourse'].current_session - 1:
293                    return _('This is not a previous session.'), None
294            p_session = previous_session
295            p_level = previous_level
296            p_current = False
297        else:
298            p_session = student['studycourse'].current_session
299            p_level = student['studycourse'].current_level
300            p_current = True
301        academic_session = self._getSessionConfiguration(p_session)
302        if academic_session == None:
303            return _(u'Session configuration object is not available.'), None
304        # Determine fee.
305        if category == 'schoolfee':
306            try:
307                certificate = student['studycourse'].certificate
308                p_item = certificate.code
309            except (AttributeError, TypeError):
310                return _('Study course data are incomplete.'), None
311            if previous_session:
312                # Students can pay for previous sessions in all
313                # workflow states.  Fresh students are excluded by the
314                # update method of the PreviousPaymentAddFormPage.
315                if previous_level == 100:
316                    amount = getattr(certificate, 'school_fee_1', 0.0)
317                else:
318                    amount = getattr(certificate, 'school_fee_2', 0.0)
319            else:
320                if student.state == CLEARED:
321                    amount = getattr(certificate, 'school_fee_1', 0.0)
322                elif student.state == RETURNING:
323                    # In case of returning school fee payment the
324                    # payment session and level contain the values of
325                    # the session the student has paid for. Payment
326                    # session is always next session.
327                    p_session, p_level = self.getReturningData(student)
328                    academic_session = self._getSessionConfiguration(p_session)
329                    if academic_session == None:
330                        return _(
331                            u'Session configuration object is not available.'
332                            ), None
333                    amount = getattr(certificate, 'school_fee_2', 0.0)
334                elif student.is_postgrad and student.state == PAID:
335                    # Returning postgraduate students also pay for the
336                    # next session but their level always remains the
337                    # same.
338                    p_session += 1
339                    academic_session = self._getSessionConfiguration(p_session)
340                    if academic_session == None:
341                        return _(
342                            u'Session configuration object is not available.'
343                            ), None
344                    amount = getattr(certificate, 'school_fee_2', 0.0)
345        elif category == 'clearance':
346            try:
347                p_item = student['studycourse'].certificate.code
348            except (AttributeError, TypeError):
349                return _('Study course data are incomplete.'), None
350            amount = academic_session.clearance_fee
351        elif category == 'combi' and combi:
352            categories = getUtility(IKofaUtils).COMBI_PAYMENT_CATEGORIES
353            for cat in combi:
354                fee_name = cat + '_fee'
355                cat_amount = getattr(academic_session, fee_name, 0.0)
356                if not cat_amount:
357                    return _('%s undefined.' % categories[cat]), None
358                amount += cat_amount
359                p_item += u'%s + ' % categories[cat]
360            p_item = p_item.strip(' + ')
361        else:
362            fee_name = category + '_fee'
363            amount = getattr(academic_session, fee_name, 0.0)
364        if amount in (0.0, None):
365            return _('Amount could not be determined.'), None
366        if self.samePaymentMade(student, category, p_item, p_session):
367            return _('This type of payment has already been made.'), None
368        if self._isPaymentDisabled(p_session, category, student):
369            return _('This category of payments has been disabled.'), None
370        payment = createObject(u'waeup.StudentOnlinePayment')
371        timestamp = ("%d" % int(time()*10000))[1:]
372        payment.p_id = "p%s" % timestamp
373        payment.p_category = category
374        payment.p_item = p_item
375        payment.p_session = p_session
376        payment.p_level = p_level
377        payment.p_current = p_current
378        payment.amount_auth = amount
379        payment.p_combi = combi
380        return None, payment
381
382
383    def setPaymentDetails(self, category, student,
384            previous_session, previous_level, combi):
385        """Create Payment object and set the payment data of a student for
386        the payment category specified.
387
388        """
389        if grok.getSite().__name__ == 'uniben-cdl':
390            return self.setCDLPaymentDetails(category, student,
391                previous_session, previous_level, combi)
392        p_item = u''
393        amount = 0.0
394        if previous_session:
395            if previous_session < student['studycourse'].entry_session:
396                return _('The previous session must not fall below '
397                         'your entry session.'), None
398            if category == 'schoolfee':
399                # School fee is always paid for the following session
400                if previous_session > student['studycourse'].current_session:
401                    return _('This is not a previous session.'), None
402            else:
403                if previous_session > student['studycourse'].current_session - 1:
404                    return _('This is not a previous session.'), None
405            p_session = previous_session
406            p_level = previous_level
407            p_current = False
408        else:
409            p_session = student['studycourse'].current_session
410            p_level = student['studycourse'].current_level
411            p_current = True
412        academic_session = self._getSessionConfiguration(p_session)
413        if academic_session == None:
414            return _(u'Session configuration object is not available.'), None
415        # Determine fee.
416        if category.startswith('pharmd') \
417            and student.current_mode == 'special_ft':
418            amount = 80000.0
419        elif category == 'plag_test':
420            amount = 2500.0
421            if student.is_postgrad:
422                amount = 5000.0
423        #elif category == 'develop' and student.is_postgrad:
424        #    amount = academic_session.development_fee
425        elif category == 'bed_allocation':
426            acco_details = self.getAccommodationDetails(student)
427            p_session = acco_details['booking_session']
428            p_item = acco_details['bt']
429            desired_hostel = student['accommodation'].desired_hostel
430            if not desired_hostel:
431                return _(u'Select your favoured hostel first.'), None
432            if desired_hostel and desired_hostel != 'no':
433                p_item = u'%s (%s)' % (p_item, desired_hostel)
434            amount = academic_session.booking_fee
435            if student.is_postgrad:
436                amount += 500
437        elif category == 'hostel_maintenance':
438            amount = 0.0
439            booking_session = grok.getSite()['hostels'].accommodation_session
440            bedticket = student['accommodation'].get(str(booking_session), None)
441            if bedticket is not None and bedticket.bed is not None:
442                p_item = bedticket.bed_coordinates
443                p_session = booking_session
444                if bedticket.bed.__parent__.maint_fee > 0:
445                    amount = bedticket.bed.__parent__.maint_fee
446                else:
447                    # fallback
448                    amount = academic_session.maint_fee
449            else:
450                return _(u'No bed allocated.'), None
451        #elif category == 'hostel_application':
452        #    amount = 1000.0
453        #elif category.startswith('tempmaint'):
454        #    if not self._hostelApplicationPaymentMade(
455        #        student, student.current_session):
456        #        return _(
457        #            'You have not yet paid the hostel application fee.'), None
458        #    if category == 'tempmaint_1':
459        #        amount = 8150.0
460        #    elif category == 'tempmaint_2':
461        #        amount = 12650.0
462        #    elif category == 'tempmaint_3':
463        #        amount = 9650.0
464        elif category == 'clearance':
465            p_item = student.certcode
466            if p_item is None:
467                return _('Study course data are incomplete.'), None
468            if student.is_jupeb:
469                amount = 50000.0
470            elif student.faccode.startswith('FCETA'):
471                # ASABA and AKOKA
472                amount = 35000.0
473            elif student.depcode == 'DMIC':
474                amount = 70000.0
475            elif student.faccode in ('BMS', 'MED', 'DEN') \
476                and not student.is_postgrad:
477                amount = 80000.0
478            elif student.faccode == 'DCOEM':
479                return _('Acceptance fee payment not necessary.'), None
480            else:
481                amount = 60000.0
482        elif category == 'schoolfee':
483            try:
484                certificate = student['studycourse'].certificate
485                p_item = certificate.code
486            except (AttributeError, TypeError):
487                return _('Study course data are incomplete.'), None
488            try:
489                if student.entry_session < 2017:
490                    schoolfees_dict = SCHOOLFEES[12][p_item]
491                elif student.entry_session < 2020:
492                    schoolfees_dict = SCHOOLFEES[17][p_item]
493                elif student.entry_session < 2022:
494                    schoolfees_dict = SCHOOLFEES[20][p_item]
495                else:
496                    schoolfees_dict = SCHOOLFEES[22][p_item]
497            except KeyError:
498                return _('School fee not yet fixed: p_item = %s' % p_item), None
499            if previous_session:
500                # Students can pay for previous sessions in all workflow states.
501                # Fresh students are excluded by the update method of the
502                # PreviousPaymentAddFormPage.
503                if previous_session == student['studycourse'].entry_session:
504                    if student.is_foreigner:
505                        amount = schoolfees_dict['initial_foreigner']
506                    else:
507                        amount = schoolfees_dict['initial']
508                else:
509                    if student.is_foreigner:
510                        amount = schoolfees_dict['returning_foreigner']
511                    else:
512                        amount = schoolfees_dict['returning']
513            else:
514                if student.state == CLEARED:
515                    if student.is_foreigner:
516                        amount = schoolfees_dict['initial_foreigner']
517                    else:
518                        amount = schoolfees_dict['initial']
519                elif student.state == PAID and student.is_postgrad:
520                    p_session += 1
521                    academic_session = self._getSessionConfiguration(p_session)
522                    if academic_session == None:
523                        return _(u'Session configuration object is not available.'), None
524                    if student.is_foreigner:
525                        amount = schoolfees_dict['returning_foreigner']
526                    else:
527                        amount = schoolfees_dict['returning']
528                elif student.state == RETURNING:
529                    # In case of returning school fee payment the payment session
530                    # and level contain the values of the session the student
531                    # has paid for.
532                    p_session, p_level = self.getReturningData(student)
533                    academic_session = self._getSessionConfiguration(p_session)
534                    if academic_session == None:
535                        return _(u'Session configuration object is not available.'), None
536
537                    # Students are only allowed to pay for the next session
538                    # if current session payment has really been made,
539                    # i.e. payment object exists and is paid.
540                    #if not self._paymentMade(
541                    #    student, student.current_session):
542                    #    return _('You have not yet paid your current/active' +
543                    #             ' session. Please use the previous session' +
544                    #             ' payment form first.'), None
545
546                    if student.is_foreigner:
547                        amount = schoolfees_dict['returning_foreigner']
548                    else:
549                        amount = schoolfees_dict['returning']
550                # PHARMD school fee amount is fixed and previously paid
551                # installments in current session are deducted.
552                if student.current_mode == 'special_ft' \
553                    and student.state in (RETURNING, CLEARED):
554                    if student.is_foreigner:
555                        amount = 260000.0 - self._pharmdInstallments(student)
556                    else:
557                        amount = 160000.0 - self._pharmdInstallments(student)
558            try:
559                amount = float(amount)
560            except ValueError:
561                return _(u'School fee not yet fixed: p_item = %s' % p_item), None
562            # Give 50% school fee discount to staff members.
563            if student.is_staff:
564                amount /= 2
565
566        else:
567            fee_name = category + '_fee'
568            amount = getattr(academic_session, fee_name, 0.0)
569        if amount in (0.0, None):
570            return _('Amount could not be determined.'), None
571        # Add session specific penalty fee.
572        if category == 'schoolfee' and student.is_postgrad:
573            amount += academic_session.penalty_pg
574            amount += academic_session.development_fee
575        elif category == 'schoolfee' and student.current_mode == ('ug_ft'):
576            amount += academic_session.penalty_ug_ft
577        elif category == 'schoolfee' and student.current_mode == ('ug_pt'):
578            amount += academic_session.penalty_ug_pt
579        elif category == 'schoolfee' and student.current_mode == ('ug_sw'):
580            amount += academic_session.penalty_sw
581        elif category == 'schoolfee' and student.current_mode in (
582            'dp_ft', 'dp_pt'):
583            amount += academic_session.penalty_dp
584        if category.startswith('tempmaint'):
585            p_item = getUtility(IKofaUtils).PAYMENT_CATEGORIES[category]
586            p_item = unicode(p_item)
587            # Now we change the category because tempmaint payments
588            # will be obsolete when Uniben returns to Kofa bed allocation.
589            category = 'hostel_maintenance'
590        # Create ticket.
591        if self.samePaymentMade(student, category, p_item, p_session):
592            return _('This type of payment has already been made.'), None
593        if self._isPaymentDisabled(p_session, category, student):
594            return _('This category of payments has been disabled.'), None
595        payment = createObject(u'waeup.StudentOnlinePayment')
596        timestamp = ("%d" % int(time()*10000))[1:]
597        payment.p_id = "p%s" % timestamp
598        payment.p_category = category
599        payment.p_item = p_item
600        payment.p_session = p_session
601        payment.p_level = p_level
602        payment.p_current = p_current
603        payment.amount_auth = amount
604        return None, payment
605
606    def warnCreditsOOR(self, studylevel, course=None):
607        studycourse = studylevel.__parent__
608        certificate = getattr(studycourse,'certificate', None)
609        current_level = studycourse.current_level
610        if None in (current_level, certificate):
611            return
612        if studylevel.__parent__.previous_verdict == 'R':
613            return
614        end_level = certificate.end_level
615        if studylevel.student.faccode in (
616            'MED', 'DEN', 'BMS') and studylevel.level == 200:
617            limit = 61
618        elif current_level >= end_level:
619            limit = 51
620        else:
621            limit = 50
622        if course and studylevel.total_credits + course.credits > limit:
623            return _('Maximum credits exceeded.')
624        elif studylevel.total_credits > limit:
625            return _('Maximum credits exceeded.')
626        return
627
628    def warnCourseAlreadyPassed(self, studylevel, course):
629        """Return message if course has already been passed at
630        previous levels.
631        """
632
633        # med: we may need to put this matter on hold and allow
634        # all the students to register.
635        return False
636
637        previous_verdict = studylevel.__parent__.previous_verdict
638        if previous_verdict in ('C', 'M'):
639            return False
640        for slevel in studylevel.__parent__.values():
641            for cticket in slevel.values():
642                if cticket.code == course.code \
643                    and cticket.total_score >= cticket.passmark:
644                    return _('Course has already been passed at previous level.')
645        return False
646
647    def clearance_disabled_message(self, student):
648        if student.is_postgrad:
649            return None
650        try:
651            session_config = grok.getSite()[
652                'configuration'][str(student.current_session)]
653        except KeyError:
654            return _('Session configuration object is not available.')
655        if not session_config.clearance_enabled:
656            return _('Clearance is disabled for this session.')
657        return None
658
659    def renderPDFTranscript(self, view, filename='transcript.pdf',
660                  student=None,
661                  studentview=None,
662                  note=None,
663                  signatures=(),
664                  sigs_in_footer=(),
665                  digital_sigs=(),
666                  show_scans=True, topMargin=1.5,
667                  omit_fields=(),
668                  tableheader=None,
669                  no_passport=False,
670                  save_file=False):
671        """Render pdf slip of a transcripts.
672        """
673        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
674        # XXX: tell what the different parameters mean
675        style = getSampleStyleSheet()
676        creator = self.getPDFCreator(student)
677        data = []
678        doc_title = view.label
679        author = '%s (%s)' % (view.request.principal.title,
680                              view.request.principal.id)
681        footer_text = view.label.split('\n')
682        if len(footer_text) > 2:
683            # We can add a department in first line
684            footer_text = footer_text[1]
685        else:
686            # Only the first line is used for the footer
687            footer_text = footer_text[0]
688        if getattr(student, 'student_id', None) is not None:
689            footer_text = "%s - %s - " % (student.student_id, footer_text)
690
691        # Insert student data table
692        if student is not None:
693            #bd_translation = trans(_('Base Data'), portal_language)
694            #data.append(Paragraph(bd_translation, HEADING_STYLE))
695            data.append(render_student_data(
696                studentview, view.context,
697                omit_fields, lang=portal_language,
698                slipname=filename,
699                no_passport=no_passport))
700
701        transcript_data = view.context.getTranscriptData()
702        levels_data = transcript_data[0]
703
704        contextdata = []
705        f_label = trans(_('Course of Study:'), portal_language)
706        f_label = Paragraph(f_label, ENTRY1_STYLE)
707        f_text = formatted_text(view.context.certificate.longtitle)
708        f_text = Paragraph(f_text, ENTRY1_STYLE)
709        contextdata.append([f_label,f_text])
710
711        f_label = trans(_('Faculty:'), portal_language)
712        f_label = Paragraph(f_label, ENTRY1_STYLE)
713        f_text = formatted_text(
714            view.context.certificate.__parent__.__parent__.__parent__.longtitle)
715        f_text = Paragraph(f_text, ENTRY1_STYLE)
716        contextdata.append([f_label,f_text])
717
718        f_label = trans(_('Department:'), portal_language)
719        f_label = Paragraph(f_label, ENTRY1_STYLE)
720        f_text = formatted_text(
721            view.context.certificate.__parent__.__parent__.longtitle)
722        f_text = Paragraph(f_text, ENTRY1_STYLE)
723        contextdata.append([f_label,f_text])
724
725        f_label = trans(_('Entry Session:'), portal_language)
726        f_label = Paragraph(f_label, ENTRY1_STYLE)
727        f_text = formatted_text(
728            view.session_dict.get(view.context.entry_session))
729        f_text = Paragraph(f_text, ENTRY1_STYLE)
730        contextdata.append([f_label,f_text])
731
732        f_label = trans(_('Final Session:'), portal_language)
733        f_label = Paragraph(f_label, ENTRY1_STYLE)
734        f_text = formatted_text(
735            view.session_dict.get(view.context.current_session))
736        f_text = Paragraph(f_text, ENTRY1_STYLE)
737        contextdata.append([f_label,f_text])
738
739        f_label = trans(_('Entry Mode:'), portal_language)
740        f_label = Paragraph(f_label, ENTRY1_STYLE)
741        f_text = formatted_text(view.studymode_dict.get(
742            view.context.entry_mode))
743        f_text = Paragraph(f_text, ENTRY1_STYLE)
744        contextdata.append([f_label,f_text])
745
746        f_label = trans(_('Final Verdict:'), portal_language)
747        f_label = Paragraph(f_label, ENTRY1_STYLE)
748        f_text = formatted_text(view.studymode_dict.get(
749            view.context.current_verdict))
750        f_text = Paragraph(f_text, ENTRY1_STYLE)
751        contextdata.append([f_label,f_text])
752
753        f_label = trans(_('Cumulative GPA:'), portal_language)
754        f_label = Paragraph(f_label, ENTRY1_STYLE)
755        format_float = getUtility(IKofaUtils).format_float
756        cgpa = format_float(transcript_data[1], 3)
757        if student.state == GRADUATED:
758            f_text = formatted_text('%s (%s)' % (
759                cgpa, self.getClassFromCGPA(transcript_data[1], student)[1]))
760        else:
761            f_text = formatted_text('%s' % cgpa)
762            if not transcript_data[1]:
763                f_text = formatted_text('No results yet!')
764        f_text = Paragraph(f_text, ENTRY1_STYLE)
765        contextdata.append([f_label,f_text])
766
767        contexttable = Table(contextdata,style=SLIP_STYLE)
768        data.append(contexttable)
769
770        if transcript_data[1]:
771            transcripttables = render_transcript_data(
772                view, tableheader, levels_data, lang=portal_language)
773            data.extend(transcripttables)
774
775        # Insert signatures
776        # XXX: We are using only sigs_in_footer in waeup.kofa, so we
777        # do not have a test for the following lines.
778        if signatures and not sigs_in_footer:
779            data.append(Spacer(1, 20))
780            # Render one signature table per signature to
781            # get date and signature in line.
782            for signature in signatures:
783                signaturetables = get_signature_tables(signature)
784                data.append(signaturetables[0])
785
786        # Insert digital signatures
787        if digital_sigs:
788            data.append(Spacer(1, 20))
789            sigs = digital_sigs.split('\n')
790            for sig in sigs:
791                data.append(Paragraph(sig, NOTE_STYLE))
792
793        view.response.setHeader(
794            'Content-Type', 'application/pdf')
795        try:
796            pdf_stream = creator.create_pdf(
797                data, None, doc_title, author=author, footer=footer_text,
798                note=note, sigs_in_footer=sigs_in_footer, topMargin=topMargin,
799                view=view)
800        except IOError:
801            view.flash(_('Error in image file.'))
802            return view.redirect(view.url(view.context))
803        if save_file:
804            self._saveTranscriptPDF(student, pdf_stream)
805            return
806        return pdf_stream
807
808    #: A tuple containing the names of registration states in which changing of
809    #: passport pictures is allowed.
810    PORTRAIT_CHANGE_STATES = (CLEARANCE, REQUESTED)
811
812    #: A tuple containing the names of registration states in which changing of
813    #: scanned signatures is allowed.
814    SIGNATURE_CHANGE_STATES = (CLEARED, RETURNING, PAID, REGISTERED, VALIDATED, )
815
816    # Uniben prefix
817    @property
818    def STUDENT_ID_PREFIX(self):
819        if grok.getSite().__name__ == 'uniben-cdl':
820            return u'C'
821        return u'B'
822
823    STUDENT_EXPORTER_NAMES = (
824            'students',
825            'studentstudycourses',
826            'studentstudycourses_1',
827            'studentstudylevels',
828            #'studentstudylevels_1',
829            'coursetickets',
830            #'coursetickets_1',
831            'studentpayments',
832            'bedtickets',
833            'trimmed',
834            'outstandingcourses',
835            'unpaidpayments',
836            'sfpaymentsoverview',
837            'sessionpaymentsoverview',
838            'studylevelsoverview',
839            'combocard',
840            'bursary',
841            'accommodationpayments',
842            'transcriptdata',
843            'trimmedpayments',
844            'medicalhistory',
845            'nysc',
846            )
Note: See TracBrowser for help on using the repository browser.