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

Last change on this file since 17421 was 17395, checked in by Henrik Bettermann, 19 months ago

Allow students to update their ship data from the personal page.

Add boolean field to indicate interest in NYSC.

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