## $Id: browser.py 15503 2019-07-19 06:06:17Z henrik $ ## ## Copyright (C) 2011 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 ## """UI components for basic applicants and related components. """ import grok import os from zope.component import getUtility, getAdapter from zope.i18n import translate from hurry.workflow.interfaces import IWorkflowState from waeup.kofa.interfaces import ( IExtFileStore, IFileStoreNameChooser, IKofaUtils) from zope.formlib.textwidgets import BytesDisplayWidget from waeup.kofa.utils.helpers import string_from_bytes, file_size from waeup.kofa.applicants.browser import ( ApplicantCheckStatusPage, ApplicantBaseDisplayFormPage) from waeup.kofa.applicants.workflow import STARTED, PAID from waeup.kofa.applicants.viewlets import PDFActionButton from waeup.kofa.applicants.interfaces import IApplicantRegisterUpdate from waeup.kofa.browser.layout import UtilityView from waeup.kofa.students.interfaces import IStudentsUtils from waeup.kofa.interfaces import IPDF from waeup.kofa.browser.viewlets import ManageActionButton from waeup.aaue.interfaces import MessageFactory as _ from kofacustom.nigeria.applicants.browser import ( NigeriaApplicantDisplayFormPage, NigeriaApplicantManageFormPage, NigeriaApplicantEditFormPage, NigeriaPDFApplicationSlip, NigeriaApplicantRegistrationPage, NigeriaExportPDFPaymentSlipPage, ) from kofacustom.nigeria.applicants.interfaces import OMIT_DISPLAY_FIELDS from waeup.aaue.applicants.interfaces import ( ICustomUGApplicant, ICustomUGApplicantEdit, ITranscriptApplicant, ICertificateRequest, ICustomApplicant ) UG_OMIT_FIELDS = ( 'hq_type', 'hq_fname', 'hq_matric_no', 'hq_degree', 'hq_school', 'hq_session', 'hq_disc', 'hq_type2', 'hq_fname2', 'hq_matric_no2', 'hq_degree2', 'hq_school2', 'hq_session2', 'hq_disc2', 'hq_type3', 'hq_fname3', 'hq_matric_no3', 'hq_degree3', 'hq_school3', 'hq_session3', 'hq_disc3', 'nysc_year', 'nysc_location', 'nysc_lga', 'employer', 'emp_position', 'emp_start', 'emp_end', 'emp_reason', 'employer2', 'emp2_position', 'emp2_start', 'emp2_end', 'emp2_reason', 'former_matric', ) UG_OMIT_DISPLAY_FIELDS = OMIT_DISPLAY_FIELDS + ( 'jamb_subjects_list', 'master_sheet_number') + UG_OMIT_FIELDS UG_OMIT_PDF_FIELDS = UG_OMIT_DISPLAY_FIELDS + UG_OMIT_FIELDS + ( 'alr_fname', 'alr_no', 'alr_date', 'alr_results', 'notice') UG_OMIT_MANAGE_FIELDS = ( 'special_application','jamb_subjects_list',) + UG_OMIT_FIELDS UG_OMIT_EDIT_FIELDS = UG_OMIT_MANAGE_FIELDS + OMIT_DISPLAY_FIELDS + ( 'student_id', 'notice', 'jamb_age', 'jamb_subjects', 'jamb_score', 'jamb_reg_number', 'aggregate', 'master_sheet_number', 'screening_venue', 'screening_score', 'screening_date' ) UDE_OMIT_FIELDS = ( 'nysc_year', 'nysc_location', 'nysc_lga', 'employer', 'emp_position', 'emp_start', 'emp_end', 'emp_reason', 'employer2', 'emp2_position', 'emp2_start', 'emp2_end', 'emp2_reason', 'former_matric', ) UDE_OMIT_DISPLAY_FIELDS = OMIT_DISPLAY_FIELDS + ( 'jamb_subjects_list', 'master_sheet_number') + UDE_OMIT_FIELDS UDE_OMIT_PDF_FIELDS = UDE_OMIT_DISPLAY_FIELDS + UDE_OMIT_FIELDS + ( #'alr_fname', 'alr_no', 'alr_date', 'alr_results', 'hq_type2', 'hq_fname2', 'hq_matric_no2', 'hq_degree2', 'hq_school2', 'hq_session2', 'hq_disc2', 'hq_type3', 'hq_fname3', 'hq_matric_no3', 'hq_degree3', 'hq_school3', 'hq_session3', 'hq_disc3', 'notice') UDE_OMIT_MANAGE_FIELDS = ( 'special_application','jamb_subjects_list',) + UDE_OMIT_FIELDS UDE_OMIT_EDIT_FIELDS = UDE_OMIT_MANAGE_FIELDS + OMIT_DISPLAY_FIELDS + ( 'student_id', 'notice', 'jamb_age', 'jamb_subjects', 'jamb_score', 'jamb_reg_number', 'aggregate', 'master_sheet_number', 'screening_venue', 'screening_score', 'screening_date' ) #UG_OMIT_PDF_FIELDS = tuple([ # element for element in UG_OMIT_PDF_FIELDS if not element == 'phone']) #UG_OMIT_PDF_FIELDS += ( # 'reg_number','alr_fname', 'alr_no', 'alr_date', # 'alr_results', 'notice' # ) PG_OMIT_FIELDS = ( 'fst_sit_fname', 'fst_sit_no', 'fst_sit_date', 'fst_sit_type', 'fst_sit_results', 'scd_sit_fname', 'scd_sit_no', 'scd_sit_date', 'scd_sit_type', 'scd_sit_results', #'programme_type', 'jamb_age', 'jamb_subjects', 'jamb_score', 'jamb_reg_number', 'aggregate' ) PG_OMIT_DISPLAY_FIELDS = OMIT_DISPLAY_FIELDS + ( 'jamb_subjects_list',) + PG_OMIT_FIELDS PG_OMIT_PDF_FIELDS = PG_OMIT_DISPLAY_FIELDS + PG_OMIT_FIELDS + ( 'reg_number','alr_fname', 'alr_no', 'alr_date', 'alr_results', 'notice', 'nysc_year', 'nysc_location', 'nysc_lga', 'former_matric', ) PG_OMIT_MANAGE_FIELDS = ( 'special_application','jamb_subjects_list',) + PG_OMIT_FIELDS PG_OMIT_EDIT_FIELDS = PG_OMIT_MANAGE_FIELDS + OMIT_DISPLAY_FIELDS + ( 'student_id', 'notice', ) PTEE_OMIT_FIELDS = ( 'jamb_age', 'jamb_subjects', 'jamb_score', 'jamb_reg_number', 'aggregate' ) PTEE_OMIT_DISPLAY_FIELDS = OMIT_DISPLAY_FIELDS + ( 'jamb_subjects_list',) + PTEE_OMIT_FIELDS PTEE_OMIT_PDF_FIELDS = PTEE_OMIT_DISPLAY_FIELDS + PTEE_OMIT_FIELDS + ( 'reg_number','alr_fname', 'alr_no', 'alr_date', 'alr_results', 'notice', 'nysc_year', 'nysc_location', 'nysc_lga', 'employer', 'emp_position', 'emp_start', 'emp_end', 'emp_reason', 'employer2', 'emp2_position', 'emp2_start', 'emp2_end', 'emp2_reason', 'former_matric', ) PTEE_OMIT_MANAGE_FIELDS = ( 'special_application','jamb_subjects_list',) + PTEE_OMIT_FIELDS PTEE_OMIT_EDIT_FIELDS = PTEE_OMIT_MANAGE_FIELDS + OMIT_DISPLAY_FIELDS + ( 'student_id', 'notice', ) UPDATE_OMIT_FIELDS = ( 'firstname', 'middlename', 'lastname', 'sex', 'lga', 'course1', ) MAX_FILE_UPLOAD_SIZE = 1024 * 500 def handle_file_upload(upload, context, view, attr=None): """Handle upload of applicant files. Returns `True` in case of success or `False`. Please note that file pointer passed in (`upload`) most probably points to end of file when leaving this function. """ size = file_size(upload) if size > MAX_FILE_UPLOAD_SIZE: view.flash(_('Uploaded file is too big!')) return False dummy, ext = os.path.splitext(upload.filename) ext.lower() if ext != '.pdf': view.flash(_('pdf file extension expected.')) return False upload.seek(0) # file pointer moved when determining size store = getUtility(IExtFileStore) file_id = IFileStoreNameChooser(context).chooseName(attr=attr) store.createFile(file_id, upload) return True class CustomApplicantDisplayFormPage(NigeriaApplicantDisplayFormPage): """A display view for applicant data. """ @property def file_links(self): html = '' pdf = getUtility(IExtFileStore).getFileByContext( self.context, attr='stateresult.pdf') if pdf: html += 'Statement of Result' return html @property def form_fields(self): if self.target is not None and self.target == 'trans': form_fields = grok.AutoFields(ITranscriptApplicant).omit( 'locked', 'suspended') form_fields['dispatch_address'].custom_widget = BytesDisplayWidget form_fields['perm_address'].custom_widget = BytesDisplayWidget return form_fields if self.target is not None and self.target == 'cert': form_fields = grok.AutoFields(ICertificateRequest).omit( 'locked', 'suspended') #form_fields['dispatch_address'].custom_widget = BytesDisplayWidget #form_fields['perm_address'].custom_widget = BytesDisplayWidget return form_fields # AAUE is using the same interface for all regular applications. form_fields = grok.AutoFields(ICustomUGApplicant) if self.target is not None and self.target.startswith('pg'): for field in PG_OMIT_DISPLAY_FIELDS: form_fields = form_fields.omit(field) elif self.target is not None and self.target in ('ptee',): for field in PTEE_OMIT_DISPLAY_FIELDS: form_fields = form_fields.omit(field) elif self.target is not None and self.target in ('bridge', 'ude',): for field in UDE_OMIT_DISPLAY_FIELDS: form_fields = form_fields.omit(field) else: for field in UG_OMIT_DISPLAY_FIELDS: form_fields = form_fields.omit(field) form_fields['perm_address'].custom_widget = BytesDisplayWidget form_fields['notice'].custom_widget = BytesDisplayWidget if not getattr(self.context, 'student_id'): form_fields = form_fields.omit('student_id') if not getattr(self.context, 'screening_score'): form_fields = form_fields.omit('screening_score') if not getattr(self.context, 'screening_venue') or \ self.context.state not in ('submitted', 'admitted', 'created'): form_fields = form_fields.omit('screening_venue') if not getattr(self.context, 'screening_date') or \ self.context.state not in ('submitted', 'admitted', 'created'): form_fields = form_fields.omit('screening_date') return form_fields def getCourseAdmitted(self): """Return link, title and code in html format to the certificate admitted. """ if self.layout.isApplicant(): return '' course_admitted = self.context.course_admitted if getattr(course_admitted, '__parent__',None): url = self.url(course_admitted) title = course_admitted.title code = course_admitted.code return '%s - %s' %(url,code,title) return '' def update(self): super(CustomApplicantDisplayFormPage, self).update() self.extraform_url = self.url(self.context, 'stateresult.pdf') return class CustomPDFActionButton(PDFActionButton): @property def target_url(self): if self.context.state in ('initialized', 'started', 'paid') \ or self.context.special or self.view.target in ('trans', 'cert'): return return self.view.url(self.view.context, self.target) class CustomPDFApplicationSlip(NigeriaPDFApplicationSlip): column_two_fields = ('applicant_id', 'reg_number', 'firstname', 'middlename', 'lastname', 'sex', 'date_of_birth') #two_columns_design_fields = [ # 'fst_sit_fname', 'fst_sit_no', 'fst_sit_date', # 'fst_sit_type', 'fst_sit_results', # 'scd_sit_fname', 'scd_sit_no', 'scd_sit_date', # 'scd_sit_type', 'scd_sit_results'] def _getCourseAdmittedLink(self, view): return None def _getDeptAndFaculty(self): return [None, None] @property def note(self): note = getattr(self.context.__parent__, 'application_slip_notice', None) if note: return '

' + note if self.context.sex == 'm': pronoun = 'he' else: pronoun = 'she' return ''' The applicant has acknowledged that, if discovered at any time that %s does not possess any of the qualifications which %s claims %s has obtained, %s will be expelled from the University not be re-admitted for the same or any other programme, even if %s has upgraded previous qualifications or possess additional qualifications. ''' % ( pronoun, pronoun, pronoun, pronoun, pronoun) @property def form_fields(self): # AAUE is using the same interface for all regular applications. form_fields = grok.AutoFields(ICustomUGApplicant) if self.target is not None and self.target.startswith('pg'): for field in PG_OMIT_PDF_FIELDS: form_fields = form_fields.omit(field) elif self.target is not None and self.target in ('ptee',): for field in PTEE_OMIT_PDF_FIELDS: form_fields = form_fields.omit(field) elif self.target is not None and self.target in ('bridge', 'ude',): for field in UDE_OMIT_PDF_FIELDS: form_fields = form_fields.omit(field) else: for field in UG_OMIT_PDF_FIELDS: form_fields = form_fields.omit(field) if not getattr(self.context, 'student_id'): form_fields = form_fields.omit('student_id') if not getattr(self.context, 'screening_score'): form_fields = form_fields.omit('screening_score') if not getattr(self.context, 'screening_venue'): form_fields = form_fields.omit('screening_venue') if not getattr(self.context, 'screening_date'): form_fields = form_fields.omit('screening_date') return form_fields class CustomApplicantManageFormPage(NigeriaApplicantManageFormPage): """A full edit view for applicant data. """ @property def form_fields(self): if self.target is not None and self.target == 'trans': form_fields = grok.AutoFields(ITranscriptApplicant) form_fields['applicant_id'].for_display = True return form_fields if self.target is not None and self.target == 'cert': form_fields = grok.AutoFields(ICertificateRequest) form_fields['applicant_id'].for_display = True return form_fields # AAUE is using the same interface for all regular applications. form_fields = grok.AutoFields(ICustomUGApplicant) if self.target is not None and self.target.startswith('pg'): for field in PG_OMIT_MANAGE_FIELDS: form_fields = form_fields.omit(field) elif self.target is not None and self.target in ('ptee',): for field in PTEE_OMIT_MANAGE_FIELDS: form_fields = form_fields.omit(field) elif self.target is not None and self.target in ('bridge', 'ude',): for field in UDE_OMIT_MANAGE_FIELDS: form_fields = form_fields.omit(field) else: for field in UG_OMIT_MANAGE_FIELDS: form_fields = form_fields.omit(field) form_fields['student_id'].for_display = True form_fields['applicant_id'].for_display = True return form_fields def update(self): super(CustomApplicantManageFormPage, self).update() upload_stateresult = self.request.form.get('form.stateresult', None) if upload_stateresult: # We got a fresh stateresult upload success = handle_file_upload( upload_stateresult, self.context, self, attr='stateresult.pdf') if success: self.context.writeLogMessage(self, 'saved: stateresult') else: self.upload_success = False self.max_file_upload_size = string_from_bytes(MAX_FILE_UPLOAD_SIZE) return class CustomApplicantEditFormPage(NigeriaApplicantEditFormPage): """An applicant-centered edit view for applicant data. """ def unremovable(self, ticket): return True def dataNotComplete(self): store = getUtility(IExtFileStore) # Temporarily enable passport upload also for cert and trans # applications. if self.context.__parent__.with_picture: if not store.getFileByContext(self.context, attr=u'passport.jpg'): return _('No passport picture uploaded.') if not self.target[:4] in ('cert', 'tran') and \ not self.request.form.get('confirm_passport', False): return _('Passport picture confirmation box not ticked.') if self.target in ('trans', 'cert') and \ not store.getFileByContext(self.context, attr=u'stateresult.pdf'): return _('No statement of result pdf file uploaded.') return False # AAUE applicants never see the 'Remove Selected Tickets' button. @property def display_actions(self): # If the form is unlocked, applicants are allowed to save the form # and remove unused tickets. actions = [[_('Save')], []] # Only in state started they can also add tickets. if self.context.state == STARTED: actions = [[_('Save')], [_('Add online payment ticket')]] # In state paid, they can submit the data and further add tickets # if the application is special. elif self.context.special and self.context.state == PAID: actions = [[_('Save'), _('Finally Submit')], [_('Add online payment ticket')]] elif self.context.state == PAID: actions = [[_('Save'), _('Finally Submit')], []] return actions @property def form_fields(self): if self.target is not None and self.target == 'trans': form_fields = grok.AutoFields(ITranscriptApplicant).omit( 'locked', 'suspended') form_fields['applicant_id'].for_display = True form_fields['reg_number'].for_display = True form_fields['place_of_birth'].field.required = True form_fields['date_of_birth'].field.required = True form_fields['nationality'].field.required = True form_fields['email'].field.required = True form_fields['phone'].field.required = True form_fields['perm_address'].field.required = True form_fields['dispatch_address'].field.required = True form_fields['entry_mode'].field.required = True form_fields['entry_session'].field.required = True form_fields['end_session'].field.required = True form_fields['course_studied'].field.required = True return form_fields if self.target is not None and self.target == 'cert': form_fields = grok.AutoFields(ICertificateRequest).omit( 'locked', 'suspended') form_fields['applicant_id'].for_display = True form_fields['reg_number'].for_display = True form_fields['place_of_birth'].field.required = True form_fields['date_of_birth'].field.required = True form_fields['nationality'].field.required = True form_fields['email'].field.required = True form_fields['phone'].field.required = True form_fields['entry_session'].field.required = True form_fields['end_session'].field.required = True form_fields['course_studied'].field.required = True form_fields['certificate_type'].field.required = True # Additional omissions if self.context.__parent__.code == 'cert5': for field in ('firstname', 'middlename', 'lastname'): form_fields[field].for_display = True return form_fields # AAUE is using the same interface for all regular applications. form_fields = grok.AutoFields(ICustomUGApplicantEdit) if self.target is not None and self.target.startswith('pg'): for field in PG_OMIT_EDIT_FIELDS: form_fields = form_fields.omit(field) elif self.target is not None and self.target in ('ptee',): for field in PTEE_OMIT_EDIT_FIELDS: form_fields = form_fields.omit(field) elif self.target is not None and self.target in ('bridge', 'ude',): for field in UDE_OMIT_EDIT_FIELDS: form_fields = form_fields.omit(field) else: for field in UG_OMIT_EDIT_FIELDS: form_fields = form_fields.omit(field) # Additional omissions if self.target is not None and self.target in ('ude', 'utme'): for field in UPDATE_OMIT_FIELDS: form_fields[field].for_display = True form_fields['applicant_id'].for_display = True form_fields['reg_number'].for_display = True return form_fields def update(self): if self.context.locked or ( self.context.__parent__.expired and self.context.__parent__.strict_deadline): self.emit_lock_message() return if getattr( self.context.course1, 'code', 'nocourse') == self.request.form.get( 'form.course2', None): self.flash(_('2nd choice course must differ from 1st choice course.'), type='danger') self.redirect(self.url(self.context)) return if getattr( self.context.course1, 'code', 'nocourse') == self.request.form.get( 'form.course3', None): self.flash(_('3rd choice course must differ from 1st choice course.'), type='danger') self.redirect(self.url(self.context)) return super(CustomApplicantEditFormPage, self).update() upload_stateresult = self.request.form.get('form.stateresult', None) if upload_stateresult: # We got a fresh stateresult upload success = handle_file_upload( upload_stateresult, self.context, self, attr='stateresult.pdf') if not success: self.upload_success = False self.max_file_upload_size = string_from_bytes(MAX_FILE_UPLOAD_SIZE) return class CustomApplicantRegistrationPage(NigeriaApplicantRegistrationPage): """Captcha'd registration page for applicants. """ @property def form_fields(self): form_fields = None if self.context.mode == 'update': form_fields = grok.AutoFields(IApplicantRegisterUpdate).select( 'lastname','reg_number','email') target = getattr(self.context, 'prefix', None) if target in ('trans', 'cert'): form_fields.get('reg_number').field.title = u'Matriculation Number' else: #if self.context.mode == 'create': form_fields = grok.AutoFields(ICustomUGApplicantEdit).select( 'firstname', 'middlename', 'lastname', 'email', 'phone') return form_fields def _redirect(self, email, password, applicant_id): # Forward email and credentials to landing page. self.redirect(self.url(self.context, 'registration_complete', data = dict(email=email, password=password, applicant_id=applicant_id))) return @property def _postfix(self): """Alumni records have to be imported into several containers. Therefore a string must be added to their registration number to make it unique. """ if self.context.prefix in ('trans', 'cert'): return self.context.code return '' class CustomExportPDFPaymentSlipPage(NigeriaExportPDFPaymentSlipPage): @property def payment_slip_download_warning(self): return '' class CustomApplicantCheckStatusPage(ApplicantCheckStatusPage): """Captcha'd status checking page for applicants. """ grok.template('applicantcheckstatus') class ScreeningInvitationActionButton(ManageActionButton): grok.order(8) # This button should always be the last one. grok.context(ICustomApplicant) grok.view(CustomApplicantDisplayFormPage) grok.require('waeup.viewApplication') icon = 'actionicon_pdf.png' text = _('Download screening invitation letter') target = 'screening_invitation.pdf' @property def target_url(self): if not self.context.screening_date or not self.context.state in ( 'submitted', 'admitted', 'created'): return '' return self.view.url(self.view.context, self.target) class ExportScreeningInvitationLetter(UtilityView, grok.View): """Deliver a slip with only screening data. This form page is available only in AAUE. """ grok.context(ICustomApplicant) grok.name('screening_invitation.pdf') grok.require('waeup.viewApplication') prefix = 'form' label = u'Screening Invitation Letter' form_fields = [] @property def note(self): if self.context.screening_date: year = self.context.__parent__.year session = '%s/%s' % (year, year + 1) sdate = self.context.screening_date stime = '' if '@' in self.context.screening_date: sdate = self.context.screening_date.split('@')[0].strip() stime = self.context.screening_date.split('@')[1].strip() return """



Dear %s,

You are invited to the Ambrose Alli University %s Admissions Screening Exercise.

Date: %s

Time: %s

Venue: %s


Please bring this letter of invitation and the downloaded application form along with you on your screening date.

You are expected to be available 30 minutes before the commencement of your Screening.
""" % ( self.context.display_fullname, session, sdate, stime, self.context.screening_venue) return @property def title(self): return None def update(self): if not self.context.screening_date or not self.context.state in ( 'submitted', 'admitted', 'created'): self.flash(_('Forbidden'), type="warning") self.redirect(self.url(self.context)) def render(self): applicantview = ApplicantBaseDisplayFormPage(self.context, self.request) students_utils = getUtility(IStudentsUtils) return students_utils.renderPDF(self,'screening_data.pdf', self.context, applicantview, note=self.note) class StateResult(grok.View): """Renders the pdf form extension for applicants. """ grok.name('stateresult.pdf') grok.context(ICustomApplicant) grok.require('waeup.viewApplication') def render(self): pdf = getUtility(IExtFileStore).getFileByContext( self.context, attr='stateresult.pdf') self.response.setHeader('Content-Type', 'application/pdf') return pdf