## $Id: browser.py 16171 2020-07-19 20:56:10Z henrik $ ## ## Copyright (C) 2012 Uli Fouquet & Henrik Bettermann ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## import grok from zope.i18n import translate from zope.security import checkPermission from zope.schema.interfaces import ConstraintNotSatisfied from zope.formlib.textwidgets import BytesDisplayWidget from zope.component import getUtility from hurry.workflow.interfaces import IWorkflowInfo from waeup.kofa.interfaces import ( REQUESTED, IExtFileStore, IKofaUtils, IObjectHistory) from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget from waeup.kofa.browser.layout import action, KofaEditFormPage, UtilityView from waeup.kofa.students.browser import ( StudentBaseEditFormPage, StudyLevelEditFormPage, StudyLevelDisplayFormPage, StudentBasePDFFormPage, ExportPDFCourseRegistrationSlip, CourseTicketDisplayFormPage, StudentTriggerTransitionFormPage, msave, emit_lock_message, StudentActivateView, StudentDeactivateView, ExportPDFTranscriptSlip, PaymentsManageFormPage, StartClearancePage) from waeup.kofa.students.workflow import (CREATED, ADMITTED, PAID, CLEARANCE, REQUESTED, RETURNING, CLEARED, REGISTERED, VALIDATED, GRADUATED, TRANSREQ, TRANSVAL, TRANSREL, FORBIDDEN_POSTGRAD_TRANS) from waeup.kofa.students.interfaces import IStudentsUtils, ICourseTicket from waeup.kofa.students.workflow import FORBIDDEN_POSTGRAD_TRANS from kofacustom.nigeria.students.browser import ( NigeriaOnlinePaymentDisplayFormPage, NigeriaStudentBaseManageFormPage, NigeriaStudentClearanceEditFormPage, NigeriaOnlinePaymentAddFormPage, NigeriaExportPDFPaymentSlip, NigeriaExportPDFClearanceSlip, NigeriaExportPDFBedTicketSlip, NigeriaStudentPersonalDisplayFormPage, NigeriaStudentPersonalManageFormPage, NigeriaStudentPersonalEditFormPage, NigeriaAccommodationManageFormPage, NigeriaAccommodationDisplayFormPage, NigeriaStudentBaseDisplayFormPage, ) from waeup.uniben.students.interfaces import ( ICustomStudent, ICustomStudentBase, ICustomStudentOnlinePayment, ICustomStudentStudyCourse, ICustomStudentStudyLevel, ICustomUGStudentClearance, ICustomPGStudentClearance, ICustomStudentPersonal, ICustomStudentPersonalEdit) from waeup.uniben.interfaces import MessageFactory as _ class CustomStudentBaseDisplayFormPage(NigeriaStudentBaseDisplayFormPage): """ Page to display student base data """ form_fields = grok.AutoFields(ICustomStudentBase).omit( 'password', 'suspended', 'suspended_comment', 'flash_notice', 'provisionally_cleared', 'parents_email') form_fields[ 'financial_clearance_date'].custom_widget = FriendlyDatetimeDisplayWidget('le') class CustomStudentBaseManageFormPage(NigeriaStudentBaseManageFormPage): """ View to manage student base data """ form_fields = grok.AutoFields(ICustomStudentBase).omit( 'student_id', 'adm_code', 'suspended', 'financially_cleared_by', 'financial_clearance_date', 'parents_email') class CustomOnlinePaymentDisplayFormPage(NigeriaOnlinePaymentDisplayFormPage): """ Page to view an online payment ticket """ grok.context(ICustomStudentOnlinePayment) form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit( 'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item') form_fields[ 'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le') form_fields[ 'payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le') class CustomStartClearancePage(StartClearancePage): @property def with_ac(self): if self.context.faccode == 'DCOEM': return False return True class CustomStudentClearanceEditFormPage(NigeriaStudentClearanceEditFormPage): """ View to edit student clearance data by student """ @property def form_fields(self): if self.context.is_postgrad: form_fields = grok.AutoFields(ICustomPGStudentClearance).omit( 'clearance_locked', 'nysc_location', 'clr_code', 'officer_comment', 'physical_clearance_date') else: form_fields = grok.AutoFields(ICustomUGStudentClearance).omit( 'clearance_locked', 'clr_code', 'officer_comment', 'physical_clearance_date') form_fields['date_of_birth'].for_display = True form_fields['nationality'].for_display = True form_fields['lga'].for_display = True return form_fields def dataNotComplete(self): store = getUtility(IExtFileStore) if not store.getFileByContext(self.context, attr=u'birth_certificate.jpg'): return _('No birth certificate uploaded.') if not store.getFileByContext(self.context, attr=u'ref_let.jpg'): return _('No guarantor/referee letter uploaded.') if not store.getFileByContext(self.context, attr=u'acc_let.jpg'): return _('No acceptance letter uploaded.') if not store.getFileByContext(self.context, attr=u'fst_sit_scan.jpg'): return _('No first sitting result uploaded.') #if not store.getFileByContext(self.context, attr=u'scd_sit_scan.jpg'): # return _('No second sitting result uploaded.') if not store.getFileByContext(self.context, attr=u'secr_cults.jpg'): return _('No affidavit of non-membership of secret cults uploaded.') return False class CustomOnlinePaymentAddFormPage(NigeriaOnlinePaymentAddFormPage): """ Page to add an online payment ticket """ form_fields = grok.AutoFields(ICustomStudentOnlinePayment).select( 'p_combi') class CustomExportPDFPaymentSlip(NigeriaExportPDFPaymentSlip): """Deliver a PDF slip of the context. """ grok.context(ICustomStudentOnlinePayment) form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit( 'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item', 'p_combi') form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le') form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le') class CustomExportPDFClearanceSlip(NigeriaExportPDFClearanceSlip): """Deliver a PDF slip of the context. """ @property def omit_fields(self): omit_fields = ('password', 'suspended', 'suspended_comment', 'phone', 'adm_code', 'email', 'date_of_birth', 'flash_notice') if self.context.is_jupeb: omit_fields += ('faculty', 'department') return omit_fields @property def label(self): portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE line0 = '' if self.context.is_jupeb: line0 = 'Joint Universities Preliminary Examinations Board (JUPEB)\n' line1 = translate(_('Clearance Slip of'), 'waeup.kofa', target_language=portal_language) \ + ' %s' % self.context.display_fullname return '%s%s' % (line0, line1) def _sigsInFooter(self): isStudent = getattr( self.request.principal, 'user_type', None) == 'student' if not isStudent and self.context.state in (CLEARED, RETURNING): return (_('Date, Student Signature'), _('Date, Clearance Officer Signature'), ) return () def render(self): studentview = StudentBasePDFFormPage(self.context.student, self.request, self.omit_fields) students_utils = getUtility(IStudentsUtils) return students_utils.renderPDF( self, 'clearance_slip.pdf', self.context.student, studentview, signatures=self._signatures(), sigs_in_footer=self._sigsInFooter(), show_scans=False, omit_fields=self.omit_fields) class ExportClearanceInvitationSlip(UtilityView, grok.View): """Deliver an invitation letter to physical clearance. This form page is available only in Uniben. """ grok.context(ICustomStudent) grok.name('clearance_invitation_slip.pdf') grok.require('waeup.viewStudent') prefix = 'form' label = u'Invitation Letter for Physical Clearance' omit_fields = ( 'suspended', 'phone', 'email', 'adm_code', 'suspended_comment', 'date_of_birth', 'current_level', 'department', 'current_mode', 'entry_session', 'matric_number', 'sex', 'flash_notice') form_fields = [] @property def note(self): if self.context.physical_clearance_date: return """



Dear %s,


You are invited for your physical clearance on:

%s.

Please bring along this letter of invitation to the University Main Auditorium

on your clearance date.


Signed,

The Registrar
""" % (self.context.display_fullname, self.context.physical_clearance_date) return def update(self): if self.context.student.state != REQUESTED \ or not self.context.student.physical_clearance_date: self.flash(_('Forbidden'), type="warning") self.redirect(self.url(self.context)) def render(self): studentview = StudentBasePDFFormPage(self.context.student, self.request, self.omit_fields) students_utils = getUtility(IStudentsUtils) return students_utils.renderPDF( self, 'clearance_invitation_slip', self.context.student, studentview, omit_fields=self.omit_fields, note=self.note) class ExportExaminationScheduleSlip(UtilityView, grok.View): """Deliver a examination schedule slip. This form page is available only in Uniben and AAUE. """ grok.context(ICustomStudent) grok.name('examination_schedule_slip.pdf') grok.require('waeup.viewStudent') prefix = 'form' label = u'Examination Schedule Slip' omit_fields = ( 'suspended', 'phone', 'email', 'adm_code', 'suspended_comment', 'date_of_birth', 'current_level', 'current_mode', 'entry_session', 'flash_notice') form_fields = [] @property def note(self): return """



Your examination date, time and venue is scheduled as follows:

%s
""" % self.context.flash_notice return def update(self): if not self.context.flash_notice \ or not 'exam' in self.context.flash_notice.lower(): self.flash(_('Forbidden'), type="warning") self.redirect(self.url(self.context)) def render(self): studentview = StudentBasePDFFormPage(self.context.student, self.request, self.omit_fields) students_utils = getUtility(IStudentsUtils) return students_utils.renderPDF( self, 'examination_schedule_slip', self.context.student, studentview, omit_fields=self.omit_fields, note=self.note) class SwitchLibraryAccessView(UtilityView, grok.View): """ Switch the library attribute """ grok.context(ICustomStudent) grok.name('switch_library_access') grok.require('waeup.switchLibraryAccess') def update(self): if self.context.library: self.context.library = False self.context.writeLogMessage(self, 'library access disabled') self.flash(_('Library access disabled')) else: self.context.library = True self.context.writeLogMessage(self, 'library access enabled') self.flash(_('Library access enabled')) self.redirect(self.url(self.context)) return def render(self): return class ExportJHLIdCard(UtilityView, grok.View): """Deliver an id card for the John Harris Library. """ grok.context(ICustomStudent) grok.name('jhl_idcard.pdf') grok.require('waeup.viewStudent') prefix = 'form' label = u"John Harris Library Clearance" omit_fields = ( 'suspended', 'email', 'phone', 'adm_code', 'suspended_comment', 'date_of_birth', 'current_mode', 'certificate', 'entry_session', 'flash_notice') form_fields = [] def _sigsInFooter(self): isStudent = getattr( self.request.principal, 'user_type', None) == 'student' if isStudent: return '' return (_("Date, Reader's Signature"), _("Date, Circulation Librarian's Signature"), ) def update(self): if not self.context.library: self.flash(_('Forbidden!'), type="danger") self.redirect(self.url(self.context)) return @property def note(self): return """



This is to certify that the bearer whose photograph and other details appear overleaf is a registered user of John Harris Library, University of Benin. The card is not transferable. A replacement fee is charged for a loss, mutilation or otherwise. If found, please, return to John Harris Library, University of Benin, Benin City. """ return def render(self): studentview = StudentBasePDFFormPage(self.context.student, self.request, self.omit_fields) students_utils = getUtility(IStudentsUtils) return students_utils.renderPDF( self, 'jhl_idcard', self.context.student, studentview, omit_fields=self.omit_fields, sigs_in_footer=self._sigsInFooter(), note=self.note) class ExportJUPEBResultSlip(ExportExaminationScheduleSlip): """Deliver a JUPEB result slip. This form page is available only in Uniben. """ grok.name('jupeb_result_slip.pdf') label = u'JUPEB Result Slip' @property def note(self): return """



%s



Key: A = 5, B = 4, C = 3, D = 2, E = 1, F = 0, X = Absent, Q = Cancelled """ % self.context.flash_notice return def update(self): if not self.context.flash_notice or not self.context.is_jupeb \ or not 'results' in self.context.flash_notice.lower(): self.flash(_('Forbidden'), type="warning") self.redirect(self.url(self.context)) class CustomStudentPersonalDisplayFormPage( NigeriaStudentPersonalDisplayFormPage): """ Page to display student personal data """ form_fields = grok.AutoFields(ICustomStudentPersonal) form_fields['perm_address'].custom_widget = BytesDisplayWidget form_fields['next_kin_address'].custom_widget = BytesDisplayWidget form_fields[ 'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le') class CustomStudentPersonalManageFormPage( NigeriaStudentPersonalManageFormPage): """ Page to manage personal data """ form_fields = grok.AutoFields(ICustomStudentPersonal) form_fields['personal_updated'].for_display = True form_fields[ 'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le') class CstomStudentPersonalEditFormPage(NigeriaStudentPersonalEditFormPage): """ Page to edit personal data """ form_fields = grok.AutoFields( ICustomStudentPersonalEdit).omit('personal_updated') class StudyCourseCOEditFormPage(KofaEditFormPage): """ Page to edit the student study course data by clearance officers. This form page is available only in Uniben. """ grok.context(ICustomStudentStudyCourse) grok.name('edit_level') grok.require('waeup.clearStudent') label = _('Edit current level') pnav = 4 form_fields = grok.AutoFields( ICustomStudentStudyCourse).select('current_level') def update(self): if not (self.context.is_current and self.context.student.state == REQUESTED): emit_lock_message(self) return super(StudyCourseCOEditFormPage, self).update() return @action(_('Save'), style='primary') def save(self, **data): try: msave(self, **data) except ConstraintNotSatisfied: # The selected level might not exist in certificate self.flash(_('Current level not available for certificate.')) return #notify(grok.ObjectModifiedEvent(self.context.__parent__)) return class CustomStudyLevelEditFormPage(StudyLevelEditFormPage): """ Page to edit the student study level data by students. """ grok.template('studyleveleditpage') class CustomStudyLevelDisplayFormPage(StudyLevelDisplayFormPage): """ Page to display student study levels """ grok.template('studylevelpage') class CustomExportPDFCourseRegistrationSlip( ExportPDFCourseRegistrationSlip): """Deliver a PDF slip of the context. """ form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit( 'level_verdict', 'gpa', 'level', 'transcript_remark') def update(self): if self.context.student.state != REGISTERED \ or self.context.student.current_level != self.context.level: self.flash(_('Forbidden'), type="warning") self.redirect(self.url(self.context)) @property def tabletitle(self): portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE tabletitle = [] tabletitle.append(translate(_('1st Semester Courses'), 'waeup.kofa', target_language=portal_language)) tabletitle.append(translate(_('2nd Semester Courses'), 'waeup.kofa', target_language=portal_language)) tabletitle.append(translate(_('Level Courses'), 'waeup.kofa', target_language=portal_language)) tabletitle.append(translate(_('1st Trimester Courses'), 'waeup.kofa', target_language=portal_language)) tabletitle.append(translate(_('2nd Trimester Courses'), 'waeup.kofa', target_language=portal_language)) tabletitle.append(translate(_('3rd Trimester Courses'), 'waeup.kofa', target_language=portal_language)) return tabletitle def render(self): portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE Code = translate('Code', 'waeup.kofa', target_language=portal_language) Title = translate('Title', 'waeup.kofa', target_language=portal_language) Dept = translate('Dept.', 'waeup.kofa', target_language=portal_language) Faculty = translate('Faculty', 'waeup.kofa', target_language=portal_language) Cred = translate(_('Credits'), 'waeup.uniben', target_language=portal_language) studentview = StudentBasePDFFormPage(self.context.student, self.request, self.omit_fields) students_utils = getUtility(IStudentsUtils) tabledata = [] tableheader = [] for i in range(1,7): tabledata.append(sorted( [value for value in self.context.values() if value.semester == i], key=lambda value: str(value.semester) + value.code)) tableheader.append([(Code,'code', 2.5), (Title,'title', 5), (Dept,'dcode', 1.5), (Faculty,'fcode', 1.5), (Cred, 'credits', 1.5), ]) return students_utils.renderPDF( self, 'course_registration_slip.pdf', self.context.student, studentview, tableheader=tableheader, tabledata=tabledata, omit_fields=self.omit_fields ) class ExportPDFCourseResultSlip(ExportPDFCourseRegistrationSlip): """Deliver a PDF slip of the context. """ grok.name('course_result_slip.pdf') form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit('level') @property def tabletitle(self): portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE tabletitle = [] tabletitle.append(translate(_('1st Semester Courses'), 'waeup.kofa', target_language=portal_language)) tabletitle.append(translate(_('2nd Semester Courses'), 'waeup.kofa', target_language=portal_language)) tabletitle.append(translate(_('Level Courses'), 'waeup.kofa', target_language=portal_language)) tabletitle.append(translate(_('1st Trimester Courses'), 'waeup.kofa', target_language=portal_language)) tabletitle.append(translate(_('2nd Trimester Courses'), 'waeup.kofa', target_language=portal_language)) tabletitle.append(translate(_('3rd Trimester Courses'), 'waeup.kofa', target_language=portal_language)) return tabletitle @property def label(self): portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE lang = self.request.cookies.get('kofa.language', portal_language) level_title = translate(self.context.level_title, 'waeup.kofa', target_language=lang) return translate(_('Course Result Slip'), 'waeup.uniben', target_language=portal_language) \ + ' %s' % level_title def render(self): portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE Code = translate('Code', 'waeup.kofa', target_language=portal_language) Title = translate('Title', 'waeup.kofa', target_language=portal_language) Dept = translate('Dept.', 'waeup.kofa', target_language=portal_language) Faculty = translate('Faculty', 'waeup.kofa', target_language=portal_language) Cred = translate(_('Credits'), 'waeup.uniben', target_language=portal_language) Grade = translate('Grade', 'waeup.kofa', target_language=portal_language) studentview = StudentBasePDFFormPage(self.context.student, self.request, self.omit_fields) students_utils = getUtility(IStudentsUtils) tabledata = [] tableheader = [] for i in range(1,7): tabledata.append(sorted( [value for value in self.context.values() if value.semester == i], key=lambda value: str(value.semester) + value.code)) tableheader.append([(Code,'code', 2.5), (Title,'title', 5), (Dept,'dcode', 1.5), (Faculty,'fcode', 1.5), (Cred, 'credits', 1.5), (Grade, 'grade', 1.5), ]) return students_utils.renderPDF( self, 'course_result_slip.pdf', self.context.student, studentview, tableheader=tableheader, tabledata=tabledata, omit_fields=self.omit_fields ) class CustomCourseTicketDisplayFormPage(CourseTicketDisplayFormPage): """ Page to display course tickets """ form_fields = grok.AutoFields(ICourseTicket).omit('score') class CustomStudentActivateView(StudentActivateView): """ Activate student account """ def update(self): self.context.suspended = False self.context.writeLogMessage(self, 'account activated') history = IObjectHistory(self.context) history.addMessage('Student account activated', user='undisclosed') self.flash(_('Student account has been activated.')) self.redirect(self.url(self.context)) return class CustomStudentDeactivateView(StudentDeactivateView): """ Deactivate student account """ def update(self): self.context.suspended = True self.context.writeLogMessage(self, 'account deactivated') history = IObjectHistory(self.context) history.addMessage('Student account deactivated', user='undisclosed') self.flash(_('Student account has been deactivated.')) self.redirect(self.url(self.context)) return class CustomExportPDFTranscriptSlip(ExportPDFTranscriptSlip): """Deliver a PDF slip of the context. """ def _sigsInFooter(self): isStudent = getattr( self.request.principal, 'user_type', None) == 'student' if not isStudent and self.context.student.state in (TRANSVAL, TRANSREL): return (_('D. R. (Exams & Records)'),_('Current Dean of Faculty'),) return () #def _signatures(self): # return ([( # 'Current HD
D. R. (Exams & Records)
' # 'For: Registrar')],) def render(self): portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE Term = translate(_('Term'), 'waeup.kofa', target_language=portal_language) Code = translate(_('Code'), 'waeup.kofa', target_language=portal_language) Title = translate(_('Title'), 'waeup.kofa', target_language=portal_language) Cred = translate(_('Credits'), 'waeup.kofa', target_language=portal_language) #Score = translate(_('Score'), 'waeup.kofa', target_language=portal_language) Grade = translate(_('Grade'), 'waeup.kofa', target_language=portal_language) studentview = StudentBasePDFFormPage(self.context.student, self.request, self.omit_fields) students_utils = getUtility(IStudentsUtils) tableheader = [(Code,'code', 2.5), (Title,'title', 8.5), (Term, 'semester', 1.5), (Cred, 'credits', 1.5), #(Score, 'score', 1.5), (Grade, 'grade', 1.5), ] pdfstream = students_utils.renderPDFTranscript( self, 'transcript.pdf', self.context.student, studentview, omit_fields=self.omit_fields, tableheader=tableheader, signatures=self._signatures(), sigs_in_footer=self._sigsInFooter(), digital_sigs=self._digital_sigs(), save_file=self._save_file(), ) if not pdfstream: self.redirect(self.url(self.context.student)) return return pdfstream class CustomExportPDFBedTicketSlip(NigeriaExportPDFBedTicketSlip): """Deliver a PDF slip of the context. """ omit_fields = ('password', 'suspended', 'suspended_comment', 'phone', 'adm_code', 'email', 'date_of_birth', 'flash_notice') class CustomPaymentsManageFormPage(PaymentsManageFormPage): """ Page to manage the student payments. This manage form page is for both students and students officers. Uniben does not allow students to remove any payment ticket. """ @property def manage_payments_allowed(self): return checkPermission('waeup.manageStudent', self.context) class CustomAccommodationDisplayFormPage(NigeriaAccommodationDisplayFormPage): """ Page to view bed tickets. """ with_hostel_selection = True class CustomAccommodationManageFormPage(NigeriaAccommodationManageFormPage): """ Page to manage bed tickets. This manage form page is for both students and students officers. """ with_hostel_selection = True class CustomStudentBaseEditFormPage(StudentBaseEditFormPage): """ View to edit student base data """ form_fields = grok.AutoFields(ICustomStudentBase).select( 'email', 'phone') form_fields['email'].field.required = True