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

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

Add sc_pin and sc_serial_number fields and customize all views.

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