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

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

Add imported_gpa and imported_cgpa fields and adjust exporters and batch processors.

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