source: main/waeup.uniben/trunk/src/waeup/uniben/students/browser.py @ 17974

Last change on this file since 17974 was 17942, checked in by Henrik Bettermann, 2 months ago

Change header.

  • Property svn:keywords set to Id
File size: 45.4 KB
Line 
1## $Id: browser.py 17942 2024-10-12 11:41:29Z 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 os
20import csv
21import pytz
22from datetime import datetime
23from zope.i18n import translate
24from zope.security import checkPermission
25from zope.schema.interfaces import ConstraintNotSatisfied
26from zope.formlib.textwidgets import BytesDisplayWidget
27from zope.component import getUtility, queryUtility
28from hurry.workflow.interfaces import IWorkflowInfo
29from waeup.kofa.interfaces import (
30    REQUESTED, IExtFileStore, IKofaUtils, IObjectHistory)
31from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget
32from waeup.kofa.browser.layout import action, KofaEditFormPage, UtilityView, KofaPage
33from waeup.kofa.students.browser import (
34    StudentBaseEditFormPage,
35    StudyLevelEditFormPage, StudyLevelDisplayFormPage,
36    StudentBasePDFFormPage, ExportPDFCourseRegistrationSlip,
37    CourseTicketDisplayFormPage,
38    CourseTicketManageFormPage,
39    StudentTriggerTransitionFormPage,
40    msave, emit_lock_message,
41    StudentActivateView, StudentDeactivateView,
42    ExportPDFTranscriptSlip,
43    PaymentsManageFormPage,
44    StartClearancePage,
45    StudentFilesUploadPage,
46    StudyCourseTranscriptPage,
47    ExportPDFAdmissionSlip)
48from waeup.kofa.students.workflow import (CREATED, ADMITTED, PAID,
49    CLEARANCE, REQUESTED, RETURNING, CLEARED, REGISTERED, VALIDATED,
50    GRADUATED, TRANSREQ, TRANSVAL, TRANSREL, FORBIDDEN_POSTGRAD_TRANS)
51from waeup.kofa.students.interfaces import IStudentsUtils, ICourseTicket
52from waeup.kofa.students.workflow import FORBIDDEN_POSTGRAD_TRANS
53from kofacustom.nigeria.students.browser import (
54    NigeriaOnlinePaymentDisplayFormPage,
55    NigeriaStudentBaseManageFormPage,
56    NigeriaStudentClearanceEditFormPage,
57    NigeriaOnlinePaymentAddFormPage,
58    NigeriaExportPDFPaymentSlip,
59    NigeriaExportPDFClearanceSlip,
60    NigeriaExportPDFBedTicketSlip,
61    NigeriaStudentPersonalDisplayFormPage,
62    NigeriaStudentPersonalManageFormPage,
63    NigeriaStudentPersonalEditFormPage,
64    NigeriaAccommodationManageFormPage,
65    NigeriaAccommodationDisplayFormPage,
66    NigeriaStudentBaseDisplayFormPage,
67    NigeriaExportPDFFinancialClearancePage,
68    )
69from waeup.uniben.students.interfaces import (
70    ICustomStudent,
71    ICustomStudentBase,
72    ICustomStudentOnlinePayment,
73    ICustomStudentStudyCourse,
74    ICustomStudentStudyLevel,
75    ICustomUGStudentClearance,
76    ICustomPGStudentClearance,
77    ICustomStudentPersonal,
78    ICustomStudentPersonalEdit,
79    IMedicalHistory,
80    ITiship,
81    INYSC,
82    ICustomCourseTicket)
83from waeup.uniben.interfaces import MessageFactory as _
84
85deplinks_path = os.path.join(
86    os.path.dirname(__file__), 'deplinks.csv')
87reader = csv.DictReader(open(deplinks_path, 'rb'))
88DEPLINKS = {line['code']:line['url'] for line in reader}
89
90class CustomStudentBaseDisplayFormPage(NigeriaStudentBaseDisplayFormPage):
91    """ Page to display student base data
92    """
93    form_fields = grok.AutoFields(ICustomStudentBase).omit(
94        'password', 'suspended', 'suspended_comment',
95        'flash_notice', 'provisionally_cleared', 'parents_email')
96    form_fields[
97        'financial_clearance_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
98    form_fields[
99        'final_clearance_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
100
101class CustomStudentBaseManageFormPage(NigeriaStudentBaseManageFormPage):
102    """ View to manage student base data
103    """
104    form_fields = grok.AutoFields(ICustomStudentBase).omit(
105        'student_id', 'adm_code', 'suspended',
106        'financially_cleared_by', 'financial_clearance_date',
107        'finally_cleared_by', 'final_clearance_date',
108        'parents_email')
109
110class CustomOnlinePaymentDisplayFormPage(NigeriaOnlinePaymentDisplayFormPage):
111    """ Page to view an online payment ticket
112    """
113    grok.context(ICustomStudentOnlinePayment)
114    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
115        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
116    form_fields[
117        'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
118    form_fields[
119        'payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
120
121class CustomStartClearancePage(StartClearancePage):
122
123    @property
124    def with_ac(self):
125        if self.context.faccode == 'DCOEM':
126            return False
127        return True
128
129    @property
130    def portrait_uploaded(self):
131        return True
132
133
134CLEARANCE_FLASH_MESSAGE = """
135If you have not presented for physical clearance, scan your WAEC, NECO
136and other O level results scratch cards on one document and
137attach to the form below (Scans > O Level Results Scratch Card).
138Then come back to check for your successful cleared status as from 3 days
139after successful clearance request.
140If status is cleared, pay your school charges and proceed to the
141Dean's Office to collect the signed eligibility form.
142"""
143
144class CustomStudentClearanceEditFormPage(NigeriaStudentClearanceEditFormPage):
145    """ View to edit student clearance data by student
146    """
147
148    @property
149    def form_fields(self):
150        if self.context.is_postgrad:
151            form_fields = grok.AutoFields(ICustomPGStudentClearance).omit(
152            'clearance_locked', 'nysc_location', 'clr_code', 'officer_comment',
153            'physical_clearance_date')
154        else:
155            form_fields = grok.AutoFields(ICustomUGStudentClearance).omit(
156            'clearance_locked', 'clr_code', 'officer_comment',
157            'physical_clearance_date')
158            form_fields['date_of_birth'].for_display = True
159            form_fields['nationality'].for_display = True
160            form_fields['lga'].for_display = True
161        return form_fields
162
163    def dataNotComplete(self):
164        store = getUtility(IExtFileStore)
165        if self.context.entry_mode in ('ug_ft', 'de_ft') and \
166            not store.getFileByContext(self.context, attr=u'passport.jpg'):
167            return _('No red background passport picture uploaded.')
168        if not store.getFileByContext(self.context, attr=u'birth_certificate.jpg'):
169            return _('No birth certificate uploaded.')
170        if not store.getFileByContext(self.context, attr=u'ref_let.jpg'):
171            return _('No guarantor/referee letter uploaded.')
172        if not store.getFileByContext(self.context, attr=u'acc_let.jpg'):
173            return _('No acceptance letter uploaded.')
174        if not store.getFileByContext(self.context, attr=u'fst_sit_scan.jpg'):
175            return _('No first sitting result uploaded.')
176        if not store.getFileByContext(self.context, attr=u'secr_cults.jpg'):
177            return _('No affidavit of non-membership of secret cults uploaded.')
178        if not store.getFileByContext(self.context, attr=u'olevel_sc.jpg'):
179            return _('No O level results scratch card uploaded.')
180        return False
181
182    #def update(self):
183    #    self.flash(CLEARANCE_FLASH_MESSAGE, type="warning")
184    #    return super(CustomStudentClearanceEditFormPage, self).update()
185
186class CustomOnlinePaymentAddFormPage(NigeriaOnlinePaymentAddFormPage):
187    """ Page to add an online payment ticket
188    """
189    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).select(
190        'p_combi')
191
192class CustomExportPDFPaymentSlip(NigeriaExportPDFPaymentSlip):
193    """Deliver a PDF slip of the context.
194    """
195    grok.context(ICustomStudentOnlinePayment)
196    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
197        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item', 'p_combi')
198    form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
199    form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
200
201
202class CustomExportPDFClearanceSlip(NigeriaExportPDFClearanceSlip):
203    """Deliver a PDF slip of the context.
204    """
205
206    @property
207    def omit_fields(self):
208        omit_fields = ('password', 'suspended', 'suspended_comment',
209                       'phone', 'adm_code', 'email', 'date_of_birth',
210                       'flash_notice')
211        if self.context.is_jupeb:
212            omit_fields += ('faculty', 'department')
213        return omit_fields
214
215    @property
216    def label(self):
217        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
218        line0 = ''
219        if self.context.is_jupeb:
220            line0 = 'Joint Universities Preliminary Examinations Board (JUPEB)\n'
221        line1 = translate(_('Clearance Slip of'),
222            'waeup.kofa', target_language=portal_language) \
223            + ' %s' % self.context.display_fullname
224        return '%s%s' % (line0, line1)
225
226    def _sigsInFooter(self):
227        isStudent = getattr(
228            self.request.principal, 'user_type', None) == 'student'
229        if not isStudent and self.context.state in (CLEARED, RETURNING):
230            return (_('Date, Student Signature'),
231                    _('Date, Clearance Officer Signature'),
232                    )
233        return ()
234
235    def render(self):
236        studentview = StudentBasePDFFormPage(self.context.student,
237            self.request, self.omit_fields)
238        students_utils = getUtility(IStudentsUtils)
239        return students_utils.renderPDF(
240            self, 'clearance_slip.pdf',
241            self.context.student, studentview, signatures=self._signatures(),
242            sigs_in_footer=self._sigsInFooter(), show_scans=False,
243            omit_fields=self.omit_fields)
244
245
246class ExportClearanceInvitationSlip(UtilityView, grok.View):
247    """Deliver an invitation letter to physical clearance.
248
249    This form page is available only in Uniben.
250    """
251    grok.context(ICustomStudent)
252    grok.name('clearance_invitation_slip.pdf')
253    grok.require('waeup.viewStudent')
254    prefix = 'form'
255
256    label = u'Invitation Letter for Physical Clearance'
257
258    omit_fields = (
259        'suspended', 'phone', 'email',
260        'adm_code', 'suspended_comment',
261        'date_of_birth', 'current_level',
262        'department', 'current_mode',
263        'entry_session', 'matric_number', 'sex',
264        'flash_notice')
265
266    form_fields = []
267
268    @property
269    def note(self):
270        if self.context.physical_clearance_date:
271            return """
272<br /><br /><br /><br /><font size='12'>
273Dear %s,
274<br /><br /><br />
275You are invited for your Health Centre clearance on:
276<br /><br />
277<strong>%s</strong>.
278<br /><br />
279Please bring along your downloaded Health Centre Form to the University Health Centre
280on your Health Centre clearance date.
281<br /><br /><br />
282Signed,
283<br /><br />
284The Registrar<br />
285</font>
286
287""" % (self.context.display_fullname, self.context.physical_clearance_date)
288        return
289
290
291    def update(self):
292        if self.context.student.state != REQUESTED \
293            or not  self.context.student.physical_clearance_date:
294            self.flash(_('Forbidden'), type="warning")
295            self.redirect(self.url(self.context))
296
297    def render(self):
298        studentview = StudentBasePDFFormPage(self.context.student,
299            self.request, self.omit_fields)
300        students_utils = getUtility(IStudentsUtils)
301        return students_utils.renderPDF(
302            self, 'clearance_invitation_slip',
303            self.context.student, studentview,
304            omit_fields=self.omit_fields,
305            note=self.note)
306
307class ExportMedicalExaminationSlip(UtilityView, grok.View):
308    """Deliver an .
309
310    This form page is available only in Uniben.
311    """
312    grok.context(ICustomStudent)
313    grok.name('medical_examination_slip.pdf')
314    grok.require('waeup.viewStudent')
315    prefix = 'form'
316
317    label = u'Uniben Medical Examination Form'
318
319    omit_fields = (
320        'suspended',
321        'adm_code', 'suspended_comment',
322        'current_level',
323        'department', 'current_mode',
324        'entry_session', 'matric_number',
325        'flash_notice', 'parents_email',
326        'faculty', 'department')
327
328    form_fields = grok.AutoFields(ICustomStudentPersonal).select(
329        'marit_stat', 'perm_address', 'next_kin_phone')
330    form_fields['perm_address'].custom_widget = BytesDisplayWidget
331
332    def _medicalClearancePaymentMade(self, student, session):
333        if len(student['payments']):
334            for ticket in student['payments'].values():
335                if ticket.p_state == 'paid' and \
336                    ticket.p_category == 'medical_clearance' and \
337                    ticket.p_session == session:
338                    return True
339        return False
340
341    def update(self):
342        if self.context.student.state != CLEARED \
343            or not  self.context.student.physical_clearance_date:
344            self.flash(_('Forbidden'), type="warning")
345            self.redirect(self.url(self.context))
346        if not self._medicalClearancePaymentMade(
347            self.context, self.context.current_session):
348            self.flash('Please pay medical clearance fee first.',
349                type="warning")
350            self.redirect(self.url(self.context))
351        return
352
353    def render(self):
354        studentview = StudentBasePDFFormPage(self.context.student,
355            self.request, self.omit_fields)
356        students_utils = getUtility(IStudentsUtils)
357        file_path = os.path.join(
358            os.path.dirname(__file__), 'static', 'medical_examination_form.pdf')
359        file = open(file_path, 'rb')
360        mergefiles = [file,]
361        return students_utils.renderPDF(
362            self, 'medical_examination_slip',
363            self.context.student, studentview,
364            omit_fields=self.omit_fields,
365            mergefiles=mergefiles,
366            )
367
368
369class ExportExaminationScheduleSlip(UtilityView, grok.View):
370    """Deliver a examination schedule slip.
371
372    This form page is available only in Uniben and AAUE.
373    """
374    grok.context(ICustomStudent)
375    grok.name('examination_schedule_slip.pdf')
376    grok.require('waeup.viewStudent')
377    prefix = 'form'
378
379    label = u'Examination Schedule Slip'
380
381    omit_fields = (
382        'suspended', 'phone', 'email',
383        'adm_code', 'suspended_comment',
384        'date_of_birth', 'current_level',
385        'current_mode',
386        'entry_session',
387        'flash_notice')
388
389    form_fields = []
390
391    @property
392    def note(self):
393        return """
394<br /><br /><br /><br /><font size='12'>
395Your examination date, time and venue is scheduled as follows:
396<br /><br />
397<strong>%s</strong>
398</font>
399
400""" % self.context.flash_notice
401        return
402
403
404    def update(self):
405        if not self.context.flash_notice \
406            or not 'exam' in self.context.flash_notice.lower():
407            self.flash(_('Forbidden'), type="warning")
408            self.redirect(self.url(self.context))
409
410    def render(self):
411        studentview = StudentBasePDFFormPage(self.context.student,
412            self.request, self.omit_fields)
413        students_utils = getUtility(IStudentsUtils)
414        return students_utils.renderPDF(
415            self, 'examination_schedule_slip',
416            self.context.student, studentview,
417            omit_fields=self.omit_fields,
418            note=self.note)
419
420class SwitchLibraryAccessView(UtilityView, grok.View):
421    """ Switch the library attribute
422    """
423    grok.context(ICustomStudent)
424    grok.name('switch_library_access')
425    grok.require('waeup.switchLibraryAccess')
426
427    def update(self):
428        if self.context.library:
429            self.context.library = False
430            self.context.writeLogMessage(self, 'library access disabled')
431            self.flash(_('Library access disabled'))
432        else:
433            self.context.library = True
434            self.context.writeLogMessage(self, 'library access enabled')
435            self.flash(_('Library access enabled'))
436        self.redirect(self.url(self.context))
437        return
438
439    def render(self):
440        return
441
442class ExportJHLIdCard(UtilityView, grok.View):
443    """Deliver an id card for the John Harris Library.
444    """
445    grok.context(ICustomStudent)
446    grok.name('jhl_idcard.pdf')
447    grok.require('waeup.viewStudent')
448    prefix = 'form'
449
450    label = u"John Harris Library Clearance"
451
452    omit_fields = (
453        'suspended', 'email', 'phone',
454        'adm_code', 'suspended_comment',
455        'date_of_birth',
456        'current_mode', 'certificate',
457        'entry_session',
458        'flash_notice')
459
460    form_fields = []
461
462    def _sigsInFooter(self):
463        isStudent = getattr(
464            self.request.principal, 'user_type', None) == 'student'
465        if isStudent:
466            return ''
467        return (_("Date, Reader's Signature"),
468                _("Date, Circulation Librarian's Signature"),
469                )
470
471    def update(self):
472        if not self.context.library:
473            self.flash(_('Forbidden!'), type="danger")
474            self.redirect(self.url(self.context))
475        return
476
477    @property
478    def note(self):
479        return """
480<br /><br /><br /><br /><font size='12'>
481This is to certify that the bearer whose photograph and other details appear
482 overleaf is a registered user of <b>John Harris Library, University of Benin</b>.
483 The card is not transferable. A replacement fee is charged for a loss,
484 mutilation or otherwise. If found, please, return to John Harris Library,
485 University of Benin, Benin City.
486</font>
487
488"""
489        return
490
491    def render(self):
492        studentview = StudentBasePDFFormPage(self.context.student,
493            self.request, self.omit_fields)
494        students_utils = getUtility(IStudentsUtils)
495        return students_utils.renderPDF(
496            self, 'jhl_idcard',
497            self.context.student, studentview,
498            omit_fields=self.omit_fields,
499            sigs_in_footer=self._sigsInFooter(),
500            note=self.note)
501
502class ExportJUPEBResultSlip(ExportExaminationScheduleSlip):
503    """Deliver a JUPEB result slip.
504
505    This form page is available only in Uniben.
506    """
507
508    grok.name('jupeb_result_slip.pdf')
509    label = u'JUPEB Result Slip'
510
511    @property
512    def note(self):
513        return """
514<br /><br /><br /><br /><font size='12'>
515<strong>%s</strong>
516</font>
517<br /><br /><br /><br />
518<font size='8'>
519Key: A = 5, B = 4, C = 3, D = 2, E = 1, F = 0, X = Absent, Q = Cancelled
520</font>
521
522""" % self.context.flash_notice
523        return
524
525    def update(self):
526        if not self.context.flash_notice or not self.context.is_jupeb \
527            or not 'results' in self.context.flash_notice.lower():
528            self.flash(_('Forbidden'), type="warning")
529            self.redirect(self.url(self.context))
530
531class CustomStudentPersonalDisplayFormPage(
532    NigeriaStudentPersonalDisplayFormPage):
533    """ Page to display student personal data
534    """
535
536    form_fields = grok.AutoFields(ICustomStudentPersonal) + grok.AutoFields(ITiship)
537    form_fields['perm_address'].custom_widget = BytesDisplayWidget
538    form_fields['next_kin_address'].custom_widget = BytesDisplayWidget
539    form_fields[
540        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
541
542class CustomStudentPersonalManageFormPage(
543    NigeriaStudentPersonalManageFormPage):
544    """ Page to manage personal data
545    """
546
547    form_fields = grok.AutoFields(ICustomStudentPersonal) + grok.AutoFields(ITiship)
548    form_fields['personal_updated'].for_display = True
549    form_fields[
550        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
551
552class CstomStudentPersonalEditFormPage(NigeriaStudentPersonalEditFormPage):
553    """ Page to edit personal data
554    """
555
556    @property
557    def form_fields(self):
558        form_fields = grok.AutoFields(
559            ICustomStudentPersonalEdit).omit('personal_updated') + grok.AutoFields(ITiship)
560        return form_fields
561
562class StudyCourseCOEditFormPage(KofaEditFormPage):
563    """ Page to edit the student study course data by clearance officers.
564
565    This form page is available only in Uniben.
566    """
567    grok.context(ICustomStudentStudyCourse)
568    grok.name('edit_level')
569    grok.require('waeup.clearStudent')
570    label = _('Edit current level')
571    pnav = 4
572    form_fields = grok.AutoFields(
573        ICustomStudentStudyCourse).select('current_level')
574
575    def update(self):
576        if not (self.context.is_current and
577            self.context.student.state == REQUESTED):
578            emit_lock_message(self)
579            return
580        super(StudyCourseCOEditFormPage, self).update()
581        return
582
583    @action(_('Save'), style='primary')
584    def save(self, **data):
585        try:
586            msave(self, **data)
587        except ConstraintNotSatisfied:
588            # The selected level might not exist in certificate
589            self.flash(_('Current level not available for certificate.'))
590            return
591        #notify(grok.ObjectModifiedEvent(self.context.__parent__))
592        return
593
594class CustomStudyLevelEditFormPage(StudyLevelEditFormPage):
595    """ Page to edit the student study level data by students.
596
597    """
598    grok.template('studyleveleditpage')
599
600class CustomStudyLevelDisplayFormPage(StudyLevelDisplayFormPage):
601    """ Page to display student study levels
602    """
603    grok.template('studylevelpage')
604
605    @property
606    def view_scores_allowed(self):
607        return checkPermission('waeup.manageStudent', self.context)
608
609class CustomExportPDFCourseRegistrationSlip(
610    ExportPDFCourseRegistrationSlip):
611    """Deliver a PDF slip of the context.
612    """
613
614    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
615        'level_verdict', 'gpa', 'level', 'transcript_remark')
616
617    def update(self):
618        if self.context.student.state != REGISTERED \
619            or self.context.student.current_level != self.context.level:
620            self.flash(_('Forbidden'), type="warning")
621            self.redirect(self.url(self.context))
622
623    @property
624    def tabletitle(self):
625        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
626        tabletitle = []
627        tabletitle.append(translate(_('1st Semester Courses'), 'waeup.kofa',
628            target_language=portal_language))
629        tabletitle.append(translate(_('2nd Semester Courses'), 'waeup.kofa',
630            target_language=portal_language))
631        tabletitle.append(translate(_('Level Courses'), 'waeup.kofa',
632            target_language=portal_language))
633        tabletitle.append(translate(_('1st Trimester Courses'), 'waeup.kofa',
634            target_language=portal_language))
635        tabletitle.append(translate(_('2nd Trimester Courses'), 'waeup.kofa',
636            target_language=portal_language))
637        tabletitle.append(translate(_('3rd Trimester Courses'), 'waeup.kofa',
638            target_language=portal_language))
639        return tabletitle
640
641    def render(self):
642        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
643        Code = translate('Code', 'waeup.kofa', target_language=portal_language)
644        Title = translate('Title', 'waeup.kofa', target_language=portal_language)
645        Dept = translate('Dept.', 'waeup.kofa', target_language=portal_language)
646        Faculty = translate('Faculty', 'waeup.kofa', target_language=portal_language)
647        Cred = translate(_('Credits'), 'waeup.uniben', target_language=portal_language)
648        studentview = StudentBasePDFFormPage(self.context.student,
649            self.request, self.omit_fields)
650        students_utils = getUtility(IStudentsUtils)
651
652        tabledata = []
653        tableheader = []
654        for i in range(1,7):
655            tabledata.append(sorted(
656                [value for value in self.context.values() if value.semester == i],
657                key=lambda value: str(value.semester) + value.code))
658            tableheader.append([(Code,'code', 2.5),
659                             (Title,'title', 5),
660                             (Dept,'dcode', 1.5), (Faculty,'fcode', 1.5),
661                             (Cred, 'credits', 1.5),
662                             ])
663        return students_utils.renderPDF(
664            self, 'course_registration_slip.pdf',
665            self.context.student, studentview,
666            tableheader=tableheader,
667            tabledata=tabledata,
668            omit_fields=self.omit_fields
669            )
670
671class ExportPDFCourseResultSlip(ExportPDFCourseRegistrationSlip):
672    """Deliver a PDF slip of the context.
673    """
674
675    grok.name('course_result_slip.pdf')
676
677    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit('level')
678
679    @property
680    def tabletitle(self):
681        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
682        tabletitle = []
683        tabletitle.append(translate(_('1st Semester Courses'), 'waeup.kofa',
684            target_language=portal_language))
685        tabletitle.append(translate(_('2nd Semester Courses'), 'waeup.kofa',
686            target_language=portal_language))
687        tabletitle.append(translate(_('Level Courses'), 'waeup.kofa',
688            target_language=portal_language))
689        tabletitle.append(translate(_('1st Trimester Courses'), 'waeup.kofa',
690            target_language=portal_language))
691        tabletitle.append(translate(_('2nd Trimester Courses'), 'waeup.kofa',
692            target_language=portal_language))
693        tabletitle.append(translate(_('3rd Trimester Courses'), 'waeup.kofa',
694            target_language=portal_language))
695        return tabletitle
696
697    @property
698    def label(self):
699        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
700        lang = self.request.cookies.get('kofa.language', portal_language)
701        level_title = translate(self.context.level_title, 'waeup.kofa',
702            target_language=lang)
703        return translate(_('Course Result Slip'),
704            'waeup.uniben', target_language=portal_language) \
705            + ' %s' % level_title
706
707    def render(self):
708        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
709        Code = translate('Code', 'waeup.kofa', target_language=portal_language)
710        Title = translate('Title', 'waeup.kofa', target_language=portal_language)
711        Dept = translate('Dept.', 'waeup.kofa', target_language=portal_language)
712        Faculty = translate('Faculty', 'waeup.kofa', target_language=portal_language)
713        Cred = translate(_('Credits'), 'waeup.uniben', target_language=portal_language)
714        Grade = translate('Grade', 'waeup.kofa', target_language=portal_language)
715        studentview = StudentBasePDFFormPage(self.context.student,
716            self.request, self.omit_fields)
717        students_utils = getUtility(IStudentsUtils)
718
719        tabledata = []
720        tableheader = []
721        for i in range(1,7):
722            tabledata.append(sorted(
723                [value for value in self.context.values() if value.semester == i],
724                key=lambda value: str(value.semester) + value.code))
725            tableheader.append([(Code,'code', 2.5),
726                             (Title,'title', 5),
727                             (Dept,'dcode', 1.5), (Faculty,'fcode', 1.5),
728                             (Cred, 'credits', 1.5),
729                             (Grade, 'grade', 1.5),
730                             ])
731        return students_utils.renderPDF(
732            self, 'course_result_slip.pdf',
733            self.context.student, studentview,
734            tableheader=tableheader,
735            tabledata=tabledata,
736            omit_fields=self.omit_fields
737            )
738
739class CustomCourseTicketDisplayFormPage(CourseTicketDisplayFormPage):
740    """ Page to display course tickets
741    """
742    form_fields = grok.AutoFields(ICustomCourseTicket).omit('score')
743
744class CustomCourseTicketManageFormPage(CourseTicketManageFormPage):
745    """ Page to manage course tickets
746    """
747
748    form_fields = grok.AutoFields(ICustomCourseTicket).omit('course_category')
749    form_fields['title'].for_display = True
750    form_fields['fcode'].for_display = True
751    form_fields['dcode'].for_display = True
752    form_fields['semester'].for_display = True
753    form_fields['passmark'].for_display = True
754    form_fields['credits'].for_display = True
755    form_fields['mandatory'].for_display = False
756    form_fields['automatic'].for_display = True
757    form_fields['carry_over'].for_display = True
758    form_fields['ticket_session'].for_display = True
759
760class CustomStudentActivateView(StudentActivateView):
761    """ Activate student account
762    """
763
764    def update(self):
765        self.context.suspended = False
766        self.context.writeLogMessage(self, 'account activated')
767        history = IObjectHistory(self.context)
768        history.addMessage('Student account activated', user='undisclosed')
769        self.flash(_('Student account has been activated.'))
770        self.redirect(self.url(self.context))
771        return
772
773class CustomStudentDeactivateView(StudentDeactivateView):
774    """ Deactivate student account
775    """
776    def update(self):
777        self.context.suspended = True
778        self.context.writeLogMessage(self, 'account deactivated')
779        history = IObjectHistory(self.context)
780        history.addMessage('Student account deactivated', user='undisclosed')
781        self.flash(_('Student account has been deactivated.'))
782        self.redirect(self.url(self.context))
783        return
784
785class CustomStudyCourseTranscriptPage(StudyCourseTranscriptPage):
786    """ Page to display the student's transcript.
787    """
788#    grok.require('waeup.viewStudent')
789
790class CustomExportPDFTranscriptSlip(ExportPDFTranscriptSlip):
791    """Deliver a PDF slip of the context.
792    """
793#    grok.require('waeup.viewStudent')
794
795    def _sigsInFooter(self):
796        isStudent = getattr(
797            self.request.principal, 'user_type', None) == 'student'
798        if not isStudent and self.context.student.state in (TRANSVAL, TRANSREL):
799            return (_('D. R. (Exams & Records)'),_('Current Dean of Faculty'),)
800        return ()
801
802    #def _signatures(self):
803    #    return ([(
804    #        'Current HD<br /> D. R. (Exams & Records)<br /> '
805    #        'For: Registrar')],)
806
807    def render(self):
808        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
809        Term = translate(_('Sem.'), 'waeup.kofa', target_language=portal_language)
810        Code = translate(_('Course'), 'waeup.kofa', target_language=portal_language)
811        Title = translate(_('Subject/Course (Title)'), 'waeup.kofa', target_language=portal_language)
812        Cred = translate(_('Credit'), 'waeup.kofa', target_language=portal_language)
813        #Score = translate(_('Score'), 'waeup.kofa', target_language=portal_language)
814        Grade = translate(_('Grade'), 'waeup.kofa', target_language=portal_language)
815        studentview = StudentBasePDFFormPage(self.context.student,
816            self.request, self.omit_fields)
817        students_utils = getUtility(IStudentsUtils)
818
819        tableheader = [(Code,'code', 2.5),
820                         (Title,'title', 8.5),
821                         (Term, 'semester', 1.5),
822                         (Cred, 'credits', 1.5),
823                         #(Score, 'score', 1.5),
824                         (Grade, 'grade', 1.5),
825                         ]
826
827        pdfstream = students_utils.renderPDFTranscript(
828            self, 'transcript.pdf',
829            self.context.student, studentview,
830            omit_fields=self.omit_fields,
831            tableheader=tableheader,
832            signatures=self._signatures(),
833            sigs_in_footer=self._sigsInFooter(),
834            digital_sigs=self._digital_sigs(),
835            save_file=self._save_file(),
836            )
837        if not pdfstream:
838            self.redirect(self.url(self.context.student))
839            return
840        return pdfstream
841
842class CustomExportPDFBedTicketSlip(NigeriaExportPDFBedTicketSlip):
843    """Deliver a PDF slip of the context.
844    """
845    omit_fields = ('password', 'suspended', 'suspended_comment',
846        'phone', 'adm_code', 'email', 'date_of_birth', 'flash_notice')
847
848class CustomPaymentsManageFormPage(PaymentsManageFormPage):
849    """ Page to manage the student payments. This manage form page is for
850    both students and students officers. Uniben does not allow students
851    to remove any payment ticket.
852    """
853    @property
854    def manage_payments_allowed(self):
855        return checkPermission('waeup.manageStudent', self.context)
856
857class CustomAccommodationDisplayFormPage(NigeriaAccommodationDisplayFormPage):
858    """ Page to view bed tickets.
859    """
860    with_hostel_selection = True
861
862class CustomAccommodationManageFormPage(NigeriaAccommodationManageFormPage):
863    """ Page to manage bed tickets.
864    This manage form page is for both students and students officers.
865    """
866    with_hostel_selection = True
867
868class CustomStudentBaseEditFormPage(StudentBaseEditFormPage):
869    """ View to edit student base data
870    """
871    form_fields = grok.AutoFields(ICustomStudentBase).select(
872        'email', 'phone')
873    form_fields['email'].field.required = True
874
875
876
877from zope.schema.vocabulary import SimpleVocabulary
878from zope.formlib.itemswidgets import RadioWidget
879from zope.formlib.itemswidgets import SelectWidget, DropdownWidget
880
881def CustomBooleanRadioWidget(field, request, true=_('yes'), false=_('no')):
882    vocabulary = SimpleVocabulary.fromItems( ((true, True), (false, False)) )
883    widget = RadioWidget(field, vocabulary, request)
884    widget.required = False
885    return widget
886
887class StudentMedicalHistoryEditFormPage(KofaEditFormPage):
888    """ Page to edit medical data
889    """
890    grok.context(ICustomStudent)
891    grok.name('edit_medical')
892    grok.require('waeup.handleStudent')
893    label = _('Medical Questionnaire')
894    pnav = 4
895
896    def _medicalQuestPaymentMade(self, student, session):
897        if len(student['payments']):
898            for ticket in student['payments'].values():
899                if ticket.p_state == 'paid' and \
900                    ticket.p_category == 'medical_quest' and \
901                    ticket.p_session == session:
902                    return True
903        return False
904
905    def update(self):
906        if not self._medicalQuestPaymentMade(
907            self.context, self.context.current_session):
908            self.flash('Please pay medical questionnaire payment first.',
909                type="warning")
910            self.redirect(self.url(self.context))
911            return
912        return super(StudentMedicalHistoryEditFormPage, self).update()
913
914    @property
915    def form_fields(self):
916        form_fields = grok.AutoFields(IMedicalHistory) + grok.AutoFields(ITiship)
917        form_fields['medical_updated'].for_display = True
918        for field in ('fever', 'headaches', 'catarrh', 'cough', 'sore_throat',
919                      'breathing', 'sneezing', 'weakness', 'body_pains',
920                      'smell', 'taste', 'asthma', 'hypertension', 'diabetes',
921                      'obesity', 'lagos_abuja', 'outside', 'company_suspected',
922                      'company_confirmed', 'positive', 'negative',
923                      'vaccination'):
924            form_fields[field].custom_widget = CustomBooleanRadioWidget
925        return form_fields
926
927    @property
928    def separators(self):
929        return getUtility(IStudentsUtils).SEPARATORS_DICT
930
931    @action(_('Save/Confirm'), style='primary')
932    def save(self, **data):
933        msave(self, **data)
934        self.context.medical_updated = datetime.utcnow()
935        return
936
937class StudentMedicalHistoryManageFormPage(StudentMedicalHistoryEditFormPage):
938    """ Page to manage medical data
939    """
940    grok.name('manage_medical')
941    grok.require('waeup.manageStudent')
942    label = _('Manage medical questionnaire data')
943
944    def update(self):
945        return super(StudentMedicalHistoryEditFormPage, self).update()
946
947
948class ExportPDFMedicalHistorySlip(grok.View):
949    """Deliver a PDF slip of the context.
950    """
951    grok.context(ICustomStudent)
952    grok.name('medical_questionnaire_slip.pdf')
953    grok.require('waeup.viewStudent')
954    prefix = 'form'
955    form_fields = grok.AutoFields(IMedicalHistory)
956
957    omit_fields = ('password', 'suspended', 'suspended_comment',
958                   'adm_code', 'date_of_birth',
959                   'flash_notice', 'current_mode', 'entry_mode',
960                   'entry_session', 'parents_email',
961                   'current_level', 'reg_number', 'sex',
962                   'certificate')
963
964    @property
965    def title(self):
966        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
967        return translate(_('Medical Questionnaire'), 'waeup.kofa',
968            target_language=portal_language)
969
970    @property
971    def label(self):
972        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
973        return translate(_('Medical Questionnaire Slip of'),
974            'waeup.kofa', target_language=portal_language) \
975            + ' %s' % self.context.display_fullname
976
977    def render(self):
978        studentview = StudentBasePDFFormPage(self.context.student,
979            self.request, self.omit_fields)
980        students_utils = getUtility(IStudentsUtils)
981        return students_utils.renderPDF(
982            self, 'medical_questionnaire_slip.pdf',
983            self.context.student, studentview,
984            omit_fields=self.omit_fields,)
985
986class ExportPDFTishipSlip(grok.View):
987    """Deliver a PDF slip of the context.
988    """
989    grok.context(ICustomStudent)
990    grok.name('tiship_slip.pdf')
991    grok.require('waeup.viewStudent')
992    prefix = 'form'
993    form_fields = grok.AutoFields(ITiship)
994    title = ''
995
996    omit_fields = ('password', 'suspended', 'suspended_comment',
997                   'adm_code',
998                   'flash_notice', 'current_mode', 'entry_mode',
999                   'parents_email',
1000                   )
1001
1002    @property
1003    def label(self):
1004        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
1005        return translate(_('TISHIP Registration Slip of'),
1006            'waeup.kofa', target_language=portal_language) \
1007            + ' %s' % self.context.display_fullname
1008
1009
1010    def _sigsInFooter(self):
1011        return (_('Year of Graduation'),
1012                    _('Signature'),
1013                    _('Date'),
1014                    )
1015
1016    def render(self):
1017        studentview = StudentBasePDFFormPage(self.context.student,
1018            self.request, self.omit_fields)
1019        students_utils = getUtility(IStudentsUtils)
1020        return students_utils.renderPDF(
1021            self, 'tiship_slip.pdf',
1022            self.context.student, studentview,
1023            sigs_in_footer=self._sigsInFooter(),
1024            omit_fields=self.omit_fields,)
1025
1026class PlagTestInfoPage(KofaPage):
1027    "Landing page after plagiation test payment"
1028    grok.context(ICustomStudentOnlinePayment)
1029    grok.name('plagtestinfo')
1030    grok.require('waeup.viewStudent')
1031
1032    def update(self):
1033        if self.context.p_state != 'paid' \
1034            or self.context.p_category != 'plag_test':
1035            self.flash(_('Forbidden'), type="danger")
1036            self.redirect(self.url(self.context))
1037        return super(PlagTestInfoPage, self).update()
1038
1039    @property
1040    def facdep(self):
1041        facdepkey = "%s-%s" % (self.context.student.faccode,
1042                               self.context.student.depcode)
1043        return DEPLINKS[facdepkey]
1044
1045class CustomExportPDFAdmissionSlip(ExportPDFAdmissionSlip):
1046    """Deliver a PDF Admission slip.
1047    """
1048
1049    @property
1050    def post_text_pt(self):
1051        datelist = self.context.history.messages[0].split()[0].split('-')
1052        creation_date = u'%s/%s/%s' % (datelist[2], datelist[1], datelist[0])
1053        return (
1054            'Your Kofa student record was created on %s. <br/><br/>'
1055            'Please Note: This admission for '
1056            'Undergraduate Part-Time and Undergraduate Sandwich is incomplete '
1057            'until you have successfully applied on the <b>JAMB CAPS PORTAL</b>.  '
1058            'Ignore this if you have completed your own <b>pre-admission</b> '
1059            'activities on the JAMB website.' % creation_date)
1060
1061    def render(self):
1062        students_utils = getUtility(IStudentsUtils)
1063        letterhead_path = os.path.join(
1064            os.path.dirname(__file__), 'static', 'letterhead_admission.jpg')
1065        if not os.path.exists(letterhead_path):
1066            letterhead_path = None
1067        if self.context.current_mode in ('ug_pt', 'ug_sw'):
1068            return students_utils.renderPDFAdmissionLetter(self,
1069                self.context.student, omit_fields=self.omit_fields,
1070                letterhead_path=letterhead_path, post_text=self.post_text_pt)
1071        return students_utils.renderPDFAdmissionLetter(self,
1072            self.context.student, omit_fields=self.omit_fields,
1073            letterhead_path=letterhead_path, post_text=None)
1074
1075class StudentNYSCEditFormPage(KofaEditFormPage):
1076    """ Page to edit NYSC data
1077    """
1078    grok.context(ICustomStudent)
1079    grok.name('edit_nysc')
1080    grok.require('waeup.handleStudent')
1081    label = _('NYSC Request')
1082    pnav = 4
1083
1084    def update(self):
1085        if not self.context.eligible_for_nysc:
1086            self.flash('You are not allowed to apply for NYSC.',
1087                type="warning")
1088            self.redirect(self.url(self.context))
1089            return
1090        return super(StudentNYSCEditFormPage, self).update()
1091
1092    @property
1093    def form_fields(self):
1094        form_fields = grok.AutoFields(INYSC)
1095        form_fields['nysc_updated'].for_display = True
1096        #form_fields['nysc_senate_info'].for_display = True
1097        form_fields['nysc'].field.description = u'Ensure that all your school charges are paid up to date to your graduating session before making this nysc application.'
1098        form_fields['nysc'].field.title = u'Do you want to apply for NYSC?'
1099        form_fields[
1100            'nysc_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
1101        form_fields['nysc'].custom_widget = CustomBooleanRadioWidget
1102        form_fields['nysc_processed'].for_display = True
1103        #form_fields['nysc_senate_info'].field.description = u''
1104        return form_fields
1105
1106    @action(_('Save'), style='primary')
1107    def save(self, **data):
1108        msave(self, **data)
1109        self.context.nysc_updated = datetime.utcnow()
1110        self.context.nysc_processed = False
1111        return
1112
1113class StudentNYSCManageFormPage(KofaEditFormPage):
1114    """ Page to manage NYSC data
1115    """
1116    grok.context(ICustomStudent)
1117    grok.name('manage_nysc')
1118    grok.require('waeup.manageStudent')
1119    label = _('Manage NYSC request data')
1120
1121    @property
1122    def form_fields(self):
1123        form_fields = grok.AutoFields(INYSC)
1124        form_fields['nysc_updated'].for_display = True
1125        form_fields['nysc'].field.description = u''
1126        form_fields['nysc'].field.title = u'NYSC requested'
1127        form_fields[
1128            'nysc_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
1129        return form_fields
1130
1131    @action(_('Save'), style='primary')
1132    def save(self, **data):
1133        msave(self, **data)
1134        return
1135
1136class CustomNigeriaExportPDFFinancialClearancePage(NigeriaExportPDFFinancialClearancePage):
1137    """Deliver a PDF financial clearance slip.
1138    """
1139
1140    grok.context(ICustomStudent)
1141
1142    @property
1143    def note(self):
1144        if self.context.nysc and self.context.nysc_updated:
1145            nysc_updated = self.context.nysc_updated.strftime('%d/%m/%Y')
1146            return """
1147NYSC requested on %s.
1148
1149Senate Info: %s
1150""" % (nysc_updated, self.context.nysc_senate_info)
1151        else:
1152            return
1153
1154    form_fields = grok.AutoFields(ICustomStudent).select('email2')
1155
1156    omit_fields = (
1157        'suspended', 'phone', 'email', 'parents_email',
1158        'adm_code', 'suspended_comment',
1159        'date_of_birth', 'entry_session', 'current_mode',
1160        'flash_notice')
1161
1162class StudentAffidavitUploadPage(KofaPage):
1163    """View to upload scanned affidavit of good behavior by student.
1164    """
1165    grok.context(ICustomStudent)
1166    grok.name('affidavit_upload')
1167    grok.require('waeup.uploadStudentFile')
1168    grok.template('affidavituploadpage')
1169    label = _('Upload affidavit of good conduct')
1170    deletion_warning = _('Are you sure?')
1171    pnav = 4
1172
1173class ExportPDFAffidavitSlip(UtilityView, grok.View):
1174    """Deliver a affidavit PDF template.
1175    """
1176    grok.context(ICustomStudent)
1177    grok.name('affidavit_good_conduct.pdf')
1178    grok.require('waeup.viewStudent')
1179    prefix = 'form'
1180
1181    omit_fields = ('date_of_birth', 'current_level', 'certificate',
1182        'current_mode', 'entry_session')
1183
1184    form_fields = grok.AutoFields(ICustomStudentBase).select('student_id', 'matric_number', 'reg_number', 'sex')
1185
1186    pre_text = ' '
1187
1188    post_text = '''To undertake as follows:
1189
1190(i) to abide by the arrangement(s) put in place by the University Management for the
1191supply, distribution and utilization of electricity on the campuses of the
1192University of Benin;
1193
1194(ii) to pay outstanding school charges upon resumption as eligibility for examinations
1195will henceforth be strictly based on presentation of evidence of payment of approved
1196school charges;
1197
1198(iii) not to bring any prohibited appliances, such as hot plate, ring boiler,
1199gas cooker etc into the Halls of Residence, as violation of this rule shall attract
1200rustication of such a Student;
1201
1202(iv) not to be involved in any unruly conduct that is against the Rules and
1203Regulations of the University; and
1204
1205(v) to be of good conduct, and not to sponsor or participate in any unauthorized
1206assembly or demonstration within the campuses and their precincts.
1207
1208
1209________________________________
1210(Deponent)
1211
1212Sworn at ________________________________
1213
1214This Day ________________________________
1215
1216Before Me _______________________________
1217
1218
1219
1220Commissioner of Oaths
1221
1222
1223
1224
1225'''
1226
1227    @property
1228    def label(self):
1229        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
1230        return 'Affidavit of Undertaking to be of Good Conduct'
1231           
1232    def render(self):
1233        students_utils = getUtility(IStudentsUtils)
1234        letterhead_path = os.path.join(
1235            os.path.dirname(__file__), 'static', 'letterhead.jpg')
1236        if not os.path.exists(letterhead_path):
1237            letterhead_path = None
1238        return students_utils.renderPDFAdmissionLetter(self,
1239            self.context.student, omit_fields=self.omit_fields,
1240            letterhead_path=letterhead_path,
1241            pre_text=self.pre_text, post_text=self.post_text)
Note: See TracBrowser for help on using the repository browser.