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

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

Remove 'special_pg_ft', 'special_pg_pt' from list.

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