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

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

verification/clearance slip

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