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

Last change on this file since 15269 was 15267, checked in by Henrik Bettermann, 6 years ago

Activate hostel selection.

  • Property svn:keywords set to Id
File size: 23.4 KB
Line 
1## $Id: browser.py 15267 2018-12-13 07:16:11Z 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
19from zope.i18n import translate
20from zope.security import checkPermission
21from zope.schema.interfaces import ConstraintNotSatisfied
22from zope.formlib.textwidgets import BytesDisplayWidget
23from zope.component import getUtility
24from hurry.workflow.interfaces import IWorkflowInfo
25from waeup.kofa.interfaces import (
26    REQUESTED, IExtFileStore, IKofaUtils, IObjectHistory)
27from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget
28from waeup.kofa.browser.layout import action, KofaEditFormPage, UtilityView
29from waeup.kofa.students.browser import (
30    StudyLevelEditFormPage, StudyLevelDisplayFormPage,
31    StudentBasePDFFormPage, ExportPDFCourseRegistrationSlip,
32    CourseTicketDisplayFormPage, StudentTriggerTransitionFormPage,
33    msave, emit_lock_message,
34    StudentActivateView, StudentDeactivateView,
35    ExportPDFTranscriptSlip,
36    PaymentsManageFormPage)
37from waeup.kofa.students.workflow import (CREATED, ADMITTED, PAID,
38    CLEARANCE, REQUESTED, RETURNING, CLEARED, REGISTERED, VALIDATED,
39    GRADUATED, FORBIDDEN_POSTGRAD_TRANS)
40from waeup.kofa.students.interfaces import IStudentsUtils, ICourseTicket
41from waeup.kofa.students.workflow import FORBIDDEN_POSTGRAD_TRANS
42from kofacustom.nigeria.students.browser import (
43    NigeriaOnlinePaymentDisplayFormPage,
44    NigeriaStudentBaseManageFormPage,
45    NigeriaStudentClearanceEditFormPage,
46    NigeriaOnlinePaymentAddFormPage,
47    NigeriaExportPDFPaymentSlip,
48    NigeriaExportPDFClearanceSlip,
49    NigeriaExportPDFBedTicketSlip,
50    NigeriaStudentPersonalDisplayFormPage,
51    NigeriaStudentPersonalManageFormPage,
52    NigeriaStudentPersonalEditFormPage,
53    NigeriaAccommodationManageFormPage
54    )
55
56from waeup.uniben.students.interfaces import (
57    ICustomStudent,
58    ICustomStudentOnlinePayment,
59    ICustomStudentStudyCourse,
60    ICustomStudentStudyLevel,
61    ICustomUGStudentClearance,
62    ICustomPGStudentClearance,
63    ICustomStudentPersonal,
64    ICustomStudentPersonalEdit)
65from waeup.uniben.interfaces import MessageFactory as _
66
67class CustomOnlinePaymentDisplayFormPage(NigeriaOnlinePaymentDisplayFormPage):
68    """ Page to view an online payment ticket
69    """
70    grok.context(ICustomStudentOnlinePayment)
71    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
72        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
73    form_fields[
74        'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
75    form_fields[
76        'payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
77
78class CustomStudentClearanceEditFormPage(NigeriaStudentClearanceEditFormPage):
79    """ View to edit student clearance data by student
80    """
81
82    @property
83    def form_fields(self):
84        if self.context.is_postgrad:
85            form_fields = grok.AutoFields(ICustomPGStudentClearance).omit(
86            'clearance_locked', 'nysc_location', 'clr_code', 'officer_comment',
87            'physical_clearance_date')
88        else:
89            form_fields = grok.AutoFields(ICustomUGStudentClearance).omit(
90            'clearance_locked', 'clr_code', 'officer_comment',
91            'physical_clearance_date')
92            form_fields['date_of_birth'].for_display = True
93            form_fields['nationality'].for_display = True
94            form_fields['lga'].for_display = True
95        return form_fields
96
97    def dataNotComplete(self):
98        store = getUtility(IExtFileStore)
99        if not store.getFileByContext(self.context, attr=u'birth_certificate.jpg'):
100            return _('No birth certificate uploaded.')
101        if not store.getFileByContext(self.context, attr=u'ref_let.jpg'):
102            return _('No guarantor/referee letter uploaded.')
103        if not store.getFileByContext(self.context, attr=u'acc_let.jpg'):
104            return _('No acceptance letter uploaded.')
105        if not store.getFileByContext(self.context, attr=u'fst_sit_scan.jpg'):
106            return _('No first sitting result uploaded.')
107        #if not store.getFileByContext(self.context, attr=u'scd_sit_scan.jpg'):
108        #    return _('No second sitting result uploaded.')
109        if not store.getFileByContext(self.context, attr=u'secr_cults.jpg'):
110            return _('No affidavit of non-membership of secret cults uploaded.')
111        return False
112
113class CustomOnlinePaymentAddFormPage(NigeriaOnlinePaymentAddFormPage):
114    """ Page to add an online payment ticket
115    """
116    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).select(
117        'p_category')
118
119class CustomExportPDFPaymentSlip(NigeriaExportPDFPaymentSlip):
120    """Deliver a PDF slip of the context.
121    """
122    grok.context(ICustomStudentOnlinePayment)
123    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
124        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
125    form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
126    form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
127
128
129class CustomExportPDFClearanceSlip(NigeriaExportPDFClearanceSlip):
130    """Deliver a PDF slip of the context.
131    """
132
133    @property
134    def omit_fields(self):
135        omit_fields = ('password', 'suspended', 'suspended_comment',
136                       'phone', 'adm_code', 'email', 'date_of_birth',
137                       'flash_notice')
138        if self.context.is_jupeb:
139            omit_fields += ('faculty', 'department')
140        return omit_fields
141
142    @property
143    def label(self):
144        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
145        line0 = ''
146        if self.context.is_jupeb:
147            line0 = 'Joint Universities Preliminary Examinations Board (JUPEB)\n'
148        line1 = translate(_('Clearance Slip of'),
149            'waeup.kofa', target_language=portal_language) \
150            + ' %s' % self.context.display_fullname
151        return '%s%s' % (line0, line1)
152
153    def _sigsInFooter(self):
154        isStudent = getattr(
155            self.request.principal, 'user_type', None) == 'student'
156        if not isStudent and self.context.state in (CLEARED, RETURNING):
157            return (_('Date, Student Signature'),
158                    _('Date, Clearance Officer Signature'),
159                    )
160        return ()
161
162    def render(self):
163        studentview = StudentBasePDFFormPage(self.context.student,
164            self.request, self.omit_fields)
165        students_utils = getUtility(IStudentsUtils)
166        return students_utils.renderPDF(
167            self, 'clearance_slip.pdf',
168            self.context.student, studentview, signatures=self._signatures(),
169            sigs_in_footer=self._sigsInFooter(), show_scans=False,
170            omit_fields=self.omit_fields)
171
172
173class ExportClearanceInvitationSlip(UtilityView, grok.View):
174    """Deliver an invitation letter to physical clearance.
175
176    This form page is available only in Uniben.
177    """
178    grok.context(ICustomStudent)
179    grok.name('clearance_invitation_slip.pdf')
180    grok.require('waeup.viewStudent')
181    prefix = 'form'
182
183    label = u'Invitation Letter for Physical Clearance'
184
185    omit_fields = (
186        'suspended', 'phone', 'email',
187        'adm_code', 'suspended_comment',
188        'date_of_birth', 'current_level',
189        'department', 'current_mode',
190        'entry_session', 'matric_number', 'sex',
191        'flash_notice')
192
193    form_fields = []
194
195    @property
196    def note(self):
197        if self.context.physical_clearance_date:
198            return """
199<br /><br /><br /><br /><font size='12'>
200Dear %s,
201<br /><br /><br />
202You are invited for your physical clearance on:
203<br /><br />
204<strong>%s</strong>.
205<br /><br />
206Please bring along this letter of invitation to the University Main Auditorium
207<br /><br />
208on your clearance date.
209<br /><br /><br />
210Signed,
211<br /><br />
212The Registrar<br />
213</font>
214
215""" % (self.context.display_fullname, self.context.physical_clearance_date)
216        return
217
218
219    def update(self):
220        if self.context.student.state != REQUESTED \
221            or not  self.context.student.physical_clearance_date:
222            self.flash(_('Forbidden'), type="warning")
223            self.redirect(self.url(self.context))
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_invitation_slip',
231            self.context.student, studentview,
232            omit_fields=self.omit_fields,
233            note=self.note)
234
235class ExportExaminationScheduleSlip(UtilityView, grok.View):
236    """Deliver a examination schedule slip.
237
238    This form page is available only in Uniben and AAUE.
239    """
240    grok.context(ICustomStudent)
241    grok.name('examination_schedule_slip.pdf')
242    grok.require('waeup.viewStudent')
243    prefix = 'form'
244
245    label = u'Examination Schedule Slip'
246
247    omit_fields = (
248        'suspended', 'phone', 'email',
249        'adm_code', 'suspended_comment',
250        'date_of_birth', 'current_level',
251        'current_mode',
252        'entry_session',
253        'flash_notice')
254
255    form_fields = []
256
257    @property
258    def note(self):
259        return """
260<br /><br /><br /><br /><font size='12'>
261Your examination date, time and venue is scheduled as follows:
262<br /><br />
263<strong>%s</strong>
264</font>
265
266""" % self.context.flash_notice
267        return
268
269
270    def update(self):
271        if not self.context.flash_notice \
272            or not 'exam' in self.context.flash_notice.lower():
273            self.flash(_('Forbidden'), type="warning")
274            self.redirect(self.url(self.context))
275
276    def render(self):
277        studentview = StudentBasePDFFormPage(self.context.student,
278            self.request, self.omit_fields)
279        students_utils = getUtility(IStudentsUtils)
280        return students_utils.renderPDF(
281            self, 'examination_schedule_slip',
282            self.context.student, studentview,
283            omit_fields=self.omit_fields,
284            note=self.note)
285
286class ExportJUPEBResultSlip(ExportExaminationScheduleSlip):
287    """Deliver a JUPEB result slip.
288
289    This form page is available only in Uniben.
290    """
291
292    grok.name('jupeb_result_slip.pdf')
293    label = u'JUPEB Result Slip'
294
295    @property
296    def note(self):
297        return """
298<br /><br /><br /><br /><font size='12'>
299<strong>%s</strong>
300</font>
301<br /><br /><br /><br />
302<font size='8'>
303Key: A = 5, B = 4, C = 3, D = 2, E = 1, F = 0, X = Absent, Q = Cancelled
304</font>
305
306""" % self.context.flash_notice
307        return
308
309    def update(self):
310        if not self.context.flash_notice or not self.context.is_jupeb \
311            or not 'results' in self.context.flash_notice.lower():
312            self.flash(_('Forbidden'), type="warning")
313            self.redirect(self.url(self.context))
314
315class CustomStudentPersonalDisplayFormPage(
316    NigeriaStudentPersonalDisplayFormPage):
317    """ Page to display student personal data
318    """
319
320    form_fields = grok.AutoFields(ICustomStudentPersonal)
321    form_fields['perm_address'].custom_widget = BytesDisplayWidget
322    form_fields['next_kin_address'].custom_widget = BytesDisplayWidget
323    form_fields[
324        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
325
326class CustomStudentPersonalManageFormPage(
327    NigeriaStudentPersonalManageFormPage):
328    """ Page to manage personal data
329    """
330
331    form_fields = grok.AutoFields(ICustomStudentPersonal)
332    form_fields['personal_updated'].for_display = True
333    form_fields[
334        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
335
336class CstomStudentPersonalEditFormPage(NigeriaStudentPersonalEditFormPage):
337    """ Page to edit personal data
338    """
339    form_fields = grok.AutoFields(
340        ICustomStudentPersonalEdit).omit('personal_updated')
341
342class StudyCourseCOEditFormPage(KofaEditFormPage):
343    """ Page to edit the student study course data by clearance officers.
344
345    This form page is available only in Uniben.
346    """
347    grok.context(ICustomStudentStudyCourse)
348    grok.name('edit_level')
349    grok.require('waeup.clearStudent')
350    label = _('Edit current level')
351    pnav = 4
352    form_fields = grok.AutoFields(
353        ICustomStudentStudyCourse).select('current_level')
354
355    def update(self):
356        if not (self.context.is_current and
357            self.context.student.state == REQUESTED):
358            emit_lock_message(self)
359            return
360        super(StudyCourseCOEditFormPage, self).update()
361        return
362
363    @action(_('Save'), style='primary')
364    def save(self, **data):
365        try:
366            msave(self, **data)
367        except ConstraintNotSatisfied:
368            # The selected level might not exist in certificate
369            self.flash(_('Current level not available for certificate.'))
370            return
371        #notify(grok.ObjectModifiedEvent(self.context.__parent__))
372        return
373
374class CustomStudyLevelEditFormPage(StudyLevelEditFormPage):
375    """ Page to edit the student study level data by students.
376
377    """
378    grok.template('studyleveleditpage')
379
380class CustomStudyLevelDisplayFormPage(StudyLevelDisplayFormPage):
381    """ Page to display student study levels
382    """
383    grok.template('studylevelpage')
384
385class CustomExportPDFCourseRegistrationSlip(
386    ExportPDFCourseRegistrationSlip):
387    """Deliver a PDF slip of the context.
388    """
389
390    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
391        'level_verdict', 'gpa', 'level')
392
393    def update(self):
394        if self.context.student.state != REGISTERED \
395            or self.context.student.current_level != self.context.level:
396            self.flash(_('Forbidden'), type="warning")
397            self.redirect(self.url(self.context))
398
399    @property
400    def tabletitle(self):
401        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
402        tabletitle = []
403        tabletitle.append(translate(_('1st Semester Courses'), 'waeup.kofa',
404            target_language=portal_language))
405        tabletitle.append(translate(_('2nd Semester Courses'), 'waeup.kofa',
406            target_language=portal_language))
407        tabletitle.append(translate(_('Level Courses'), 'waeup.kofa',
408            target_language=portal_language))
409        tabletitle.append(translate(_('1st Trimester Courses'), 'waeup.kofa',
410            target_language=portal_language))
411        tabletitle.append(translate(_('2nd Trimester Courses'), 'waeup.kofa',
412            target_language=portal_language))
413        tabletitle.append(translate(_('3rd Trimester Courses'), 'waeup.kofa',
414            target_language=portal_language))
415        return tabletitle
416
417    def render(self):
418        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
419        Code = translate('Code', 'waeup.kofa', target_language=portal_language)
420        Title = translate('Title', 'waeup.kofa', target_language=portal_language)
421        Dept = translate('Dept.', 'waeup.kofa', target_language=portal_language)
422        Faculty = translate('Faculty', 'waeup.kofa', target_language=portal_language)
423        Cred = translate(_('Credits'), 'waeup.uniben', target_language=portal_language)
424        studentview = StudentBasePDFFormPage(self.context.student,
425            self.request, self.omit_fields)
426        students_utils = getUtility(IStudentsUtils)
427
428        tabledata = []
429        tableheader = []
430        for i in range(1,7):
431            tabledata.append(sorted(
432                [value for value in self.context.values() if value.semester == i],
433                key=lambda value: str(value.semester) + value.code))
434            tableheader.append([(Code,'code', 2.5),
435                             (Title,'title', 5),
436                             (Dept,'dcode', 1.5), (Faculty,'fcode', 1.5),
437                             (Cred, 'credits', 1.5),
438                             ])
439        return students_utils.renderPDF(
440            self, 'course_registration_slip.pdf',
441            self.context.student, studentview,
442            tableheader=tableheader,
443            tabledata=tabledata,
444            omit_fields=self.omit_fields
445            )
446
447class UnibenExportPDFCourseResultSlip(ExportPDFCourseRegistrationSlip):
448    """Deliver a PDF slip of the context.
449    """
450
451    grok.name('course_result_slip.pdf')
452
453    @property
454    def tabletitle(self):
455        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
456        tabletitle = []
457        tabletitle.append(translate(_('1st Semester Courses'), 'waeup.kofa',
458            target_language=portal_language))
459        tabletitle.append(translate(_('2nd Semester Courses'), 'waeup.kofa',
460            target_language=portal_language))
461        tabletitle.append(translate(_('Level Courses'), 'waeup.kofa',
462            target_language=portal_language))
463        tabletitle.append(translate(_('1st Trimester Courses'), 'waeup.kofa',
464            target_language=portal_language))
465        tabletitle.append(translate(_('2nd Trimester Courses'), 'waeup.kofa',
466            target_language=portal_language))
467        tabletitle.append(translate(_('3rd Trimester Courses'), 'waeup.kofa',
468            target_language=portal_language))
469        return tabletitle
470
471    @property
472    def label(self):
473        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
474        lang = self.request.cookies.get('kofa.language', portal_language)
475        level_title = translate(self.context.level_title, 'waeup.kofa',
476            target_language=lang)
477        return translate(_('Course Result Slip'),
478            'waeup.uniben', target_language=portal_language) \
479            + ' %s' % level_title
480
481    def render(self):
482        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
483        Code = translate('Code', 'waeup.kofa', target_language=portal_language)
484        Title = translate('Title', 'waeup.kofa', target_language=portal_language)
485        Dept = translate('Dept.', 'waeup.kofa', target_language=portal_language)
486        Faculty = translate('Faculty', 'waeup.kofa', target_language=portal_language)
487        Cred = translate(_('Credits'), 'waeup.uniben', target_language=portal_language)
488        Grade = translate('Grade', 'waeup.kofa', target_language=portal_language)
489        studentview = StudentBasePDFFormPage(self.context.student,
490            self.request, self.omit_fields)
491        students_utils = getUtility(IStudentsUtils)
492
493        tabledata = []
494        tableheader = []
495        for i in range(1,7):
496            tabledata.append(sorted(
497                [value for value in self.context.values() if value.semester == i],
498                key=lambda value: str(value.semester) + value.code))
499            tableheader.append([(Code,'code', 2.5),
500                             (Title,'title', 5),
501                             (Dept,'dcode', 1.5), (Faculty,'fcode', 1.5),
502                             (Cred, 'credits', 1.5),
503                             (Grade, 'grade', 1.5),
504                             ])
505        return students_utils.renderPDF(
506            self, 'course_registration_slip.pdf',
507            self.context.student, studentview,
508            tableheader=tableheader,
509            tabledata=tabledata,
510            omit_fields=self.omit_fields
511            )
512
513class CustomCourseTicketDisplayFormPage(CourseTicketDisplayFormPage):
514    """ Page to display course tickets
515    """
516    form_fields = grok.AutoFields(ICourseTicket).omit('score')
517
518class CustomStudentActivateView(StudentActivateView):
519    """ Activate student account
520    """
521
522    def update(self):
523        self.context.suspended = False
524        self.context.writeLogMessage(self, 'account activated')
525        history = IObjectHistory(self.context)
526        history.addMessage('Student account activated', user='undisclosed')
527        self.flash(_('Student account has been activated.'))
528        self.redirect(self.url(self.context))
529        return
530
531class CustomStudentDeactivateView(StudentDeactivateView):
532    """ Deactivate student account
533    """
534    def update(self):
535        self.context.suspended = True
536        self.context.writeLogMessage(self, 'account deactivated')
537        history = IObjectHistory(self.context)
538        history.addMessage('Student account deactivated', user='undisclosed')
539        self.flash(_('Student account has been deactivated.'))
540        self.redirect(self.url(self.context))
541        return
542
543class CustomExportPDFTranscriptSlip(ExportPDFTranscriptSlip):
544    """Deliver a PDF slip of the context.
545    """
546
547    #def _sigsInFooter(self):
548    #    isStudent = getattr(
549    #        self.request.principal, 'user_type', None) == 'student'
550    #    if not isStudent:
551    #        return (_('D. R. (Exams & Records)'),_('Current Dean of Faculty'),)
552    #    return ()
553
554    #def _signatures(self):
555    #    return ([(
556    #        'Current HD<br /> D. R. (Exams & Records)<br /> '
557    #        'For: Registrar')],)
558
559    def render(self):
560        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
561        Term = translate(_('Term'), 'waeup.kofa', target_language=portal_language)
562        Code = translate(_('Code'), 'waeup.kofa', target_language=portal_language)
563        Title = translate(_('Title'), 'waeup.kofa', target_language=portal_language)
564        Cred = translate(_('Credits'), 'waeup.kofa', target_language=portal_language)
565        #Score = translate(_('Score'), 'waeup.kofa', target_language=portal_language)
566        Grade = translate(_('Grade'), 'waeup.kofa', target_language=portal_language)
567        studentview = StudentBasePDFFormPage(self.context.student,
568            self.request, self.omit_fields)
569        students_utils = getUtility(IStudentsUtils)
570
571        tableheader = [(Code,'code', 2.5),
572                         (Title,'title', 8.5),
573                         (Term, 'semester', 1.5),
574                         (Cred, 'credits', 1.5),
575                         #(Score, 'score', 1.5),
576                         (Grade, 'grade', 1.5),
577                         ]
578
579        pdfstream = students_utils.renderPDFTranscript(
580            self, 'transcript.pdf',
581            self.context.student, studentview,
582            omit_fields=self.omit_fields,
583            tableheader=tableheader,
584            signatures=self._signatures(),
585            sigs_in_footer=self._sigsInFooter(),
586            digital_sigs=self._digital_sigs(),
587            save_file=self._save_file(),
588            )
589        if not pdfstream:
590            self.redirect(self.url(self.context.student))
591            return
592        return pdfstream
593
594class CustomExportPDFBedTicketSlip(NigeriaExportPDFBedTicketSlip):
595    """Deliver a PDF slip of the context.
596    """
597
598    omit_fields = ('password', 'suspended', 'suspended_comment',
599        'phone', 'adm_code', 'email', 'date_of_birth', 'flash_notice')
600
601class CustomPaymentsManageFormPage(PaymentsManageFormPage):
602    """ Page to manage the student payments. This manage form page is for
603    both students and students officers. Uniben does not allow students
604    to remove any payment ticket.
605    """
606    @property
607    def manage_payments_allowed(self):
608        return checkPermission('waeup.manageStudent', self.context)
609
610class CustomAccommodationManageFormPage(NigeriaAccommodationManageFormPage):
611    """ Page to manage bed tickets.
612    This manage form page is for both students and students officers.
613    """
614    with_hostel_selection = True
Note: See TracBrowser for help on using the repository browser.