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

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

Add note to raw score report and transripts.

  • Property svn:keywords set to Id
File size: 32.5 KB
Line 
1## $Id: browser.py 14267 2016-11-10 08:35:46Z 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    note = _("""
440<br /><br /><br /><br />
441<font size='10'>
442<strong>Note:</strong> This copy is subject to correction for typographical errors and ratification by the departmental board.
443</font>
444""")
445
446    def _sigsInFooter(self):
447        return []
448
449    def _signatures(self):
450        return ([(
451            'Mrs. Uniamikogbo, S.O., mnim, manupa <br /> Prin. Asst Registrar  <br /> '
452            'Exams, Records and Data Processing Division <br /> For: Registrar')],)
453
454    def render(self):
455        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
456        Term = translate(_('Sem.'), target_language=portal_language)
457        Code = translate(_('Code'), target_language=portal_language)
458        Title = translate(_('Title'), target_language=portal_language)
459        Cred = translate(_('Credits'), target_language=portal_language)
460        Score = translate(_('Score'), target_language=portal_language)
461        Grade = translate(_('Grade'), target_language=portal_language)
462        studentview = StudentBasePDFFormPage(self.context.student,
463            self.request, self.omit_fields)
464        students_utils = getUtility(IStudentsUtils)
465
466        tableheader = [(Code,'code', 2.5),
467                         (Title,'title', 7),
468                         (Term, 'semester', 1.5),
469                         (Cred, 'credits', 1.5),
470                         (Score, 'total_score', 1.5),
471                         (Grade, 'grade', 1.5),
472                         ]
473
474        return students_utils.renderPDFTranscript(
475            self, 'transcript.pdf',
476            self.context.student, studentview,
477            omit_fields=self.omit_fields,
478            tableheader=tableheader,
479            signatures=self._signatures(),
480            sigs_in_footer=self._sigsInFooter(),
481            note = self.note
482            )
483
484class CustomExportPDFAdmissionSlip(ExportPDFAdmissionSlip):
485    """Deliver a PDF Admission slip.
486    """
487
488    @property
489    def label(self):
490        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
491        return translate(_('e-Admission Slip \n'),
492            target_language=portal_language) \
493            + ' %s' % self.context.display_fullname
494
495class CustomExportPDFClearanceSlip(NigeriaExportPDFClearanceSlip):
496    """Deliver a PDF slip of the context.
497    """
498
499    @property
500    def label(self):
501        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
502        return translate(_('Verification/Clearance Slip\n'),
503            target_language=portal_language) \
504            + ' %s' % self.context.display_fullname
505
506    @property
507    def form_fields(self):
508        if self.context.is_postgrad:
509            form_fields = grok.AutoFields(
510                ICustomPGStudentClearance).omit('clearance_locked')
511        else:
512            form_fields = grok.AutoFields(
513                ICustomUGStudentClearance).omit('clearance_locked')
514        if not getattr(self.context, 'officer_comment'):
515            form_fields = form_fields.omit('officer_comment')
516        form_fields = form_fields.omit('def_adm')
517        return form_fields
518
519class StudentGetMatricNumberPage(UtilityView, grok.View):
520    """ Construct and set the matriculation number.
521    """
522    grok.context(IStudent)
523    grok.name('get_matric_number')
524    grok.require('waeup.viewStudent')
525
526    def update(self):
527        students_utils = getUtility(IStudentsUtils)
528        msg, mnumber = students_utils.setMatricNumber(self.context)
529        if msg:
530            self.flash(msg, type="danger")
531        else:
532            self.flash(_('Matriculation number %s assigned.' % mnumber))
533            self.context.writeLogMessage(self, '%s assigned' % mnumber)
534        self.redirect(self.url(self.context))
535        return
536
537    def render(self):
538        return
539
540class ExportPDFMatricNumberSlip(UtilityView, grok.View):
541    """Deliver a PDF notification slip.
542    """
543    grok.context(ICustomStudent)
544    grok.name('matric_number_slip.pdf')
545    grok.require('waeup.viewStudent')
546    prefix = 'form'
547
548    form_fields = grok.AutoFields(ICustomStudent).select(
549        'student_id', 'matric_number')
550    omit_fields = ('date_of_birth', 'current_level', 'flash_notice')
551
552    @property
553    def title(self):
554        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
555        return translate(_('Matriculation Number'), 'waeup.kofa',
556            target_language=portal_language)
557
558    @property
559    def label(self):
560        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
561        return translate(_('Matriculation Number Slip\n'),
562            target_language=portal_language) \
563            + ' %s' % self.context.display_fullname
564
565    def render(self):
566        if self.context.state not in (PAID,) or not self.context.is_fresh \
567            or not self.context.matric_number:
568            self.flash('Not allowed.', type="danger")
569            self.redirect(self.url(self.context))
570            return
571        students_utils = getUtility(IStudentsUtils)
572        pre_text = _('Congratulations! Your acceptance fee and school fees ' +
573                     'payments have been received and your matriculation ' +
574                     'number generated with details as follows.')
575        return students_utils.renderPDFAdmissionLetter(self,
576            self.context.student, omit_fields=self.omit_fields,
577            pre_text=pre_text, post_text='')
578
579class ExportPersonalDataSlip(UtilityView, grok.View):
580    """Deliver a PDF notification slip.
581    """
582    grok.context(ICustomStudent)
583    grok.name('personal_data_slip.pdf')
584    grok.require('waeup.viewStudent')
585    prefix = 'form'
586    note = None
587
588    form_fields = grok.AutoFields(ICustomStudentPersonal).omit('personal_updated')
589    omit_fields = ('suspended', 'suspended_comment', 'adm_code',
590                   'certificate', 'flash_notice')
591
592    @property
593    def title(self):
594        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
595        return translate(_('Personal Data'), 'waeup.kofa',
596            target_language=portal_language)
597
598    @property
599    def label(self):
600        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
601        return translate(_('Personal Data Slip\n'),
602            target_language=portal_language) \
603            + ' %s' % self.context.display_fullname
604
605    def render(self):
606        studentview = StudentBasePDFFormPage(self.context.student,
607            self.request, self.omit_fields)
608        students_utils = getUtility(IStudentsUtils)
609        return students_utils.renderPDF(self, 'personal_data_slip.pdf',
610            self.context.student, studentview, note=self.note,
611            omit_fields=self.omit_fields)
612
613class CustomAccommodationManageFormPage(NigeriaAccommodationManageFormPage):
614    """ Page to manage bed tickets.
615    This manage form page is for both students and students officers.
616    """
617    with_hostel_selection = True
618
619class CustomBedTicketAddPage(BedTicketAddPage):
620    with_ac = False
621
622class CustomStudentFilesUploadPage(StudentFilesUploadPage):
623    """ View to upload files by student. Inherit from same class in
624    base package, not from kofacustom.nigeria which
625    requires that no application slip exists.
626    """
627
628class CustomCourseTicketDisplayFormPage(CourseTicketDisplayFormPage):
629    """ Page to display course tickets
630    """
631    form_fields = grok.AutoFields(ICustomCourseTicket)
632
633class CustomCourseTicketManageFormPage(CourseTicketManageFormPage):
634    """ Page to manage course tickets
635    """
636    form_fields = grok.AutoFields(ICustomCourseTicket)
637    form_fields['title'].for_display = True
638    form_fields['fcode'].for_display = True
639    form_fields['dcode'].for_display = True
640    form_fields['semester'].for_display = True
641    form_fields['passmark'].for_display = True
642    form_fields['credits'].for_display = True
643    form_fields['mandatory'].for_display = False
644    form_fields['automatic'].for_display = True
645    form_fields['carry_over'].for_display = True
646
647class CustomEditScoresPage(EditScoresPage):
648    """Page that filters and lists students.
649    """
650    grok.template('editscorespage')
651
652
653    def _extract_uploadfile(self, uploadfile):
654        """Get a mapping of student-ids to scores.
655
656        The mapping is constructed by reading contents from `uploadfile`.
657
658        We expect uploadfile to be a regular CSV file with columns
659        ``student_id``, ``score`` and ``ca`` (other cols are ignored).
660        """
661        result = dict()
662        data = StringIO(uploadfile.read())  # ensure we have something seekable
663        reader = csv.DictReader(data)
664        for row in reader:
665            if not ('student_id' in row and 'score' in row and 'ca' in row):
666                continue
667            result[row['student_id']] = (row['score'], row['ca'])
668        return result
669
670    def update(self,  *args, **kw):
671        form = self.request.form
672        ob_class = self.__implemented__.__name__.replace('waeup.kofa.', '')
673        self.current_academic_session = grok.getSite()[
674            'configuration'].current_academic_session
675        if self.context.__parent__.__parent__.score_editing_disabled:
676            self.flash(_('Score editing disabled.'), type="warning")
677            self.redirect(self.url(self.context))
678            return
679        if not self.current_academic_session:
680            self.flash(_('Current academic session not set.'), type="warning")
681            self.redirect(self.url(self.context))
682            return
683        self.session_title = academic_sessions_vocab.getTerm(
684            self.current_academic_session).title
685        self.tickets = self._searchCatalog(self.current_academic_session)
686        self.editable_tickets = [
687            ticket for ticket in self.tickets if ticket.editable_by_lecturer]
688        if not self.tickets:
689            self.flash(_('No student found.'), type="warning")
690            self.redirect(self.url(self.context))
691            return
692        if not 'UPDATE_TABLE' in form and not 'UPDATE_FILE' in form:
693            return
694
695        if not self.editable_tickets:
696            return
697        if 'UPDATE_FILE' in form:
698            if form['uploadfile']:
699                try:
700                    formvals = self._extract_uploadfile(form['uploadfile'])
701                except:
702                    self.flash(
703                        _('Uploaded file contains illegal data. Ignored'),
704                        type="danger")
705                    return
706            else:
707                self.flash(
708                    _('No file provided.'), type="danger")
709                return
710        else:
711            formvals = dict(zip(form['sids'], zip(form['scores'], form['cas'])))
712        error = ''
713        # In AAUE only editable tickets are shown, see also customized
714        # pagetemplate
715        for ticket in self.editable_tickets:
716            ticket_error = False
717            score = ticket.score
718            ca = ticket.ca
719            sid = ticket.student.student_id
720            if formvals[sid][0] == '':
721                score = None
722            if formvals[sid][1] == '':
723                ca = None
724            try:
725                if formvals[sid][0]:
726                    score = int(formvals[sid][0])
727                if formvals[sid][1]:
728                    ca = int(formvals[sid][1])
729            except ValueError:
730                error += '%s, ' % ticket.student.display_fullname
731                ticket_error = True
732            if not ticket_error and ticket.score != score:
733                try:
734                    ticket.score = score
735                except TooBig:
736                    error += '%s, ' % ticket.student.display_fullname
737                    ticket_error = True
738                    pass
739                ticket.student.__parent__.logger.info(
740                    '%s - %s %s/%s score updated (%s)' %
741                    (ob_class, ticket.student.student_id,
742                     ticket.level, ticket.code, score))
743            if not ticket_error and ticket.ca != ca:
744                try:
745                    ticket.ca = ca
746                except TooBig:
747                    error += '%s, ' % ticket.student.display_fullname
748                    pass
749                ticket.student.__parent__.logger.info(
750                    '%s - %s %s/%s ca updated (%s)' %
751                    (ob_class, ticket.student.student_id,
752                     ticket.level, ticket.code, ca))
753        if error:
754            self.flash(_('Error: Score(s) and CA(s) of %s have not be updated. '
755              % error.strip(', ')), type="danger")
756            return
757        self.flash(_('You successfully updated course results.'))
758        return
759
760class CustomExportPDFScoresSlip(ExportPDFScoresSlip):
761    """Deliver a PDF slip of course tickets for a lecturer.
762    """
763
764    def table_data(self, session):
765        cat = queryUtility(ICatalog, name='coursetickets_catalog')
766        coursetickets = cat.searchResults(
767            session=(session, session),
768            code=(self.context.code, self.context.code)
769            )
770        # In AAUE only editable tickets can be printed
771        editable_tickets = [
772            ticket for ticket in coursetickets if ticket.editable_by_lecturer]
773        header = [[_(''),
774                   _('Matric No.'),
775                   _('Reg. No.'),
776                   _('Fullname'),
777                   _('Status'),
778                   _('Course of\nStudies'),
779                   _('Level'),
780                   _('Exam\nScore'),
781                   _(' CA  '),
782                   _('Total '),
783                   _('Grade'),
784                   ],]
785        tickets = []
786        no = 1
787        # In AAUE only editable tickets can be printed
788        for ticket in editable_tickets:
789            if None in (ticket.score, ticket.ca):
790                total = 'n/a'
791                grade = 'n/a'
792            else:
793                total = ticket.score + ticket.ca
794                grade = getGradeWeightFromScore(total)[0]
795            fullname = textwrap.fill(ticket.student.display_fullname, 30)
796            row = [no,
797                  ticket.student.matric_number,
798                  ticket.student.reg_number,
799                  fullname,
800                  ticket.student.translated_state,
801                  ticket.student.certcode,
802                  ticket.level,
803                  ticket.ca,
804                  ticket.score,
805                  total,
806                  grade,
807                  ]
808            tickets.append(row)
809            no += 1
810        return header + sorted(tickets,
811            key=lambda value: value[5] + value[3] + str(value[6]))
Note: See TracBrowser for help on using the repository browser.