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

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

Implement ExportExaminationScheduleSlip?.

  • Property svn:keywords set to Id
File size: 39.2 KB
RevLine 
[8911]1## $Id: browser.py 14661 2017-03-30 07:51:15Z 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
[14288]30from waeup.kofa.interfaces import (
[14306]31    IKofaUtils, academic_sessions_vocab, ICSVExporter, IKofaObject)
32from waeup.kofa.students.interfaces import (
33    IStudentsUtils, IStudent, IStudentRequestPW)
[14000]34from waeup.kofa.students.workflow import PAID, REGISTERED, RETURNING
[9914]35from waeup.kofa.students.browser import (
[11846]36    StartClearancePage,
[9914]37    StudentBasePDFFormPage,
38    CourseTicketAddFormPage,
39    StudyLevelDisplayFormPage,
[13834]40    StudyLevelManageFormPage,
41    StudyLevelEditFormPage,
[13059]42    ExportPDFTranscriptSlip,
43    ExportPDFAdmissionSlip,
[13353]44    BedTicketAddPage,
[13380]45    StudentFilesUploadPage,
[13523]46    PaymentsManageFormPage,
[13770]47    CourseTicketDisplayFormPage,
48    CourseTicketManageFormPage,
[13900]49    EditScoresPage,
[14165]50    ExportPDFScoresSlip,
51    StudyCourseTranscriptPage,
[14288]52    DownloadScoresView,
[14534]53    StudentRequestPasswordPage,
54    StudyCourseManageFormPage
[10269]55    )
[8911]56from kofacustom.nigeria.students.browser import (
57    NigeriaOnlinePaymentDisplayFormPage,
58    NigeriaOnlinePaymentAddFormPage,
[13059]59    NigeriaExportPDFPaymentSlip,
60    NigeriaExportPDFCourseRegistrationSlip,
[13351]61    NigeriaStudentPersonalDisplayFormPage,
62    NigeriaStudentPersonalEditFormPage,
63    NigeriaStudentPersonalManageFormPage,
[14084]64    NigeriaStudentClearanceDisplayFormPage,
65    NigeriaExportPDFClearanceSlip,
66    NigeriaStudentClearanceManageFormPage,
[13362]67    NigeriaStudentClearanceEditFormPage,
[13462]68    NigeriaAccommodationManageFormPage,
[13795]69    NigeriaStudentBaseDisplayFormPage,
[14165]70    NigeriaStudentBaseManageFormPage
[10269]71    )
[9496]72from waeup.aaue.students.interfaces import (
[9914]73    ICustomStudentOnlinePayment,
[11607]74    ICustomStudentStudyLevel,
[13351]75    ICustomStudent,
76    ICustomStudentPersonal,
[13362]77    ICustomStudentPersonalEdit,
[13770]78    ICustomUGStudentClearance,
[14298]79    ICustomUGStudentClearanceEdit,
[14084]80    ICustomPGStudentClearance,
[13795]81    ICustomCourseTicket,
[14534]82    ICustomStudentBase,
83    ICustomStudentStudyCourse)
[13414]84from waeup.aaue.interswitch.browser import gateway_net_amt
[9914]85from waeup.aaue.interfaces import MessageFactory as _
[8911]86
[14306]87grok.context(IKofaObject)  # Make IKofaObject the default context
88
[13795]89class CustomStudentBaseDisplayFormPage(NigeriaStudentBaseDisplayFormPage):
90    """ Page to display student base data
91    """
92    form_fields = grok.AutoFields(ICustomStudentBase).omit(
93        'password', 'suspended', 'suspended_comment', 'flash_notice')
94    form_fields[
95        'financial_clearance_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
96
97class CustomStudentBaseManageFormPage(NigeriaStudentBaseManageFormPage):
98    """ View to manage student base data
99    """
100    form_fields = grok.AutoFields(ICustomStudentBase).omit(
101        'student_id', 'adm_code', 'suspended',
102        'financially_cleared_by', 'financial_clearance_date')
103
[13351]104class CustomStudentPersonalDisplayFormPage(NigeriaStudentPersonalDisplayFormPage):
105    """ Page to display student personal data
106    """
107    form_fields = grok.AutoFields(ICustomStudentPersonal)
108    form_fields['perm_address'].custom_widget = BytesDisplayWidget
109    form_fields['father_address'].custom_widget = BytesDisplayWidget
110    form_fields['mother_address'].custom_widget = BytesDisplayWidget
111    form_fields['next_kin_address'].custom_widget = BytesDisplayWidget
112    form_fields[
113        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
114
115class CustomStudentPersonalEditFormPage(NigeriaStudentPersonalEditFormPage):
116    """ Page to edit personal data
117    """
118    form_fields = grok.AutoFields(ICustomStudentPersonalEdit).omit('personal_updated')
119
120class CustomStudentPersonalManageFormPage(NigeriaStudentPersonalManageFormPage):
121    """ Page to edit personal data
122    """
123    form_fields = grok.AutoFields(ICustomStudentPersonal)
124    form_fields['personal_updated'].for_display = True
125    form_fields[
126        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
127
[14661]128
129class ExportExaminationScheduleSlip(UtilityView, grok.View):
130    """Deliver a examination schedule slip.
131
132    This form page is available only in Uniben and AAUE.
133    """
134    grok.context(ICustomStudent)
135    grok.name('examination_schedule_slip.pdf')
136    grok.require('waeup.viewStudent')
137    prefix = 'form'
138
139    label = u'Examination Schedule Slip'
140
141    omit_fields = (
142        'suspended', 'phone', 'email',
143        'adm_code', 'suspended_comment',
144        'date_of_birth', 'current_level',
145        'current_mode',
146        'entry_session',
147        'flash_notice')
148
149    form_fields = []
150
151    @property
152    def note(self):
153        return """
154<br /><br /><br /><br /><font size='12'>
155Your examination date, time and venue is scheduled as follows:
156<br /><br />
157<strong>%s</strong>
158</font>
159
160""" % self.context.flash_notice
161        return
162
163
164    def update(self):
165        if not self.context.flash_notice \
166            or not 'exam' in self.context.flash_notice.lower():
167            self.flash(_('Forbidden'), type="warning")
168            self.redirect(self.url(self.context))
169
170    def render(self):
171        studentview = StudentBasePDFFormPage(self.context.student,
172            self.request, self.omit_fields)
173        students_utils = getUtility(IStudentsUtils)
174        return students_utils.renderPDF(
175            self, 'examination_schedule_slip',
176            self.context.student, studentview,
177            omit_fields=self.omit_fields,
178            note=self.note)
179
[14084]180class CustomStudentClearanceDisplayFormPage(NigeriaStudentClearanceDisplayFormPage):
181    """ Page to display student clearance data
182    """
183
184    @property
185    def form_fields(self):
186        if self.context.is_postgrad:
187            form_fields = grok.AutoFields(
188                ICustomPGStudentClearance).omit('clearance_locked')
189        else:
190            form_fields = grok.AutoFields(
191                ICustomUGStudentClearance).omit('clearance_locked')
192        if not getattr(self.context, 'officer_comment'):
193            form_fields = form_fields.omit('officer_comment')
194        else:
195            form_fields['officer_comment'].custom_widget = BytesDisplayWidget
[14104]196        form_fields = form_fields.omit('def_adm')
[14084]197        return form_fields
198
199class CustomStudentClearanceManageFormPage(NigeriaStudentClearanceManageFormPage):
200    """ Page to edit student clearance data
201    """
202
203    @property
204    def form_fields(self):
205        if self.context.is_postgrad:
206            form_fields = grok.AutoFields(
207                ICustomPGStudentClearance).omit('clr_code')
208        else:
209            form_fields = grok.AutoFields(
210                ICustomUGStudentClearance).omit('clr_code')
[14104]211        form_fields = form_fields.omit('def_adm')
[14084]212        return form_fields
213
[13362]214class CustomStudentClearanceEditFormPage(NigeriaStudentClearanceEditFormPage):
215    """ View to edit student clearance data by student
216    """
217
218    @property
219    def form_fields(self):
220        if self.context.is_postgrad:
221            form_fields = grok.AutoFields(ICustomPGStudentClearance).omit(
222            'clearance_locked', 'nysc_location', 'clr_code', 'officer_comment',
223            'physical_clearance_date')
224        else:
[14298]225            form_fields = grok.AutoFields(ICustomUGStudentClearanceEdit).omit(
[13362]226            'clearance_locked', 'clr_code', 'officer_comment',
[14104]227            'physical_clearance_date', 'date_of_birth', 'nationality', 'lga')
228        form_fields = form_fields.omit('def_adm')
[13362]229        return form_fields
230
[11846]231class CustomStartClearancePage(StartClearancePage):
[13360]232    with_ac = False
[11846]233
[13351]234    @property
235    def all_required_fields_filled(self):
236        if not self.context.email:
237            return _("Email address is missing."), 'edit_base'
238        if not self.context.phone:
239            return _("Phone number is missing."), 'edit_base'
240        if not self.context.father_name:
241            return _("Personal data form is not properly filled."), 'edit_personal'
242        return
243
[8911]244class CustomOnlinePaymentDisplayFormPage(NigeriaOnlinePaymentDisplayFormPage):
245    """ Page to view an online payment ticket
246    """
247    grok.context(ICustomStudentOnlinePayment)
[9853]248    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
[9990]249        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
[8911]250    form_fields[
251        'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
252    form_fields[
253        'payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
254
255class CustomOnlinePaymentAddFormPage(NigeriaOnlinePaymentAddFormPage):
256    """ Page to add an online payment ticket
257    """
258    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).select(
259        'p_category')
260
[14527]261    ALUMNI_PAYMENT_CATS =  {
[14296]262        'transcript_local': 'Transcript Fee Local',
263        'transcript_inter': 'Transcript Fee International',
264        }
265
[14533]266    REDUCED_PAYMENT_CATS =  {
[14527]267        'clearance': 'Acceptance Fee',
268        'schoolfee': 'School Fee',
269        }
270
[14296]271    @property
272    def selectable_categories(self):
273        if 'alumni' in self.application_url():
[14527]274            return self.ALUMNI_PAYMENT_CATS.items()
[14573]275        if self.context.student.current_mode in (
276            'ijmbe', 'special_pg_ft', 'special_pg_pt', 'found') :
[14533]277            return self.REDUCED_PAYMENT_CATS.items()
[14296]278        categories = getUtility(IKofaUtils).SELECTABLE_PAYMENT_CATEGORIES
279        return sorted(categories.items())
280
[13523]281class CustomPaymentsManageFormPage(PaymentsManageFormPage):
282    """ Page to manage the student payments.
283
284    This manage form page is for both students and students officers.
285    """
286    @property
287    def manage_payments_allowed(self):
288        return checkPermission('waeup.manageStudent', self.context)
289
[13059]290class CustomExportPDFPaymentSlip(NigeriaExportPDFPaymentSlip):
[8911]291    """Deliver a PDF slip of the context.
292    """
293    grok.context(ICustomStudentOnlinePayment)
[9853]294    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
[9990]295        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
[8911]296    form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
[9496]297    form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
[9914]298
[11625]299    @property
300    def note(self):
[13408]301        p_session = self.context.p_session
[13405]302        try:
[13408]303            academic_session = grok.getSite()['configuration'][str(p_session)]
[13405]304        except KeyError:
305            academic_session = None
[13425]306        text =  '\n\n The Amount Authorized is inclusive of: '
[13512]307        if self.context.p_category in ('schoolfee_incl', 'schoolfee_1') \
308            and academic_session:
[14385]309            #welfare_fee = gateway_net_amt(academic_session.welfare_fee)
310            #union_fee = gateway_net_amt(academic_session.union_fee)
[14244]311            if self.context.student.entry_session == 2016 \
312                and self.context.student.entry_mode == 'ug_ft' \
313                and self.context.p_session == 2016:
314                # Add student id card fee to first school fee payment.
[14385]315
316                ## Attention: The payment slip does not contain any information
317                ## whether the fee was added or not.
318                ## We can only draw conclusions from from the student's entry
319                ## session whether the fee had been included.
320                #id_card_fee = gateway_net_amt(academic_session.id_card_fee)
321                #text += ('School Fee, '
322                #         '%s Naira Student ID Card Fee, '
323                #         '%s Naira Student Union Dues, '
324                #         '%s Naira Student Welfare Assurance Fee and '
325                #         % (id_card_fee, union_fee, welfare_fee))
326
[14244]327                text += ('School Fee, '
[14385]328                         'Student ID Card Fee, '
329                         'Student Union Dues, '
330                         'Student Welfare Assurance Fee and ')
[14244]331            else:
[14385]332
333                #text += ('School Fee, '
334                #         '%s Naira Student Union Dues, '
335                #         '%s Naira Student Welfare Assurance Fee and '
336                #         % (union_fee, welfare_fee))
337
[14244]338                text += ('School Fee, '
[14385]339                         'Student Union Dues, '
340                         'Student Welfare Assurance Fee and ')
[13410]341        elif self.context.p_category in (
342            'clearance_incl', 'clearance_medical_incl') and academic_session:
[14385]343
344            #matric_gown_fee = gateway_net_amt(academic_session.matric_gown_fee)
345            #lapel_fee = gateway_net_amt(academic_session.lapel_fee)
346            #text += ('Acceptance Fee, '
347            #         '%s Naira Matriculation Gown Fee, '
348            #         '%s Naira Lapel/File Fee and '
349            #         % (matric_gown_fee, lapel_fee))
350
[13437]351            text += ('Acceptance Fee, '
[14385]352                     'Matriculation Gown Fee, '
353                     'Lapel/File Fee and ')
[11625]354
[14385]355        #return text + '250.0 Naira Transaction Charge.'
356
357        return text + 'Transaction Charge.'
358
[14534]359class CustomStudyCourseManageFormPage(StudyCourseManageFormPage):
360    """ Page to edit the student study course data
361    """
362    grok.context(ICustomStudentStudyCourse)
363
364    @property
365    def form_fields(self):
366        if self.context.is_postgrad:
367            form_fields = grok.AutoFields(ICustomStudentStudyCourse).omit(
368                'previous_verdict')
369        else:
370            form_fields = grok.AutoFields(ICustomStudentStudyCourse)
371        form_fields['imported_cgpa'].for_display = True
372        return form_fields
373
[9914]374class CustomStudyLevelDisplayFormPage(StudyLevelDisplayFormPage):
375    """ Page to display student study levels
376    """
377    grok.context(ICustomStudentStudyLevel)
[10480]378    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
[14206]379        'total_credits', 'gpa', 'level', 'imported_gpa', 'imported_cgpa')
[9914]380    form_fields[
381        'validation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
382
[14000]383    @property
384    def show_results(self):
385        isStudent = getattr(
386            self.request.principal, 'user_type', None) == 'student'
[14302]387        # Temporarily disabled on 1/12/2016
388        if isStudent:
[14000]389            return False
[14302]390        #if isStudent and self.context.student.state != RETURNING \
391        #    and self.context.student.current_level == self.context.level:
392        #    return False
[14000]393        return True
394
[13834]395class CustomStudyLevelManageFormPage(StudyLevelManageFormPage):
396    """ Page to edit the student study level data
397    """
398    grok.context(ICustomStudentStudyLevel)
399
[14534]400    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
401        'validation_date', 'validated_by', 'total_credits', 'gpa', 'level',
402        'total_credits_s1', 'total_credits_s2')
403
404    form_fields['imported_gpa'].for_display = True
405    form_fields['imported_cgpa'].for_display = True
406
[13834]407class CustomStudyLevelEditFormPage(StudyLevelEditFormPage):
408    """ Page to edit the student study level data by students
409    """
410    grok.context(ICustomStudentStudyLevel)
411
[13059]412class CustomExportPDFCourseRegistrationSlip(
413    NigeriaExportPDFCourseRegistrationSlip):
[9914]414    """Deliver a PDF slip of the context.
415    """
416    grok.context(ICustomStudentStudyLevel)
417    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
[10102]418        'level_session', 'level_verdict',
[14206]419        'validated_by', 'validation_date', 'gpa', 'level',
420        'imported_gpa', 'imported_cgpa')
[9914]421
[10269]422    omit_fields = ('password', 'suspended', 'suspended_comment',
[10689]423        'phone', 'adm_code', 'sex', 'email', 'date_of_birth',
[13713]424        'department', 'current_mode', 'current_level', 'flash_notice')
[10269]425
[14000]426    @property
427    def show_results(self):
428        isStudent = getattr(
429            self.request.principal, 'user_type', None) == 'student'
[14302]430        # Temporarily disabled on 1/12/2016
431        if isStudent:
432            return False
433        #if isStudent and self.context.student.state != RETURNING \
434        #    and self.context.student.current_level == self.context.level:
435        #    return False
436        return True
[14000]437
[13038]438    def update(self):
439        if self.context.student.state != REGISTERED \
[13051]440            and self.context.student.current_level == self.context.level:
[13038]441            self.flash(_('Forbidden'), type="warning")
442            self.redirect(self.url(self.context))
[14000]443            return
[13038]444
[9914]445    @property
446    def label(self):
447        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
448        lang = self.request.cookies.get('kofa.language', portal_language)
449        level_title = translate(self.context.level_title, 'waeup.kofa',
450            target_language=lang)
451        line0 = ''
[13788]452        if self.context.student.is_postgrad:
453            line0 = 'SCHOOL OF POSTGRADUATE STUDIES\n'
454        elif self.context.student.current_mode.endswith('_pt'):
[9914]455            line0 = 'DIRECTORATE OF PART-TIME DEGREE PROGRAMMES\n'
[13866]456        line1 = translate(_('Course Registration Slip'),
457            target_language=portal_language) \
[9914]458            + ' %s' % level_title
[13866]459        line2 = translate(_('Session'),
460            target_language=portal_language) \
[9914]461            + ' %s' % self.context.getSessionString
462        return '%s%s\n%s' % (line0, line1, line2)
463
464    @property
465    def title(self):
466        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]467        return translate(_('Units Registered'), target_language=portal_language)
[9914]468
469    def _signatures(self):
[13647]470        if self.context.student.current_mode.endswith('_pt') \
471            or self.context.student.current_mode == 'found':
472            return (
473                [('I have selected the course on the advise of my Head of '
474                 'Department. <br>', _('Student\'s Signature'), '<br>')],
475                [('This student has satisfied the department\'s requirements. '
476                 'I recommend to approve the course registration. <br>',
477                 _('Head of Department\'s Signature'), '<br>')],
[13946]478                [('' , _('Deputy Registrar\'s Signature'), '<br>')],
[13647]479                [('', _('Director\'s Signature'))]
480                )
481        if self.context.student.current_mode in (
482            'de_ft', 'ug_ft', 'dp_ft', 'transfer'):
[13649]483            return ([_('Academic Adviser\'s Signature'),
484                _('Faculty Officer\'s Signature'),
485                _('Student\'s Signature')],)
486
[13647]487        if self.context.student.current_mode in ('special_pg_ft', 'special_pg_pt'):
488            return (
[13676]489                [('I declare that all items of information supplied above are correct:' ,
[13680]490                    _('Student\'s Signature'), '<br>')],
[13676]491                [('We approved the above registration:',
[13680]492                    _('Major Supervisor (Name / Signature)'), '')],
493                [('', _('Co-Supervisor (Name / Signature)'), '')],
[13676]494                [('', _('Head of Department'), '<br>')],
495                [('The student has satisfied the conditions for renewal of '
496                  'registration for graduate school programme in this university:',
[13680]497                  _('Secretary <br /> (School of Postgraduate Studies)'), '')],
498                [('', _('Dean <br /> (School of Postgraduate Studies)'), '')],
[13647]499                )
500        return None
[9914]501
[13647]502
[9914]503    def render(self):
504        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]505        Sem = translate(_('Sem.'), target_language=portal_language)
506        Code = translate(_('Code'), target_language=portal_language)
507        Title = translate(_('Title'), target_language=portal_language)
508        Cred = translate(_('Cred.'), target_language=portal_language)
[14650]509        CC = translate(_('Cat.'), target_language=portal_language)
[14000]510        if self.show_results:
511            Score = translate(_('Score'), target_language=portal_language)
512            #CA = translate(_('CA'), target_language=portal_language)
513            Grade = translate(_('Grade'), target_language=portal_language)
[13866]514        Signature = translate(_('Lecturer\'s Signature'), 'waeup.aaue',
[9914]515            target_language=portal_language)
516        studentview = StudentBasePDFFormPage(self.context.student,
517            self.request, self.omit_fields)
518        students_utils = getUtility(IStudentsUtils)
[10442]519
520        tabledata = []
521        tableheader = []
522        contenttitle = []
523        for i in range(1,7):
524            tabledata.append(sorted(
525                [value for value in self.context.values() if value.semester == i],
526                key=lambda value: str(value.semester) + value.code))
[14000]527            if self.show_results:
528                tableheader.append([(Code,'code', 2.0),
529                                   (Title,'title', 7),
[14650]530                                   (Cred, 'credits', 1.4),
531                                   (CC, 'course_category', 1.2),
[14000]532                                   (Score, 'score', 1.4),
533                                   #(CA, 'ca', 1.4),
534                                   (Grade, 'grade', 1.4),
535                                   (Signature, 'dummy', 3),
536                                   ])
537            else:
538                tableheader.append([(Code,'code', 2.0),
539                                   (Title,'title', 7),
540                                   (Cred, 'credits', 1.5),
[14650]541                                   (CC, 'course_category', 1.2),
[14000]542                                   (Signature, 'dummy', 3),
543                                   ])
[9914]544        if len(self.label.split('\n')) == 3:
545            topMargin = 1.9
546        elif len(self.label.split('\n')) == 2:
547            topMargin = 1.7
548        else:
549            topMargin = 1.5
550        return students_utils.renderPDF(
551            self, 'course_registration_slip.pdf',
552            self.context.student, studentview,
[10442]553            tableheader=tableheader,
554            tabledata=tabledata,
[9914]555            signatures=self._signatures(),
[10269]556            topMargin=topMargin,
557            omit_fields=self.omit_fields
[9914]558            )
[10566]559
[14165]560class CustomStudyCourseTranscriptPage(StudyCourseTranscriptPage):
561    """ Page to display the student's transcript.
562    """
563    grok.require('waeup.viewStudent')
564
[13059]565class CustomExportPDFTranscriptSlip(ExportPDFTranscriptSlip):
[10566]566    """Deliver a PDF slip of the context.
567    """
[14315]568#    grok.require('waeup.viewStudent')
[10566]569
[14267]570    note = _("""
571<br /><br /><br /><br />
572<font size='10'>
573<strong>Note:</strong> This copy is subject to correction for typographical errors and ratification by the departmental board.
574</font>
575""")
576
[10566]577    def _sigsInFooter(self):
578        return []
579
580    def _signatures(self):
581        return ([(
[14136]582            'Mrs. Uniamikogbo, S.O., mnim, manupa <br /> Prin. Asst Registrar  <br /> '
[11555]583            'Exams, Records and Data Processing Division <br /> For: Registrar')],)
[10922]584
[13834]585    def render(self):
586        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]587        Term = translate(_('Sem.'), target_language=portal_language)
588        Code = translate(_('Code'), target_language=portal_language)
589        Title = translate(_('Title'), target_language=portal_language)
590        Cred = translate(_('Credits'), target_language=portal_language)
591        Score = translate(_('Score'), target_language=portal_language)
592        Grade = translate(_('Grade'), target_language=portal_language)
[13834]593        studentview = StudentBasePDFFormPage(self.context.student,
594            self.request, self.omit_fields)
595        students_utils = getUtility(IStudentsUtils)
596
597        tableheader = [(Code,'code', 2.5),
598                         (Title,'title', 7),
599                         (Term, 'semester', 1.5),
600                         (Cred, 'credits', 1.5),
[14136]601                         (Score, 'total_score', 1.5),
[13834]602                         (Grade, 'grade', 1.5),
603                         ]
604
605        return students_utils.renderPDFTranscript(
606            self, 'transcript.pdf',
607            self.context.student, studentview,
608            omit_fields=self.omit_fields,
609            tableheader=tableheader,
610            signatures=self._signatures(),
611            sigs_in_footer=self._sigsInFooter(),
[14295]612            note = self.note,
613            no_passport=True
[13834]614            )
615
[13059]616class CustomExportPDFAdmissionSlip(ExportPDFAdmissionSlip):
[10922]617    """Deliver a PDF Admission slip.
618    """
619
620    @property
621    def label(self):
622        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]623        return translate(_('e-Admission Slip \n'),
624            target_language=portal_language) \
[10922]625            + ' %s' % self.context.display_fullname
[11597]626
[13059]627class CustomExportPDFClearanceSlip(NigeriaExportPDFClearanceSlip):
[11606]628    """Deliver a PDF slip of the context.
629    """
630
631    @property
632    def label(self):
633        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[14114]634        return translate(_('Verification/Clearance Slip\n'),
[13866]635            target_language=portal_language) \
[11606]636            + ' %s' % self.context.display_fullname
637
[14084]638    @property
639    def form_fields(self):
640        if self.context.is_postgrad:
641            form_fields = grok.AutoFields(
642                ICustomPGStudentClearance).omit('clearance_locked')
643        else:
644            form_fields = grok.AutoFields(
645                ICustomUGStudentClearance).omit('clearance_locked')
646        if not getattr(self.context, 'officer_comment'):
647            form_fields = form_fields.omit('officer_comment')
[14104]648        form_fields = form_fields.omit('def_adm')
[14084]649        return form_fields
650
[11597]651class StudentGetMatricNumberPage(UtilityView, grok.View):
652    """ Construct and set the matriculation number.
653    """
654    grok.context(IStudent)
655    grok.name('get_matric_number')
656    grok.require('waeup.viewStudent')
657
658    def update(self):
659        students_utils = getUtility(IStudentsUtils)
660        msg, mnumber = students_utils.setMatricNumber(self.context)
661        if msg:
662            self.flash(msg, type="danger")
663        else:
664            self.flash(_('Matriculation number %s assigned.' % mnumber))
[11602]665            self.context.writeLogMessage(self, '%s assigned' % mnumber)
[11597]666        self.redirect(self.url(self.context))
667        return
668
669    def render(self):
[11607]670        return
671
[13059]672class ExportPDFMatricNumberSlip(UtilityView, grok.View):
[11607]673    """Deliver a PDF notification slip.
674    """
675    grok.context(ICustomStudent)
676    grok.name('matric_number_slip.pdf')
677    grok.require('waeup.viewStudent')
678    prefix = 'form'
679
680    form_fields = grok.AutoFields(ICustomStudent).select(
681        'student_id', 'matric_number')
[13713]682    omit_fields = ('date_of_birth', 'current_level', 'flash_notice')
[11607]683
684    @property
[13489]685    def title(self):
686        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]687        return translate(_('Matriculation Number'), 'waeup.kofa',
[13489]688            target_language=portal_language)
689
690    @property
[11607]691    def label(self):
692        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]693        return translate(_('Matriculation Number Slip\n'),
694            target_language=portal_language) \
[11607]695            + ' %s' % self.context.display_fullname
696
697    def render(self):
698        if self.context.state not in (PAID,) or not self.context.is_fresh \
699            or not self.context.matric_number:
700            self.flash('Not allowed.', type="danger")
701            self.redirect(self.url(self.context))
702            return
703        students_utils = getUtility(IStudentsUtils)
[11609]704        pre_text = _('Congratulations! Your acceptance fee and school fees ' +
705                     'payments have been received and your matriculation ' +
706                     'number generated with details as follows.')
[11607]707        return students_utils.renderPDFAdmissionLetter(self,
708            self.context.student, omit_fields=self.omit_fields,
[13353]709            pre_text=pre_text, post_text='')
710
[13489]711class ExportPersonalDataSlip(UtilityView, grok.View):
712    """Deliver a PDF notification slip.
713    """
714    grok.context(ICustomStudent)
715    grok.name('personal_data_slip.pdf')
716    grok.require('waeup.viewStudent')
717    prefix = 'form'
718    note = None
719
720    form_fields = grok.AutoFields(ICustomStudentPersonal).omit('personal_updated')
[13713]721    omit_fields = ('suspended', 'suspended_comment', 'adm_code',
722                   'certificate', 'flash_notice')
[13489]723
724    @property
725    def title(self):
726        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]727        return translate(_('Personal Data'), 'waeup.kofa',
[13489]728            target_language=portal_language)
729
730    @property
731    def label(self):
732        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]733        return translate(_('Personal Data Slip\n'),
734            target_language=portal_language) \
[13489]735            + ' %s' % self.context.display_fullname
736
737    def render(self):
738        studentview = StudentBasePDFFormPage(self.context.student,
739            self.request, self.omit_fields)
740        students_utils = getUtility(IStudentsUtils)
741        return students_utils.renderPDF(self, 'personal_data_slip.pdf',
742            self.context.student, studentview, note=self.note,
743            omit_fields=self.omit_fields)
744
[13462]745class CustomAccommodationManageFormPage(NigeriaAccommodationManageFormPage):
746    """ Page to manage bed tickets.
747    This manage form page is for both students and students officers.
748    """
749    with_hostel_selection = True
750
[13353]751class CustomBedTicketAddPage(BedTicketAddPage):
[13360]752    with_ac = False
[13380]753
754class CustomStudentFilesUploadPage(StudentFilesUploadPage):
755    """ View to upload files by student. Inherit from same class in
756    base package, not from kofacustom.nigeria which
757    requires that no application slip exists.
[13770]758    """
759
760class CustomCourseTicketDisplayFormPage(CourseTicketDisplayFormPage):
761    """ Page to display course tickets
762    """
763
[14453]764    @property
765    def show_results(self):
766        isStudent = getattr(
767            self.request.principal, 'user_type', None) == 'student'
768        if isStudent:
769            return False
770        return True
771
772    @property
773    def form_fields(self):
774        if self.show_results:
775            return grok.AutoFields(ICustomCourseTicket)
776        else:
777            return grok.AutoFields(ICustomCourseTicket).omit('score').omit('ca')
778
[13770]779class CustomCourseTicketManageFormPage(CourseTicketManageFormPage):
780    """ Page to manage course tickets
781    """
782    form_fields = grok.AutoFields(ICustomCourseTicket)
783    form_fields['title'].for_display = True
784    form_fields['fcode'].for_display = True
785    form_fields['dcode'].for_display = True
786    form_fields['semester'].for_display = True
787    form_fields['passmark'].for_display = True
788    form_fields['credits'].for_display = True
789    form_fields['mandatory'].for_display = False
790    form_fields['automatic'].for_display = True
791    form_fields['carry_over'].for_display = True
792
793class CustomEditScoresPage(EditScoresPage):
794    """Page that filters and lists students.
795    """
796    grok.template('editscorespage')
797
[13937]798
799    def _extract_uploadfile(self, uploadfile):
800        """Get a mapping of student-ids to scores.
801
802        The mapping is constructed by reading contents from `uploadfile`.
803
804        We expect uploadfile to be a regular CSV file with columns
805        ``student_id``, ``score`` and ``ca`` (other cols are ignored).
806        """
807        result = dict()
808        data = StringIO(uploadfile.read())  # ensure we have something seekable
809        reader = csv.DictReader(data)
810        for row in reader:
811            if not ('student_id' in row and 'score' in row and 'ca' in row):
812                continue
813            result[row['student_id']] = (row['score'], row['ca'])
814        return result
815
[14288]816    def _update_scores(self, form):
[13937]817        ob_class = self.__implemented__.__name__.replace('waeup.kofa.', '')
[14288]818        error = ''
[13937]819        if 'UPDATE_FILE' in form:
820            if form['uploadfile']:
821                try:
822                    formvals = self._extract_uploadfile(form['uploadfile'])
823                except:
824                    self.flash(
825                        _('Uploaded file contains illegal data. Ignored'),
826                        type="danger")
[14288]827                    return False
[13937]828            else:
829                self.flash(
830                    _('No file provided.'), type="danger")
[14288]831                return False
[13937]832        else:
833            formvals = dict(zip(form['sids'], zip(form['scores'], form['cas'])))
[14149]834        for ticket in self.editable_tickets:
[13937]835            ticket_error = False
836            score = ticket.score
837            ca = ticket.ca
838            sid = ticket.student.student_id
839            if formvals[sid][0] == '':
840                score = None
841            if formvals[sid][1] == '':
842                ca = None
843            try:
844                if formvals[sid][0]:
845                    score = int(formvals[sid][0])
846                if formvals[sid][1]:
847                    ca = int(formvals[sid][1])
848            except ValueError:
849                error += '%s, ' % ticket.student.display_fullname
850                ticket_error = True
851            if not ticket_error and ticket.score != score:
[14113]852                try:
853                    ticket.score = score
854                except TooBig:
855                    error += '%s, ' % ticket.student.display_fullname
856                    ticket_error = True
857                    pass
[13937]858                ticket.student.__parent__.logger.info(
859                    '%s - %s %s/%s score updated (%s)' %
860                    (ob_class, ticket.student.student_id,
861                     ticket.level, ticket.code, score))
862            if not ticket_error and ticket.ca != ca:
[14113]863                try:
864                    ticket.ca = ca
865                except TooBig:
866                    error += '%s, ' % ticket.student.display_fullname
867                    pass
[13937]868                ticket.student.__parent__.logger.info(
869                    '%s - %s %s/%s ca updated (%s)' %
870                    (ob_class, ticket.student.student_id,
871                     ticket.level, ticket.code, ca))
872        if error:
873            self.flash(_('Error: Score(s) and CA(s) of %s have not be updated. '
[14113]874              % error.strip(', ')), type="danger")
[14288]875        return True
876
877class EditPreviousSessionScoresPage(CustomEditScoresPage):
878
879    grok.name('edit_prev_scores')
880
881    def update(self,  *args, **kw):
882        form = self.request.form
883        self.current_academic_session = grok.getSite()[
884            'configuration'].current_academic_session
885        if self.context.__parent__.__parent__.score_editing_disabled:
886            self.flash(_('Score editing disabled.'), type="warning")
887            self.redirect(self.url(self.context))
[13939]888            return
[14288]889        if not self.current_academic_session:
890            self.flash(_('Current academic session not set.'), type="warning")
891            self.redirect(self.url(self.context))
892            return
893        previous_session = self.current_academic_session - 1
894        self.session_title = academic_sessions_vocab.getTerm(
895            previous_session).title
896        self.tickets = self._searchCatalog(previous_session)
897        if not self.tickets:
898            self.flash(_('No student found.'), type="warning")
899            self.redirect(self.url(self.context))
900            return
901        self.editable_tickets = [
902            ticket for ticket in self.tickets if ticket.editable_by_lecturer]
903        if not 'UPDATE_TABLE' in form and not 'UPDATE_FILE' in form:
904            return
905        if not self.editable_tickets:
906            return
907        success = self._update_scores(form)
908        if success:
909            self.flash(_('You successfully updated course results.'))
[13900]910        return
911
912class CustomExportPDFScoresSlip(ExportPDFScoresSlip):
913    """Deliver a PDF slip of course tickets for a lecturer.
914    """
915
[14315]916    def data(self, session):
[13900]917        cat = queryUtility(ICatalog, name='coursetickets_catalog')
918        coursetickets = cat.searchResults(
919            session=(session, session),
920            code=(self.context.code, self.context.code)
921            )
[14149]922        # In AAUE only editable tickets can be printed
923        editable_tickets = [
924            ticket for ticket in coursetickets if ticket.editable_by_lecturer]
[13963]925        header = [[_(''),
926                   _('Matric No.'),
[13900]927                   _('Reg. No.'),
928                   _('Fullname'),
929                   _('Status'),
[13963]930                   _('Course of\nStudies'),
[13900]931                   _('Level'),
[13963]932                   _('Exam\nScore'),
[13964]933                   _(' CA  '),
934                   _('Total '),
[13963]935                   _('Grade'),
936                   ],]
[14288]937        sorted_tickets = sorted(editable_tickets,
938            key=lambda ticket: ticket.student.certcode +
939                ticket.student.display_fullname + str(ticket.level))
940        no = 1
[13907]941        tickets = []
[14315]942        passed = 0
943        failed = 0
[14149]944        # In AAUE only editable tickets can be printed
[14288]945        for ticket in sorted_tickets:
[14532]946            if ticket.total_score is None:
[13963]947                total = 'n/a'
[13964]948                grade = 'n/a'
[13963]949            else:
[14532]950                total = ticket.total_score
951                grade = ticket._getGradeWeightFromScore[0]
952                if grade in ('F', '-'):
[14315]953                    failed += 1
954                else:
955                    passed += 1
[13964]956            fullname = textwrap.fill(ticket.student.display_fullname, 30)
[13963]957            row = [no,
958                  ticket.student.matric_number,
[13900]959                  ticket.student.reg_number,
[13964]960                  fullname,
[13900]961                  ticket.student.translated_state,
962                  ticket.student.certcode,
963                  ticket.level,
[13963]964                  ticket.ca,
[13900]965                  ticket.score,
[13963]966                  total,
[13964]967                  grade,
[13963]968                  ]
[13907]969            tickets.append(row)
[13963]970            no += 1
[14317]971        total = passed + failed
[14320]972        passed_perc = 0
973        failed_perc = 0
[14317]974        if total:
[14320]975            passed_perc = 100 * passed / total
976            failed_perc = 100 * failed / total
977        return header + tickets, [
978            total, passed, passed_perc, failed, failed_perc]
[14288]979
980class DownloadPreviousSessionScoresView(DownloadScoresView):
981    """View that exports scores.
982    """
983    grok.name('download_prev_scores')
984
985    def update(self):
986        self.current_academic_session = grok.getSite()[
987            'configuration'].current_academic_session
988        if self.context.__parent__.__parent__.score_editing_disabled:
989            self.flash(_('Score editing disabled.'), type="warning")
990            self.redirect(self.url(self.context))
991            return
992        if not self.current_academic_session:
993            self.flash(_('Current academic session not set.'), type="warning")
994            self.redirect(self.url(self.context))
995            return
996        site = grok.getSite()
997        exporter = getUtility(ICSVExporter, name='lecturer')
998        self.csv = exporter.export_filtered(site, filepath=None,
999                                 catalog='coursetickets',
1000                                 session=self.current_academic_session-1,
1001                                 level=None,
1002                                 code=self.context.code)
[14306]1003        return
1004
1005class AlumniRequestPasswordPage(StudentRequestPasswordPage):
1006    """Captcha'd request password page for students.
1007    """
1008    grok.name('alumni_requestpw')
1009    grok.require('waeup.Anonymous')
1010    grok.template('alumni_requestpw')
1011    form_fields = grok.AutoFields(IStudentRequestPW).select(
1012        'lastname','number','email')
1013    label = _('Search student record and send password for first-time login')
1014
1015    def _redirect_no_student(self):
1016        self.flash(_('No student record found.'), type="warning")
1017        self.redirect(self.application_url() + '/applicants/trans2017/register')
[14288]1018        return
Note: See TracBrowser for help on using the repository browser.