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
RevLine 
[8911]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
[13937]19import csv
[13964]20import textwrap
[13937]21from cStringIO import StringIO
[8911]22from zope.i18n import translate
[13900]23from zope.component import getUtility, queryUtility
[14113]24from zope.schema.interfaces import TooBig, TooSmall
[13523]25from zope.security import checkPermission
[13900]26from zope.catalog.interfaces import ICatalog
[13351]27from zope.formlib.textwidgets import BytesDisplayWidget
[11597]28from waeup.kofa.browser.layout import UtilityView
[8911]29from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget
[13939]30from waeup.kofa.interfaces import IKofaUtils, academic_sessions_vocab
[11597]31from waeup.kofa.students.interfaces import IStudentsUtils, IStudent
[14000]32from waeup.kofa.students.workflow import PAID, REGISTERED, RETURNING
[13964]33from waeup.kofa.students.studylevel import getGradeWeightFromScore
[9914]34from waeup.kofa.students.browser import (
[11846]35    StartClearancePage,
[9914]36    StudentBasePDFFormPage,
37    CourseTicketAddFormPage,
38    StudyLevelDisplayFormPage,
[13834]39    StudyLevelManageFormPage,
40    StudyLevelEditFormPage,
[13059]41    ExportPDFTranscriptSlip,
42    ExportPDFAdmissionSlip,
[13353]43    BedTicketAddPage,
[13380]44    StudentFilesUploadPage,
[13523]45    PaymentsManageFormPage,
[13770]46    CourseTicketDisplayFormPage,
47    CourseTicketManageFormPage,
[13900]48    EditScoresPage,
[14165]49    ExportPDFScoresSlip,
50    StudyCourseTranscriptPage,
[10269]51    )
[8911]52from kofacustom.nigeria.students.browser import (
53    NigeriaOnlinePaymentDisplayFormPage,
54    NigeriaOnlinePaymentAddFormPage,
[13059]55    NigeriaExportPDFPaymentSlip,
56    NigeriaExportPDFCourseRegistrationSlip,
[13351]57    NigeriaStudentPersonalDisplayFormPage,
58    NigeriaStudentPersonalEditFormPage,
59    NigeriaStudentPersonalManageFormPage,
[14084]60    NigeriaStudentClearanceDisplayFormPage,
61    NigeriaExportPDFClearanceSlip,
62    NigeriaStudentClearanceManageFormPage,
[13362]63    NigeriaStudentClearanceEditFormPage,
[13462]64    NigeriaAccommodationManageFormPage,
[13795]65    NigeriaStudentBaseDisplayFormPage,
[14165]66    NigeriaStudentBaseManageFormPage
[10269]67    )
[9496]68from waeup.aaue.students.interfaces import (
[9914]69    ICustomStudentOnlinePayment,
[11607]70    ICustomStudentStudyLevel,
[13351]71    ICustomStudent,
72    ICustomStudentPersonal,
[13362]73    ICustomStudentPersonalEdit,
[13770]74    ICustomUGStudentClearance,
[14084]75    ICustomPGStudentClearance,
[13795]76    ICustomCourseTicket,
77    ICustomStudentBase)
[13414]78from waeup.aaue.interswitch.browser import gateway_net_amt
[9914]79from waeup.aaue.interfaces import MessageFactory as _
[8911]80
[13795]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
[13351]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
[14084]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
[14104]136        form_fields = form_fields.omit('def_adm')
[14084]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')
[14104]151        form_fields = form_fields.omit('def_adm')
[14084]152        return form_fields
153
[13362]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',
[14104]167            'physical_clearance_date', 'date_of_birth', 'nationality', 'lga')
168        form_fields = form_fields.omit('def_adm')
[13362]169        return form_fields
170
[11846]171class CustomStartClearancePage(StartClearancePage):
[13360]172    with_ac = False
[11846]173
[13351]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
[8911]184class CustomOnlinePaymentDisplayFormPage(NigeriaOnlinePaymentDisplayFormPage):
185    """ Page to view an online payment ticket
186    """
187    grok.context(ICustomStudentOnlinePayment)
[9853]188    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
[9990]189        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
[8911]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
[13523]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
[13059]210class CustomExportPDFPaymentSlip(NigeriaExportPDFPaymentSlip):
[8911]211    """Deliver a PDF slip of the context.
212    """
213    grok.context(ICustomStudentOnlinePayment)
[9853]214    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
[9990]215        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
[8911]216    form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
[9496]217    form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
[9914]218
[11625]219    @property
220    def note(self):
[13408]221        p_session = self.context.p_session
[13405]222        try:
[13408]223            academic_session = grok.getSite()['configuration'][str(p_session)]
[13405]224        except KeyError:
225            academic_session = None
[13425]226        text =  '\n\n The Amount Authorized is inclusive of: '
[13512]227        if self.context.p_category in ('schoolfee_incl', 'schoolfee_1') \
228            and academic_session:
[13414]229            welfare_fee = gateway_net_amt(academic_session.welfare_fee)
230            union_fee = gateway_net_amt(academic_session.union_fee)
[14244]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))
[13410]250        elif self.context.p_category in (
251            'clearance_incl', 'clearance_medical_incl') and academic_session:
[13414]252            matric_gown_fee = gateway_net_amt(academic_session.matric_gown_fee)
253            lapel_fee = gateway_net_amt(academic_session.lapel_fee)
[13437]254            text += ('Acceptance Fee, '
255                     '%s Naira Matriculation Gown Fee, '
256                     '%s Naira Lapel/File Fee and '
[14239]257                     % (matric_gown_fee, lapel_fee))
[13437]258        return text + '250.0 Naira Transaction Charge.'
[11625]259
[9914]260class CustomStudyLevelDisplayFormPage(StudyLevelDisplayFormPage):
261    """ Page to display student study levels
262    """
263    grok.context(ICustomStudentStudyLevel)
[10480]264    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
[14206]265        'total_credits', 'gpa', 'level', 'imported_gpa', 'imported_cgpa')
[9914]266    form_fields[
267        'validation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
268
[14000]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
[13834]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
[13059]288class CustomExportPDFCourseRegistrationSlip(
289    NigeriaExportPDFCourseRegistrationSlip):
[9914]290    """Deliver a PDF slip of the context.
291    """
292    grok.context(ICustomStudentStudyLevel)
293    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
[10102]294        'level_session', 'level_verdict',
[14206]295        'validated_by', 'validation_date', 'gpa', 'level',
296        'imported_gpa', 'imported_cgpa')
[9914]297
[10269]298    omit_fields = ('password', 'suspended', 'suspended_comment',
[10689]299        'phone', 'adm_code', 'sex', 'email', 'date_of_birth',
[13713]300        'department', 'current_mode', 'current_level', 'flash_notice')
[10269]301
[14000]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
[13038]310    def update(self):
311        if self.context.student.state != REGISTERED \
[13051]312            and self.context.student.current_level == self.context.level:
[13038]313            self.flash(_('Forbidden'), type="warning")
314            self.redirect(self.url(self.context))
[14000]315            return
[13038]316
[9914]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 = ''
[13788]324        if self.context.student.is_postgrad:
325            line0 = 'SCHOOL OF POSTGRADUATE STUDIES\n'
326        elif self.context.student.current_mode.endswith('_pt'):
[9914]327            line0 = 'DIRECTORATE OF PART-TIME DEGREE PROGRAMMES\n'
[13866]328        line1 = translate(_('Course Registration Slip'),
329            target_language=portal_language) \
[9914]330            + ' %s' % level_title
[13866]331        line2 = translate(_('Session'),
332            target_language=portal_language) \
[9914]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
[13866]339        return translate(_('Units Registered'), target_language=portal_language)
[9914]340
341    def _signatures(self):
[13647]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>')],
[13946]350                [('' , _('Deputy Registrar\'s Signature'), '<br>')],
[13647]351                [('', _('Director\'s Signature'))]
352                )
353        if self.context.student.current_mode in (
354            'de_ft', 'ug_ft', 'dp_ft', 'transfer'):
[13649]355            return ([_('Academic Adviser\'s Signature'),
356                _('Faculty Officer\'s Signature'),
357                _('Student\'s Signature')],)
358
[13647]359        if self.context.student.current_mode in ('special_pg_ft', 'special_pg_pt'):
360            return (
[13676]361                [('I declare that all items of information supplied above are correct:' ,
[13680]362                    _('Student\'s Signature'), '<br>')],
[13676]363                [('We approved the above registration:',
[13680]364                    _('Major Supervisor (Name / Signature)'), '')],
365                [('', _('Co-Supervisor (Name / Signature)'), '')],
[13676]366                [('', _('Head of Department'), '<br>')],
367                [('The student has satisfied the conditions for renewal of '
368                  'registration for graduate school programme in this university:',
[13680]369                  _('Secretary <br /> (School of Postgraduate Studies)'), '')],
370                [('', _('Dean <br /> (School of Postgraduate Studies)'), '')],
[13647]371                )
372        return None
[9914]373
[13647]374
[9914]375    def render(self):
376        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]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)
[14000]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)
[13866]385        Signature = translate(_('Lecturer\'s Signature'), 'waeup.aaue',
[9914]386            target_language=portal_language)
387        studentview = StudentBasePDFFormPage(self.context.student,
388            self.request, self.omit_fields)
389        students_utils = getUtility(IStudentsUtils)
[10442]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))
[14000]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                                   ])
[9914]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,
[10442]422            tableheader=tableheader,
423            tabledata=tabledata,
[9914]424            signatures=self._signatures(),
[10269]425            topMargin=topMargin,
426            omit_fields=self.omit_fields
[9914]427            )
[10566]428
[14165]429class CustomStudyCourseTranscriptPage(StudyCourseTranscriptPage):
430    """ Page to display the student's transcript.
431    """
432    grok.require('waeup.viewStudent')
433
[13059]434class CustomExportPDFTranscriptSlip(ExportPDFTranscriptSlip):
[10566]435    """Deliver a PDF slip of the context.
436    """
[14165]437    grok.require('waeup.viewStudent')
[10566]438
[14267]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
[10566]446    def _sigsInFooter(self):
447        return []
448
449    def _signatures(self):
450        return ([(
[14136]451            'Mrs. Uniamikogbo, S.O., mnim, manupa <br /> Prin. Asst Registrar  <br /> '
[11555]452            'Exams, Records and Data Processing Division <br /> For: Registrar')],)
[10922]453
[13834]454    def render(self):
455        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]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)
[13834]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),
[14136]470                         (Score, 'total_score', 1.5),
[13834]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(),
[14267]481            note = self.note
[13834]482            )
483
[13059]484class CustomExportPDFAdmissionSlip(ExportPDFAdmissionSlip):
[10922]485    """Deliver a PDF Admission slip.
486    """
487
488    @property
489    def label(self):
490        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]491        return translate(_('e-Admission Slip \n'),
492            target_language=portal_language) \
[10922]493            + ' %s' % self.context.display_fullname
[11597]494
[13059]495class CustomExportPDFClearanceSlip(NigeriaExportPDFClearanceSlip):
[11606]496    """Deliver a PDF slip of the context.
497    """
498
499    @property
500    def label(self):
501        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[14114]502        return translate(_('Verification/Clearance Slip\n'),
[13866]503            target_language=portal_language) \
[11606]504            + ' %s' % self.context.display_fullname
505
[14084]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')
[14104]516        form_fields = form_fields.omit('def_adm')
[14084]517        return form_fields
518
[11597]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))
[11602]533            self.context.writeLogMessage(self, '%s assigned' % mnumber)
[11597]534        self.redirect(self.url(self.context))
535        return
536
537    def render(self):
[11607]538        return
539
[13059]540class ExportPDFMatricNumberSlip(UtilityView, grok.View):
[11607]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')
[13713]550    omit_fields = ('date_of_birth', 'current_level', 'flash_notice')
[11607]551
552    @property
[13489]553    def title(self):
554        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]555        return translate(_('Matriculation Number'), 'waeup.kofa',
[13489]556            target_language=portal_language)
557
558    @property
[11607]559    def label(self):
560        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]561        return translate(_('Matriculation Number Slip\n'),
562            target_language=portal_language) \
[11607]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)
[11609]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.')
[11607]575        return students_utils.renderPDFAdmissionLetter(self,
576            self.context.student, omit_fields=self.omit_fields,
[13353]577            pre_text=pre_text, post_text='')
578
[13489]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')
[13713]589    omit_fields = ('suspended', 'suspended_comment', 'adm_code',
590                   'certificate', 'flash_notice')
[13489]591
592    @property
593    def title(self):
594        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]595        return translate(_('Personal Data'), 'waeup.kofa',
[13489]596            target_language=portal_language)
597
598    @property
599    def label(self):
600        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]601        return translate(_('Personal Data Slip\n'),
602            target_language=portal_language) \
[13489]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
[13462]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
[13353]619class CustomBedTicketAddPage(BedTicketAddPage):
[13360]620    with_ac = False
[13380]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.
[13770]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
[13937]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
[13770]670    def update(self,  *args, **kw):
671        form = self.request.form
[13937]672        ob_class = self.__implemented__.__name__.replace('waeup.kofa.', '')
[13770]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
[13939]683        self.session_title = academic_sessions_vocab.getTerm(
684            self.current_academic_session).title
[13770]685        self.tickets = self._searchCatalog(self.current_academic_session)
[14149]686        self.editable_tickets = [
[13770]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
[13937]692        if not 'UPDATE_TABLE' in form and not 'UPDATE_FILE' in form:
693            return
694
[14149]695        if not self.editable_tickets:
[13937]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")
[13770]709                return
[13937]710        else:
711            formvals = dict(zip(form['sids'], zip(form['scores'], form['cas'])))
712        error = ''
[14149]713        # In AAUE only editable tickets are shown, see also customized
714        # pagetemplate
715        for ticket in self.editable_tickets:
[13937]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:
[14113]733                try:
734                    ticket.score = score
735                except TooBig:
736                    error += '%s, ' % ticket.student.display_fullname
737                    ticket_error = True
738                    pass
[13937]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:
[14113]744                try:
745                    ticket.ca = ca
746                except TooBig:
747                    error += '%s, ' % ticket.student.display_fullname
748                    pass
[13937]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. '
[14113]755              % error.strip(', ')), type="danger")
[13939]756            return
757        self.flash(_('You successfully updated course results.'))
[13900]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            )
[14149]770        # In AAUE only editable tickets can be printed
771        editable_tickets = [
772            ticket for ticket in coursetickets if ticket.editable_by_lecturer]
[13963]773        header = [[_(''),
774                   _('Matric No.'),
[13900]775                   _('Reg. No.'),
776                   _('Fullname'),
777                   _('Status'),
[13963]778                   _('Course of\nStudies'),
[13900]779                   _('Level'),
[13963]780                   _('Exam\nScore'),
[13964]781                   _(' CA  '),
782                   _('Total '),
[13963]783                   _('Grade'),
784                   ],]
[13907]785        tickets = []
[13963]786        no = 1
[14149]787        # In AAUE only editable tickets can be printed
788        for ticket in editable_tickets:
[13963]789            if None in (ticket.score, ticket.ca):
790                total = 'n/a'
[13964]791                grade = 'n/a'
[13963]792            else:
793                total = ticket.score + ticket.ca
[13964]794                grade = getGradeWeightFromScore(total)[0]
795            fullname = textwrap.fill(ticket.student.display_fullname, 30)
[13963]796            row = [no,
797                  ticket.student.matric_number,
[13900]798                  ticket.student.reg_number,
[13964]799                  fullname,
[13900]800                  ticket.student.translated_state,
801                  ticket.student.certcode,
802                  ticket.level,
[13963]803                  ticket.ca,
[13900]804                  ticket.score,
[13963]805                  total,
[13964]806                  grade,
[13963]807                  ]
[13907]808            tickets.append(row)
[13963]809            no += 1
[13997]810        return header + sorted(tickets,
[14068]811            key=lambda value: value[5] + value[3] + str(value[6]))
Note: See TracBrowser for help on using the repository browser.