source: main/waeup.aaue/trunk/src/waeup/aaue/students/browser.py @ 14266

Last change on this file since 14266 was 14244, checked in by Henrik Bettermann, 8 years ago

Add student id card fee to first school fee payment.

Attention: The payment slip does not contain any information whether the fee was added or not. We can only draw conclusions from from the student's entry session whether the fee had been included.

  • Property svn:keywords set to Id
File size: 32.2 KB
Line 
1## $Id: browser.py 14244 2016-10-31 07:02:58Z henrik $
2##
3## Copyright (C) 2012 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 csv
20import textwrap
21from cStringIO import StringIO
22from zope.i18n import translate
23from zope.component import getUtility, queryUtility
24from zope.schema.interfaces import TooBig, TooSmall
25from zope.security import checkPermission
26from zope.catalog.interfaces import ICatalog
27from zope.formlib.textwidgets import BytesDisplayWidget
28from waeup.kofa.browser.layout import UtilityView
29from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget
30from waeup.kofa.interfaces import IKofaUtils, academic_sessions_vocab
31from waeup.kofa.students.interfaces import IStudentsUtils, IStudent
32from waeup.kofa.students.workflow import PAID, REGISTERED, RETURNING
33from waeup.kofa.students.studylevel import getGradeWeightFromScore
34from waeup.kofa.students.browser import (
35    StartClearancePage,
36    StudentBasePDFFormPage,
37    CourseTicketAddFormPage,
38    StudyLevelDisplayFormPage,
39    StudyLevelManageFormPage,
40    StudyLevelEditFormPage,
41    ExportPDFTranscriptSlip,
42    ExportPDFAdmissionSlip,
43    BedTicketAddPage,
44    StudentFilesUploadPage,
45    PaymentsManageFormPage,
46    CourseTicketDisplayFormPage,
47    CourseTicketManageFormPage,
48    EditScoresPage,
49    ExportPDFScoresSlip,
50    StudyCourseTranscriptPage,
51    )
52from kofacustom.nigeria.students.browser import (
53    NigeriaOnlinePaymentDisplayFormPage,
54    NigeriaOnlinePaymentAddFormPage,
55    NigeriaExportPDFPaymentSlip,
56    NigeriaExportPDFCourseRegistrationSlip,
57    NigeriaStudentPersonalDisplayFormPage,
58    NigeriaStudentPersonalEditFormPage,
59    NigeriaStudentPersonalManageFormPage,
60    NigeriaStudentClearanceDisplayFormPage,
61    NigeriaExportPDFClearanceSlip,
62    NigeriaStudentClearanceManageFormPage,
63    NigeriaStudentClearanceEditFormPage,
64    NigeriaAccommodationManageFormPage,
65    NigeriaStudentBaseDisplayFormPage,
66    NigeriaStudentBaseManageFormPage
67    )
68from waeup.aaue.students.interfaces import (
69    ICustomStudentOnlinePayment,
70    ICustomStudentStudyLevel,
71    ICustomStudent,
72    ICustomStudentPersonal,
73    ICustomStudentPersonalEdit,
74    ICustomUGStudentClearance,
75    ICustomPGStudentClearance,
76    ICustomCourseTicket,
77    ICustomStudentBase)
78from waeup.aaue.interswitch.browser import gateway_net_amt
79from waeup.aaue.interfaces import MessageFactory as _
80
81class CustomStudentBaseDisplayFormPage(NigeriaStudentBaseDisplayFormPage):
82    """ Page to display student base data
83    """
84    form_fields = grok.AutoFields(ICustomStudentBase).omit(
85        'password', 'suspended', 'suspended_comment', 'flash_notice')
86    form_fields[
87        'financial_clearance_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
88
89class CustomStudentBaseManageFormPage(NigeriaStudentBaseManageFormPage):
90    """ View to manage student base data
91    """
92    form_fields = grok.AutoFields(ICustomStudentBase).omit(
93        'student_id', 'adm_code', 'suspended',
94        'financially_cleared_by', 'financial_clearance_date')
95
96class CustomStudentPersonalDisplayFormPage(NigeriaStudentPersonalDisplayFormPage):
97    """ Page to display student personal data
98    """
99    form_fields = grok.AutoFields(ICustomStudentPersonal)
100    form_fields['perm_address'].custom_widget = BytesDisplayWidget
101    form_fields['father_address'].custom_widget = BytesDisplayWidget
102    form_fields['mother_address'].custom_widget = BytesDisplayWidget
103    form_fields['next_kin_address'].custom_widget = BytesDisplayWidget
104    form_fields[
105        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
106
107class CustomStudentPersonalEditFormPage(NigeriaStudentPersonalEditFormPage):
108    """ Page to edit personal data
109    """
110    form_fields = grok.AutoFields(ICustomStudentPersonalEdit).omit('personal_updated')
111
112class CustomStudentPersonalManageFormPage(NigeriaStudentPersonalManageFormPage):
113    """ Page to edit personal data
114    """
115    form_fields = grok.AutoFields(ICustomStudentPersonal)
116    form_fields['personal_updated'].for_display = True
117    form_fields[
118        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
119
120class CustomStudentClearanceDisplayFormPage(NigeriaStudentClearanceDisplayFormPage):
121    """ Page to display student clearance data
122    """
123
124    @property
125    def form_fields(self):
126        if self.context.is_postgrad:
127            form_fields = grok.AutoFields(
128                ICustomPGStudentClearance).omit('clearance_locked')
129        else:
130            form_fields = grok.AutoFields(
131                ICustomUGStudentClearance).omit('clearance_locked')
132        if not getattr(self.context, 'officer_comment'):
133            form_fields = form_fields.omit('officer_comment')
134        else:
135            form_fields['officer_comment'].custom_widget = BytesDisplayWidget
136        form_fields = form_fields.omit('def_adm')
137        return form_fields
138
139class CustomStudentClearanceManageFormPage(NigeriaStudentClearanceManageFormPage):
140    """ Page to edit student clearance data
141    """
142
143    @property
144    def form_fields(self):
145        if self.context.is_postgrad:
146            form_fields = grok.AutoFields(
147                ICustomPGStudentClearance).omit('clr_code')
148        else:
149            form_fields = grok.AutoFields(
150                ICustomUGStudentClearance).omit('clr_code')
151        form_fields = form_fields.omit('def_adm')
152        return form_fields
153
154class CustomStudentClearanceEditFormPage(NigeriaStudentClearanceEditFormPage):
155    """ View to edit student clearance data by student
156    """
157
158    @property
159    def form_fields(self):
160        if self.context.is_postgrad:
161            form_fields = grok.AutoFields(ICustomPGStudentClearance).omit(
162            'clearance_locked', 'nysc_location', 'clr_code', 'officer_comment',
163            'physical_clearance_date')
164        else:
165            form_fields = grok.AutoFields(ICustomUGStudentClearance).omit(
166            'clearance_locked', 'clr_code', 'officer_comment',
167            'physical_clearance_date', 'date_of_birth', 'nationality', 'lga')
168        form_fields = form_fields.omit('def_adm')
169        return form_fields
170
171class CustomStartClearancePage(StartClearancePage):
172    with_ac = False
173
174    @property
175    def all_required_fields_filled(self):
176        if not self.context.email:
177            return _("Email address is missing."), 'edit_base'
178        if not self.context.phone:
179            return _("Phone number is missing."), 'edit_base'
180        if not self.context.father_name:
181            return _("Personal data form is not properly filled."), 'edit_personal'
182        return
183
184class CustomOnlinePaymentDisplayFormPage(NigeriaOnlinePaymentDisplayFormPage):
185    """ Page to view an online payment ticket
186    """
187    grok.context(ICustomStudentOnlinePayment)
188    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
189        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
190    form_fields[
191        'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
192    form_fields[
193        'payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
194
195class CustomOnlinePaymentAddFormPage(NigeriaOnlinePaymentAddFormPage):
196    """ Page to add an online payment ticket
197    """
198    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).select(
199        'p_category')
200
201class CustomPaymentsManageFormPage(PaymentsManageFormPage):
202    """ Page to manage the student payments.
203
204    This manage form page is for both students and students officers.
205    """
206    @property
207    def manage_payments_allowed(self):
208        return checkPermission('waeup.manageStudent', self.context)
209
210class CustomExportPDFPaymentSlip(NigeriaExportPDFPaymentSlip):
211    """Deliver a PDF slip of the context.
212    """
213    grok.context(ICustomStudentOnlinePayment)
214    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
215        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
216    form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
217    form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
218
219    @property
220    def note(self):
221        p_session = self.context.p_session
222        try:
223            academic_session = grok.getSite()['configuration'][str(p_session)]
224        except KeyError:
225            academic_session = None
226        text =  '\n\n The Amount Authorized is inclusive of: '
227        if self.context.p_category in ('schoolfee_incl', 'schoolfee_1') \
228            and academic_session:
229            welfare_fee = gateway_net_amt(academic_session.welfare_fee)
230            union_fee = gateway_net_amt(academic_session.union_fee)
231            if self.context.student.entry_session == 2016 \
232                and self.context.student.entry_mode == 'ug_ft' \
233                and self.context.p_session == 2016:
234                # Add student id card fee to first school fee payment.
235                # Attention: The payment slip does not contain any information
236                # whether the fee was added or not.
237                # We can only draw conclusions from from the student's entry
238                # session whether the fee had been included.
239                id_card_fee = gateway_net_amt(academic_session.id_card_fee)
240                text += ('School Fee, '
241                         '%s Naira Student ID Card Fee, '
242                         '%s Naira Student Union Dues, '
243                         '%s Naira Student Welfare Assurance Fee and '
244                         % (id_card_fee, union_fee, welfare_fee))
245            else:
246                text += ('School Fee, '
247                         '%s Naira Student Union Dues, '
248                         '%s Naira Student Welfare Assurance Fee and '
249                         % (union_fee, welfare_fee))
250        elif self.context.p_category in (
251            'clearance_incl', 'clearance_medical_incl') and academic_session:
252            matric_gown_fee = gateway_net_amt(academic_session.matric_gown_fee)
253            lapel_fee = gateway_net_amt(academic_session.lapel_fee)
254            text += ('Acceptance Fee, '
255                     '%s Naira Matriculation Gown Fee, '
256                     '%s Naira Lapel/File Fee and '
257                     % (matric_gown_fee, lapel_fee))
258        return text + '250.0 Naira Transaction Charge.'
259
260class CustomStudyLevelDisplayFormPage(StudyLevelDisplayFormPage):
261    """ Page to display student study levels
262    """
263    grok.context(ICustomStudentStudyLevel)
264    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
265        'total_credits', 'gpa', 'level', 'imported_gpa', 'imported_cgpa')
266    form_fields[
267        'validation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
268
269    @property
270    def show_results(self):
271        isStudent = getattr(
272            self.request.principal, 'user_type', None) == 'student'
273        if isStudent and self.context.student.state != RETURNING \
274            and self.context.student.current_level == self.context.level:
275            return False
276        return True
277
278class CustomStudyLevelManageFormPage(StudyLevelManageFormPage):
279    """ Page to edit the student study level data
280    """
281    grok.context(ICustomStudentStudyLevel)
282
283class CustomStudyLevelEditFormPage(StudyLevelEditFormPage):
284    """ Page to edit the student study level data by students
285    """
286    grok.context(ICustomStudentStudyLevel)
287
288class CustomExportPDFCourseRegistrationSlip(
289    NigeriaExportPDFCourseRegistrationSlip):
290    """Deliver a PDF slip of the context.
291    """
292    grok.context(ICustomStudentStudyLevel)
293    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
294        'level_session', 'level_verdict',
295        'validated_by', 'validation_date', 'gpa', 'level',
296        'imported_gpa', 'imported_cgpa')
297
298    omit_fields = ('password', 'suspended', 'suspended_comment',
299        'phone', 'adm_code', 'sex', 'email', 'date_of_birth',
300        'department', 'current_mode', 'current_level', 'flash_notice')
301
302    @property
303    def show_results(self):
304        isStudent = getattr(
305            self.request.principal, 'user_type', None) == 'student'
306        return not isStudent \
307            or self.context.student.current_level != self.context.level \
308            or self.context.student.state == RETURNING
309
310    def update(self):
311        if self.context.student.state != REGISTERED \
312            and self.context.student.current_level == self.context.level:
313            self.flash(_('Forbidden'), type="warning")
314            self.redirect(self.url(self.context))
315            return
316
317    @property
318    def label(self):
319        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
320        lang = self.request.cookies.get('kofa.language', portal_language)
321        level_title = translate(self.context.level_title, 'waeup.kofa',
322            target_language=lang)
323        line0 = ''
324        if self.context.student.is_postgrad:
325            line0 = 'SCHOOL OF POSTGRADUATE STUDIES\n'
326        elif self.context.student.current_mode.endswith('_pt'):
327            line0 = 'DIRECTORATE OF PART-TIME DEGREE PROGRAMMES\n'
328        line1 = translate(_('Course Registration Slip'),
329            target_language=portal_language) \
330            + ' %s' % level_title
331        line2 = translate(_('Session'),
332            target_language=portal_language) \
333            + ' %s' % self.context.getSessionString
334        return '%s%s\n%s' % (line0, line1, line2)
335
336    @property
337    def title(self):
338        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
339        return translate(_('Units Registered'), target_language=portal_language)
340
341    def _signatures(self):
342        if self.context.student.current_mode.endswith('_pt') \
343            or self.context.student.current_mode == 'found':
344            return (
345                [('I have selected the course on the advise of my Head of '
346                 'Department. <br>', _('Student\'s Signature'), '<br>')],
347                [('This student has satisfied the department\'s requirements. '
348                 'I recommend to approve the course registration. <br>',
349                 _('Head of Department\'s Signature'), '<br>')],
350                [('' , _('Deputy Registrar\'s Signature'), '<br>')],
351                [('', _('Director\'s Signature'))]
352                )
353        if self.context.student.current_mode in (
354            'de_ft', 'ug_ft', 'dp_ft', 'transfer'):
355            return ([_('Academic Adviser\'s Signature'),
356                _('Faculty Officer\'s Signature'),
357                _('Student\'s Signature')],)
358
359        if self.context.student.current_mode in ('special_pg_ft', 'special_pg_pt'):
360            return (
361                [('I declare that all items of information supplied above are correct:' ,
362                    _('Student\'s Signature'), '<br>')],
363                [('We approved the above registration:',
364                    _('Major Supervisor (Name / Signature)'), '')],
365                [('', _('Co-Supervisor (Name / Signature)'), '')],
366                [('', _('Head of Department'), '<br>')],
367                [('The student has satisfied the conditions for renewal of '
368                  'registration for graduate school programme in this university:',
369                  _('Secretary <br /> (School of Postgraduate Studies)'), '')],
370                [('', _('Dean <br /> (School of Postgraduate Studies)'), '')],
371                )
372        return None
373
374
375    def render(self):
376        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
377        Sem = translate(_('Sem.'), target_language=portal_language)
378        Code = translate(_('Code'), target_language=portal_language)
379        Title = translate(_('Title'), target_language=portal_language)
380        Cred = translate(_('Cred.'), target_language=portal_language)
381        if self.show_results:
382            Score = translate(_('Score'), target_language=portal_language)
383            #CA = translate(_('CA'), target_language=portal_language)
384            Grade = translate(_('Grade'), target_language=portal_language)
385        Signature = translate(_('Lecturer\'s Signature'), 'waeup.aaue',
386            target_language=portal_language)
387        studentview = StudentBasePDFFormPage(self.context.student,
388            self.request, self.omit_fields)
389        students_utils = getUtility(IStudentsUtils)
390
391        tabledata = []
392        tableheader = []
393        contenttitle = []
394        for i in range(1,7):
395            tabledata.append(sorted(
396                [value for value in self.context.values() if value.semester == i],
397                key=lambda value: str(value.semester) + value.code))
398            if self.show_results:
399                tableheader.append([(Code,'code', 2.0),
400                                   (Title,'title', 7),
401                                   (Cred, 'credits', 1.5),
402                                   (Score, 'score', 1.4),
403                                   #(CA, 'ca', 1.4),
404                                   (Grade, 'grade', 1.4),
405                                   (Signature, 'dummy', 3),
406                                   ])
407            else:
408                tableheader.append([(Code,'code', 2.0),
409                                   (Title,'title', 7),
410                                   (Cred, 'credits', 1.5),
411                                   (Signature, 'dummy', 3),
412                                   ])
413        if len(self.label.split('\n')) == 3:
414            topMargin = 1.9
415        elif len(self.label.split('\n')) == 2:
416            topMargin = 1.7
417        else:
418            topMargin = 1.5
419        return students_utils.renderPDF(
420            self, 'course_registration_slip.pdf',
421            self.context.student, studentview,
422            tableheader=tableheader,
423            tabledata=tabledata,
424            signatures=self._signatures(),
425            topMargin=topMargin,
426            omit_fields=self.omit_fields
427            )
428
429class CustomStudyCourseTranscriptPage(StudyCourseTranscriptPage):
430    """ Page to display the student's transcript.
431    """
432    grok.require('waeup.viewStudent')
433
434class CustomExportPDFTranscriptSlip(ExportPDFTranscriptSlip):
435    """Deliver a PDF slip of the context.
436    """
437    grok.require('waeup.viewStudent')
438
439    def _sigsInFooter(self):
440        return []
441
442    def _signatures(self):
443        return ([(
444            'Mrs. Uniamikogbo, S.O., mnim, manupa <br /> Prin. Asst Registrar  <br /> '
445            'Exams, Records and Data Processing Division <br /> For: Registrar')],)
446
447    def render(self):
448        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
449        Term = translate(_('Sem.'), target_language=portal_language)
450        Code = translate(_('Code'), target_language=portal_language)
451        Title = translate(_('Title'), target_language=portal_language)
452        Cred = translate(_('Credits'), target_language=portal_language)
453        Score = translate(_('Score'), target_language=portal_language)
454        Grade = translate(_('Grade'), target_language=portal_language)
455        studentview = StudentBasePDFFormPage(self.context.student,
456            self.request, self.omit_fields)
457        students_utils = getUtility(IStudentsUtils)
458
459        tableheader = [(Code,'code', 2.5),
460                         (Title,'title', 7),
461                         (Term, 'semester', 1.5),
462                         (Cred, 'credits', 1.5),
463                         (Score, 'total_score', 1.5),
464                         (Grade, 'grade', 1.5),
465                         ]
466
467        return students_utils.renderPDFTranscript(
468            self, 'transcript.pdf',
469            self.context.student, studentview,
470            omit_fields=self.omit_fields,
471            tableheader=tableheader,
472            signatures=self._signatures(),
473            sigs_in_footer=self._sigsInFooter(),
474            )
475
476class CustomExportPDFAdmissionSlip(ExportPDFAdmissionSlip):
477    """Deliver a PDF Admission slip.
478    """
479
480    @property
481    def label(self):
482        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
483        return translate(_('e-Admission Slip \n'),
484            target_language=portal_language) \
485            + ' %s' % self.context.display_fullname
486
487class CustomExportPDFClearanceSlip(NigeriaExportPDFClearanceSlip):
488    """Deliver a PDF slip of the context.
489    """
490
491    @property
492    def label(self):
493        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
494        return translate(_('Verification/Clearance Slip\n'),
495            target_language=portal_language) \
496            + ' %s' % self.context.display_fullname
497
498    @property
499    def form_fields(self):
500        if self.context.is_postgrad:
501            form_fields = grok.AutoFields(
502                ICustomPGStudentClearance).omit('clearance_locked')
503        else:
504            form_fields = grok.AutoFields(
505                ICustomUGStudentClearance).omit('clearance_locked')
506        if not getattr(self.context, 'officer_comment'):
507            form_fields = form_fields.omit('officer_comment')
508        form_fields = form_fields.omit('def_adm')
509        return form_fields
510
511class StudentGetMatricNumberPage(UtilityView, grok.View):
512    """ Construct and set the matriculation number.
513    """
514    grok.context(IStudent)
515    grok.name('get_matric_number')
516    grok.require('waeup.viewStudent')
517
518    def update(self):
519        students_utils = getUtility(IStudentsUtils)
520        msg, mnumber = students_utils.setMatricNumber(self.context)
521        if msg:
522            self.flash(msg, type="danger")
523        else:
524            self.flash(_('Matriculation number %s assigned.' % mnumber))
525            self.context.writeLogMessage(self, '%s assigned' % mnumber)
526        self.redirect(self.url(self.context))
527        return
528
529    def render(self):
530        return
531
532class ExportPDFMatricNumberSlip(UtilityView, grok.View):
533    """Deliver a PDF notification slip.
534    """
535    grok.context(ICustomStudent)
536    grok.name('matric_number_slip.pdf')
537    grok.require('waeup.viewStudent')
538    prefix = 'form'
539
540    form_fields = grok.AutoFields(ICustomStudent).select(
541        'student_id', 'matric_number')
542    omit_fields = ('date_of_birth', 'current_level', 'flash_notice')
543
544    @property
545    def title(self):
546        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
547        return translate(_('Matriculation Number'), 'waeup.kofa',
548            target_language=portal_language)
549
550    @property
551    def label(self):
552        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
553        return translate(_('Matriculation Number Slip\n'),
554            target_language=portal_language) \
555            + ' %s' % self.context.display_fullname
556
557    def render(self):
558        if self.context.state not in (PAID,) or not self.context.is_fresh \
559            or not self.context.matric_number:
560            self.flash('Not allowed.', type="danger")
561            self.redirect(self.url(self.context))
562            return
563        students_utils = getUtility(IStudentsUtils)
564        pre_text = _('Congratulations! Your acceptance fee and school fees ' +
565                     'payments have been received and your matriculation ' +
566                     'number generated with details as follows.')
567        return students_utils.renderPDFAdmissionLetter(self,
568            self.context.student, omit_fields=self.omit_fields,
569            pre_text=pre_text, post_text='')
570
571class ExportPersonalDataSlip(UtilityView, grok.View):
572    """Deliver a PDF notification slip.
573    """
574    grok.context(ICustomStudent)
575    grok.name('personal_data_slip.pdf')
576    grok.require('waeup.viewStudent')
577    prefix = 'form'
578    note = None
579
580    form_fields = grok.AutoFields(ICustomStudentPersonal).omit('personal_updated')
581    omit_fields = ('suspended', 'suspended_comment', 'adm_code',
582                   'certificate', 'flash_notice')
583
584    @property
585    def title(self):
586        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
587        return translate(_('Personal Data'), 'waeup.kofa',
588            target_language=portal_language)
589
590    @property
591    def label(self):
592        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
593        return translate(_('Personal Data Slip\n'),
594            target_language=portal_language) \
595            + ' %s' % self.context.display_fullname
596
597    def render(self):
598        studentview = StudentBasePDFFormPage(self.context.student,
599            self.request, self.omit_fields)
600        students_utils = getUtility(IStudentsUtils)
601        return students_utils.renderPDF(self, 'personal_data_slip.pdf',
602            self.context.student, studentview, note=self.note,
603            omit_fields=self.omit_fields)
604
605class CustomAccommodationManageFormPage(NigeriaAccommodationManageFormPage):
606    """ Page to manage bed tickets.
607    This manage form page is for both students and students officers.
608    """
609    with_hostel_selection = True
610
611class CustomBedTicketAddPage(BedTicketAddPage):
612    with_ac = False
613
614class CustomStudentFilesUploadPage(StudentFilesUploadPage):
615    """ View to upload files by student. Inherit from same class in
616    base package, not from kofacustom.nigeria which
617    requires that no application slip exists.
618    """
619
620class CustomCourseTicketDisplayFormPage(CourseTicketDisplayFormPage):
621    """ Page to display course tickets
622    """
623    form_fields = grok.AutoFields(ICustomCourseTicket)
624
625class CustomCourseTicketManageFormPage(CourseTicketManageFormPage):
626    """ Page to manage course tickets
627    """
628    form_fields = grok.AutoFields(ICustomCourseTicket)
629    form_fields['title'].for_display = True
630    form_fields['fcode'].for_display = True
631    form_fields['dcode'].for_display = True
632    form_fields['semester'].for_display = True
633    form_fields['passmark'].for_display = True
634    form_fields['credits'].for_display = True
635    form_fields['mandatory'].for_display = False
636    form_fields['automatic'].for_display = True
637    form_fields['carry_over'].for_display = True
638
639class CustomEditScoresPage(EditScoresPage):
640    """Page that filters and lists students.
641    """
642    grok.template('editscorespage')
643
644
645    def _extract_uploadfile(self, uploadfile):
646        """Get a mapping of student-ids to scores.
647
648        The mapping is constructed by reading contents from `uploadfile`.
649
650        We expect uploadfile to be a regular CSV file with columns
651        ``student_id``, ``score`` and ``ca`` (other cols are ignored).
652        """
653        result = dict()
654        data = StringIO(uploadfile.read())  # ensure we have something seekable
655        reader = csv.DictReader(data)
656        for row in reader:
657            if not ('student_id' in row and 'score' in row and 'ca' in row):
658                continue
659            result[row['student_id']] = (row['score'], row['ca'])
660        return result
661
662    def update(self,  *args, **kw):
663        form = self.request.form
664        ob_class = self.__implemented__.__name__.replace('waeup.kofa.', '')
665        self.current_academic_session = grok.getSite()[
666            'configuration'].current_academic_session
667        if self.context.__parent__.__parent__.score_editing_disabled:
668            self.flash(_('Score editing disabled.'), type="warning")
669            self.redirect(self.url(self.context))
670            return
671        if not self.current_academic_session:
672            self.flash(_('Current academic session not set.'), type="warning")
673            self.redirect(self.url(self.context))
674            return
675        self.session_title = academic_sessions_vocab.getTerm(
676            self.current_academic_session).title
677        self.tickets = self._searchCatalog(self.current_academic_session)
678        self.editable_tickets = [
679            ticket for ticket in self.tickets if ticket.editable_by_lecturer]
680        if not self.tickets:
681            self.flash(_('No student found.'), type="warning")
682            self.redirect(self.url(self.context))
683            return
684        if not 'UPDATE_TABLE' in form and not 'UPDATE_FILE' in form:
685            return
686
687        if not self.editable_tickets:
688            return
689        if 'UPDATE_FILE' in form:
690            if form['uploadfile']:
691                try:
692                    formvals = self._extract_uploadfile(form['uploadfile'])
693                except:
694                    self.flash(
695                        _('Uploaded file contains illegal data. Ignored'),
696                        type="danger")
697                    return
698            else:
699                self.flash(
700                    _('No file provided.'), type="danger")
701                return
702        else:
703            formvals = dict(zip(form['sids'], zip(form['scores'], form['cas'])))
704        error = ''
705        # In AAUE only editable tickets are shown, see also customized
706        # pagetemplate
707        for ticket in self.editable_tickets:
708            ticket_error = False
709            score = ticket.score
710            ca = ticket.ca
711            sid = ticket.student.student_id
712            if formvals[sid][0] == '':
713                score = None
714            if formvals[sid][1] == '':
715                ca = None
716            try:
717                if formvals[sid][0]:
718                    score = int(formvals[sid][0])
719                if formvals[sid][1]:
720                    ca = int(formvals[sid][1])
721            except ValueError:
722                error += '%s, ' % ticket.student.display_fullname
723                ticket_error = True
724            if not ticket_error and ticket.score != score:
725                try:
726                    ticket.score = score
727                except TooBig:
728                    error += '%s, ' % ticket.student.display_fullname
729                    ticket_error = True
730                    pass
731                ticket.student.__parent__.logger.info(
732                    '%s - %s %s/%s score updated (%s)' %
733                    (ob_class, ticket.student.student_id,
734                     ticket.level, ticket.code, score))
735            if not ticket_error and ticket.ca != ca:
736                try:
737                    ticket.ca = ca
738                except TooBig:
739                    error += '%s, ' % ticket.student.display_fullname
740                    pass
741                ticket.student.__parent__.logger.info(
742                    '%s - %s %s/%s ca updated (%s)' %
743                    (ob_class, ticket.student.student_id,
744                     ticket.level, ticket.code, ca))
745        if error:
746            self.flash(_('Error: Score(s) and CA(s) of %s have not be updated. '
747              % error.strip(', ')), type="danger")
748            return
749        self.flash(_('You successfully updated course results.'))
750        return
751
752class CustomExportPDFScoresSlip(ExportPDFScoresSlip):
753    """Deliver a PDF slip of course tickets for a lecturer.
754    """
755
756    def table_data(self, session):
757        cat = queryUtility(ICatalog, name='coursetickets_catalog')
758        coursetickets = cat.searchResults(
759            session=(session, session),
760            code=(self.context.code, self.context.code)
761            )
762        # In AAUE only editable tickets can be printed
763        editable_tickets = [
764            ticket for ticket in coursetickets if ticket.editable_by_lecturer]
765        header = [[_(''),
766                   _('Matric No.'),
767                   _('Reg. No.'),
768                   _('Fullname'),
769                   _('Status'),
770                   _('Course of\nStudies'),
771                   _('Level'),
772                   _('Exam\nScore'),
773                   _(' CA  '),
774                   _('Total '),
775                   _('Grade'),
776                   ],]
777        tickets = []
778        no = 1
779        # In AAUE only editable tickets can be printed
780        for ticket in editable_tickets:
781            if None in (ticket.score, ticket.ca):
782                total = 'n/a'
783                grade = 'n/a'
784            else:
785                total = ticket.score + ticket.ca
786                grade = getGradeWeightFromScore(total)[0]
787            fullname = textwrap.fill(ticket.student.display_fullname, 30)
788            row = [no,
789                  ticket.student.matric_number,
790                  ticket.student.reg_number,
791                  fullname,
792                  ticket.student.translated_state,
793                  ticket.student.certcode,
794                  ticket.level,
795                  ticket.ca,
796                  ticket.score,
797                  total,
798                  grade,
799                  ]
800            tickets.append(row)
801            no += 1
802        return header + sorted(tickets,
803            key=lambda value: value[5] + value[3] + str(value[6]))
Note: See TracBrowser for help on using the repository browser.