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

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

Define and use ICustomUGStudentClearanceEdit.

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