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

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

Lecturers can edit current session and previous session courses whenever they like. All restrictions are removed.

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