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

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

Change introductory text of examination slip.

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