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

Last change on this file since 17915 was 17915, checked in by Henrik Bettermann, 2 weeks ago

Add exporter.

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