source: main/waeup.aaue/trunk/src/waeup/aaue/applicants/browser.py @ 15100

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

Fix bug.

  • Property svn:keywords set to Id
File size: 21.4 KB
Line 
1## $Id: browser.py 15100 2018-08-06 10:59:28Z henrik $
2##
3## Copyright (C) 2011 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##
18"""UI components for basic applicants and related components.
19"""
20import grok
21import os
22from zope.component import getUtility, getAdapter
23from zope.i18n import translate
24from hurry.workflow.interfaces import IWorkflowState
25from waeup.kofa.interfaces import (
26    IExtFileStore, IFileStoreNameChooser, IKofaUtils)
27from zope.formlib.textwidgets import BytesDisplayWidget
28from waeup.kofa.utils.helpers import string_from_bytes, file_size
29from waeup.kofa.applicants.browser import (
30    ApplicantCheckStatusPage, ApplicantBaseDisplayFormPage)
31from waeup.kofa.applicants.workflow import STARTED, PAID
32from waeup.kofa.applicants.viewlets import PDFActionButton
33from waeup.kofa.applicants.interfaces import IApplicantRegisterUpdate
34from waeup.kofa.browser.layout import UtilityView
35from waeup.kofa.students.interfaces import IStudentsUtils
36from waeup.kofa.interfaces import IPDF
37from waeup.kofa.browser.viewlets import ManageActionButton
38from waeup.aaue.interfaces import MessageFactory as _
39from kofacustom.nigeria.applicants.browser import (
40    NigeriaApplicantDisplayFormPage,
41    NigeriaApplicantManageFormPage,
42    NigeriaApplicantEditFormPage,
43    NigeriaPDFApplicationSlip,
44    NigeriaApplicantRegistrationPage,
45    NigeriaExportPDFPaymentSlipPage,
46    )
47from kofacustom.nigeria.applicants.interfaces import OMIT_DISPLAY_FIELDS
48from waeup.aaue.applicants.interfaces import (
49    ICustomUGApplicant,
50    ICustomUGApplicantEdit,
51    ITranscriptApplicant,
52    ICertificateRequest,
53    ICustomApplicant
54    )
55
56UG_OMIT_FIELDS = (
57      'hq_type', 'hq_fname', 'hq_matric_no',
58      'hq_degree', 'hq_school', 'hq_session', 'hq_disc',
59      'hq_type2', 'hq_fname2', 'hq_matric_no2',
60      'hq_degree2', 'hq_school2', 'hq_session2', 'hq_disc2',
61      'hq_type3', 'hq_fname3', 'hq_matric_no3',
62      'hq_degree3', 'hq_school3', 'hq_session3', 'hq_disc3',
63      'nysc_year',
64      'nysc_location',
65      'nysc_lga',
66      'employer',
67      'emp_position',
68      'emp_start',
69      'emp_end',
70      'emp_reason',
71      'employer2',
72      'emp2_position',
73      'emp2_start',
74      'emp2_end',
75      'emp2_reason',
76      'former_matric',
77      )
78UG_OMIT_DISPLAY_FIELDS = OMIT_DISPLAY_FIELDS + (
79    'jamb_subjects_list', 'master_sheet_number') + UG_OMIT_FIELDS
80UG_OMIT_PDF_FIELDS = UG_OMIT_DISPLAY_FIELDS + UG_OMIT_FIELDS + (
81      'alr_fname', 'alr_no', 'alr_date',
82      'alr_results', 'notice')
83UG_OMIT_MANAGE_FIELDS = (
84    'special_application','jamb_subjects_list',) + UG_OMIT_FIELDS
85UG_OMIT_EDIT_FIELDS = UG_OMIT_MANAGE_FIELDS + OMIT_DISPLAY_FIELDS + (
86    'student_id',
87    'notice',
88    'jamb_age',
89    'jamb_subjects',
90    'jamb_score',
91    'jamb_reg_number',
92    'aggregate',
93    'master_sheet_number',
94    'screening_venue',
95    'screening_score',
96    'screening_date'
97    )
98
99UDE_OMIT_FIELDS = (
100      'nysc_year',
101      'nysc_location',
102      'nysc_lga',
103      'employer',
104      'emp_position',
105      'emp_start',
106      'emp_end',
107      'emp_reason',
108      'employer2',
109      'emp2_position',
110      'emp2_start',
111      'emp2_end',
112      'emp2_reason',
113      'former_matric',
114      )
115UDE_OMIT_DISPLAY_FIELDS = OMIT_DISPLAY_FIELDS + (
116    'jamb_subjects_list', 'master_sheet_number') + UDE_OMIT_FIELDS
117UDE_OMIT_PDF_FIELDS = UDE_OMIT_DISPLAY_FIELDS + UDE_OMIT_FIELDS + (
118      #'alr_fname', 'alr_no', 'alr_date', 'alr_results',
119      'hq_type2', 'hq_fname2', 'hq_matric_no2',
120      'hq_degree2', 'hq_school2', 'hq_session2', 'hq_disc2',
121      'hq_type3', 'hq_fname3', 'hq_matric_no3',
122      'hq_degree3', 'hq_school3', 'hq_session3', 'hq_disc3',
123      'notice')
124UDE_OMIT_MANAGE_FIELDS = (
125    'special_application','jamb_subjects_list',) + UDE_OMIT_FIELDS
126UDE_OMIT_EDIT_FIELDS = UDE_OMIT_MANAGE_FIELDS + OMIT_DISPLAY_FIELDS + (
127    'student_id',
128    'notice',
129    'jamb_age',
130    'jamb_subjects',
131    'jamb_score',
132    'jamb_reg_number',
133    'aggregate',
134    'master_sheet_number',
135    'screening_venue',
136    'screening_score',
137    'screening_date'
138    )
139
140#UG_OMIT_PDF_FIELDS = tuple([
141#    element for element in UG_OMIT_PDF_FIELDS if not element == 'phone'])
142
143#UG_OMIT_PDF_FIELDS += (
144#      'reg_number','alr_fname', 'alr_no', 'alr_date',
145#      'alr_results', 'notice'
146#      )
147
148PG_OMIT_FIELDS = (
149    'fst_sit_fname',
150    'fst_sit_no',
151    'fst_sit_date',
152    'fst_sit_type',
153    'fst_sit_results',
154    'scd_sit_fname',
155    'scd_sit_no',
156    'scd_sit_date',
157    'scd_sit_type',
158    'scd_sit_results',
159    #'programme_type',
160    'jamb_age',
161    'jamb_subjects',
162    'jamb_score',
163    'jamb_reg_number',
164    'aggregate'
165    )
166PG_OMIT_DISPLAY_FIELDS = OMIT_DISPLAY_FIELDS + (
167    'jamb_subjects_list',) + PG_OMIT_FIELDS
168PG_OMIT_PDF_FIELDS = PG_OMIT_DISPLAY_FIELDS + PG_OMIT_FIELDS + (
169      'reg_number','alr_fname', 'alr_no', 'alr_date',
170      'alr_results', 'notice',
171      'nysc_year',
172      'nysc_location',
173      'nysc_lga',
174      'former_matric',
175      )
176PG_OMIT_MANAGE_FIELDS = (
177    'special_application','jamb_subjects_list',) + PG_OMIT_FIELDS
178PG_OMIT_EDIT_FIELDS = PG_OMIT_MANAGE_FIELDS + OMIT_DISPLAY_FIELDS + (
179    'student_id',
180    'notice',
181    )
182
183PTEE_OMIT_FIELDS = (
184    'jamb_age',
185    'jamb_subjects',
186    'jamb_score',
187    'jamb_reg_number',
188    'aggregate'
189    )
190PTEE_OMIT_DISPLAY_FIELDS = OMIT_DISPLAY_FIELDS + (
191    'jamb_subjects_list',) + PTEE_OMIT_FIELDS
192PTEE_OMIT_PDF_FIELDS = PTEE_OMIT_DISPLAY_FIELDS + PTEE_OMIT_FIELDS + (
193      'reg_number','alr_fname', 'alr_no', 'alr_date',
194      'alr_results', 'notice',
195      'nysc_year',
196      'nysc_location',
197      'nysc_lga',
198      'employer',
199      'emp_position',
200      'emp_start',
201      'emp_end',
202      'emp_reason',
203      'employer2',
204      'emp2_position',
205      'emp2_start',
206      'emp2_end',
207      'emp2_reason',
208      'former_matric',
209    )
210PTEE_OMIT_MANAGE_FIELDS = (
211    'special_application','jamb_subjects_list',) + PTEE_OMIT_FIELDS
212PTEE_OMIT_EDIT_FIELDS = PTEE_OMIT_MANAGE_FIELDS + OMIT_DISPLAY_FIELDS + (
213    'student_id',
214    'notice',
215    )
216
217UPDATE_OMIT_FIELDS = (
218    'firstname',
219    'middlename',
220    'lastname',
221    'sex',
222    'lga',
223    'course1',
224    )
225
226class CustomApplicantDisplayFormPage(NigeriaApplicantDisplayFormPage):
227    """A display view for applicant data.
228    """
229
230    @property
231    def form_fields(self):
232        if self.target is not None and self.target == 'trans':
233            form_fields = grok.AutoFields(ITranscriptApplicant).omit(
234                'locked', 'suspended')
235            form_fields['dispatch_address'].custom_widget = BytesDisplayWidget
236            form_fields['perm_address'].custom_widget = BytesDisplayWidget
237            return form_fields
238        if self.target is not None and self.target == 'cert':
239            form_fields = grok.AutoFields(ICertificateRequest).omit(
240                'locked', 'suspended')
241            #form_fields['dispatch_address'].custom_widget = BytesDisplayWidget
242            #form_fields['perm_address'].custom_widget = BytesDisplayWidget
243            return form_fields
244        # AAUE is using the same interface for all regular applications.
245        form_fields = grok.AutoFields(ICustomUGApplicant)
246        if self.target is not None and self.target.startswith('pg'):
247            for field in PG_OMIT_DISPLAY_FIELDS:
248                form_fields = form_fields.omit(field)
249        elif self.target is not None and self.target in ('ptee',):
250            for field in PTEE_OMIT_DISPLAY_FIELDS:
251                form_fields = form_fields.omit(field)
252        elif self.target is not None and self.target in ('ude',):
253            for field in UDE_OMIT_DISPLAY_FIELDS:
254                form_fields = form_fields.omit(field)
255        else:
256            for field in UG_OMIT_DISPLAY_FIELDS:
257                form_fields = form_fields.omit(field)
258        form_fields['perm_address'].custom_widget = BytesDisplayWidget
259        form_fields['notice'].custom_widget = BytesDisplayWidget
260        if not getattr(self.context, 'student_id'):
261            form_fields = form_fields.omit('student_id')
262        if not getattr(self.context, 'screening_score'):
263            form_fields = form_fields.omit('screening_score')
264        if not getattr(self.context, 'screening_venue') or \
265            self.context.state not in ('submitted', 'admitted', 'created'):
266            form_fields = form_fields.omit('screening_venue')
267        if not getattr(self.context, 'screening_date') or \
268            self.context.state not in ('submitted', 'admitted', 'created'):
269            form_fields = form_fields.omit('screening_date')
270        return form_fields
271
272    def getCourseAdmitted(self):
273        """Return link, title and code in html format to the certificate
274           admitted.
275        """
276        if self.layout.isApplicant():
277            return ''
278        course_admitted = self.context.course_admitted
279        if getattr(course_admitted, '__parent__',None):
280            url = self.url(course_admitted)
281            title = course_admitted.title
282            code = course_admitted.code
283            return '<a href="%s">%s - %s</a>' %(url,code,title)
284        return ''
285
286class CustomPDFActionButton(PDFActionButton):
287
288    @property
289    def target_url(self):
290        if self.context.state in ('initialized', 'started', 'paid') \
291            or self.context.special or self.view.target in ('trans', 'cert'):
292            return
293        return self.view.url(self.view.context, self.target)
294
295
296class CustomPDFApplicationSlip(NigeriaPDFApplicationSlip):
297
298    column_two_fields = ('applicant_id', 'reg_number',
299        'firstname', 'middlename', 'lastname', 'sex', 'date_of_birth')
300    #two_columns_design_fields = [
301    #    'fst_sit_fname', 'fst_sit_no', 'fst_sit_date',
302    #    'fst_sit_type', 'fst_sit_results',
303    #    'scd_sit_fname', 'scd_sit_no', 'scd_sit_date',
304    #    'scd_sit_type', 'scd_sit_results']
305
306    def _getCourseAdmittedLink(self, view):
307        return None
308
309    def _getDeptAndFaculty(self):
310        return [None, None]
311
312    @property
313    def note(self):
314        note = getattr(self.context.__parent__, 'application_slip_notice', None)
315        if note:
316            return '<br /><br />' + note
317        if self.context.sex == 'm':
318            pronoun = 'he'
319        else:
320            pronoun = 'she'
321        return '''
322The applicant has acknowledged that, if discovered at any time that %s does not possess
323any of the qualifications which %s claims %s has obtained, %s will be expelled from the
324University not be re-admitted for the same or any other programme, even if %s has
325upgraded previous qualifications or possess additional qualifications.
326
327''' % (
328    pronoun, pronoun, pronoun, pronoun, pronoun)
329
330    @property
331    def form_fields(self):
332        # AAUE is using the same interface for all regular applications.
333        form_fields = grok.AutoFields(ICustomUGApplicant)
334        if self.target is not None and self.target.startswith('pg'):
335            for field in PG_OMIT_PDF_FIELDS:
336                form_fields = form_fields.omit(field)
337        elif self.target is not None and self.target in ('ptee',):
338            for field in PTEE_OMIT_PDF_FIELDS:
339                form_fields = form_fields.omit(field)
340        elif self.target is not None and self.target in ('ude',):
341            for field in UDE_OMIT_PDF_FIELDS:
342                form_fields = form_fields.omit(field)
343        else:
344            for field in UG_OMIT_PDF_FIELDS:
345                form_fields = form_fields.omit(field)
346        if not getattr(self.context, 'student_id'):
347            form_fields = form_fields.omit('student_id')
348        if not getattr(self.context, 'screening_score'):
349            form_fields = form_fields.omit('screening_score')
350        if not getattr(self.context, 'screening_venue'):
351            form_fields = form_fields.omit('screening_venue')
352        if not getattr(self.context, 'screening_date'):
353            form_fields = form_fields.omit('screening_date')
354        return form_fields
355
356class CustomApplicantManageFormPage(NigeriaApplicantManageFormPage):
357    """A full edit view for applicant data.
358    """
359
360    @property
361    def form_fields(self):
362        if self.target is not None and self.target == 'trans':
363            form_fields = grok.AutoFields(ITranscriptApplicant)
364            form_fields['applicant_id'].for_display = True
365            return form_fields
366        if self.target is not None and self.target == 'cert':
367            form_fields = grok.AutoFields(ICertificateRequest)
368            form_fields['applicant_id'].for_display = True
369            return form_fields
370        # AAUE is using the same interface for all regular applications.
371        form_fields = grok.AutoFields(ICustomUGApplicant)
372        if self.target is not None and self.target.startswith('pg'):
373            for field in PG_OMIT_MANAGE_FIELDS:
374                form_fields = form_fields.omit(field)
375        elif self.target is not None and self.target in ('ptee',):
376            for field in PTEE_OMIT_MANAGE_FIELDS:
377                form_fields = form_fields.omit(field)
378        elif self.target is not None and self.target in ('ude',):
379            for field in UDE_OMIT_MANAGE_FIELDS:
380                form_fields = form_fields.omit(field)
381        else:
382            for field in UG_OMIT_MANAGE_FIELDS:
383                form_fields = form_fields.omit(field)
384        form_fields['student_id'].for_display = True
385        form_fields['applicant_id'].for_display = True
386        return form_fields
387
388class CustomApplicantEditFormPage(NigeriaApplicantEditFormPage):
389    """An applicant-centered edit view for applicant data.
390    """
391
392    def unremovable(self, ticket):
393        return True
394
395    def dataNotComplete(self):
396        store = getUtility(IExtFileStore)
397        if not self.target[:4] in ('cert', 'tran'):
398            if not store.getFileByContext(self.context, attr=u'passport.jpg'):
399                return _('No passport picture uploaded.')
400            if not self.request.form.get('confirm_passport', False):
401                return _('Passport picture confirmation box not ticked.')
402        return False
403
404    # AAUE applicants never see the 'Remove Selected Tickets' button.
405    @property
406    def display_actions(self):
407        # If the form is unlocked, applicants are allowed to save the form
408        # and remove unused tickets.
409        actions = [[_('Save')], []]
410        # Only in state started they can also add tickets.
411        if self.context.state == STARTED:
412            actions = [[_('Save')],
413                [_('Add online payment ticket')]]
414        # In state paid, they can submit the data and further add tickets
415        # if the application is special.
416        elif self.context.special and self.context.state == PAID:
417            actions = [[_('Save'), _('Finally Submit')],
418                [_('Add online payment ticket')]]
419        elif self.context.state == PAID:
420            actions = [[_('Save'), _('Finally Submit')], []]
421        return actions
422
423    @property
424    def form_fields(self):
425        if self.target is not None and self.target == 'trans':
426            form_fields = grok.AutoFields(ITranscriptApplicant).omit(
427                'locked', 'suspended')
428            form_fields['applicant_id'].for_display = True
429            form_fields['reg_number'].for_display = True
430            return form_fields
431        if self.target is not None and self.target == 'cert':
432            form_fields = grok.AutoFields(ICertificateRequest).omit(
433                'locked', 'suspended')
434            form_fields['applicant_id'].for_display = True
435            form_fields['reg_number'].for_display = True
436            # Additional omissions
437            if self.context.__parent__.code == 'cert5':
438                for field in ('firstname', 'middlename', 'lastname'):
439                    form_fields[field].for_display = True
440            return form_fields
441        # AAUE is using the same interface for all regular applications.
442        form_fields = grok.AutoFields(ICustomUGApplicantEdit)
443        if self.target is not None and self.target.startswith('pg'):
444            for field in PG_OMIT_EDIT_FIELDS:
445                form_fields = form_fields.omit(field)
446        elif self.target is not None and self.target in ('ptee',):
447            for field in PTEE_OMIT_EDIT_FIELDS:
448                form_fields = form_fields.omit(field)
449        elif self.target is not None and self.target in ('ude',):
450            for field in UDE_OMIT_EDIT_FIELDS:
451                form_fields = form_fields.omit(field)
452        else:
453            for field in UG_OMIT_EDIT_FIELDS:
454                form_fields = form_fields.omit(field)
455        # Additional omissions
456        if self.target is not None and self.target in ('ude', 'utme'):
457            for field in UPDATE_OMIT_FIELDS:
458                form_fields[field].for_display = True
459        form_fields['applicant_id'].for_display = True
460        form_fields['reg_number'].for_display = True
461        return form_fields
462
463class CustomApplicantRegistrationPage(NigeriaApplicantRegistrationPage):
464    """Captcha'd registration page for applicants.
465    """
466
467    @property
468    def form_fields(self):
469        form_fields = None
470        if self.context.mode == 'update':
471            form_fields = grok.AutoFields(IApplicantRegisterUpdate).select(
472                'lastname','reg_number','email')
473            target = getattr(self.context, 'prefix', None)
474            if target in ('trans', 'cert'):
475                form_fields.get('reg_number').field.title = u'Matriculation Number'
476        else: #if self.context.mode == 'create':
477            form_fields = grok.AutoFields(ICustomUGApplicantEdit).select(
478                'firstname', 'middlename', 'lastname', 'email', 'phone')
479        return form_fields
480
481    def _redirect(self, email, password, applicant_id):
482        # Forward email and credentials to landing page.
483        self.redirect(self.url(self.context, 'registration_complete',
484            data = dict(email=email, password=password,
485            applicant_id=applicant_id)))
486        return
487
488    @property
489    def _postfix(self):
490        """Alumni records have to be imported into several containers.
491        Therefore a string must be added to their registration number
492        to make it unique.
493        """
494        if self.context.prefix in ('trans', 'cert'):
495            return self.context.code
496        return ''
497
498class CustomExportPDFPaymentSlipPage(NigeriaExportPDFPaymentSlipPage):
499
500    @property
501    def payment_slip_download_warning(self):
502        return ''
503
504class CustomApplicantCheckStatusPage(ApplicantCheckStatusPage):
505    """Captcha'd status checking page for applicants.
506    """
507    grok.template('applicantcheckstatus')
508
509class ScreeningInvitationActionButton(ManageActionButton):
510    grok.order(8) # This button should always be the last one.
511    grok.context(ICustomApplicant)
512    grok.view(CustomApplicantDisplayFormPage)
513    grok.require('waeup.viewApplication')
514    icon = 'actionicon_pdf.png'
515    text = _('Download screening invitation letter')
516    target = 'screening_invitation.pdf'
517
518    @property
519    def target_url(self):
520        if not self.context.screening_date or not self.context.state in (
521            'submitted', 'admitted', 'created'):
522            return ''
523        return self.view.url(self.view.context, self.target)
524
525class ExportScreeningInvitationLetter(UtilityView, grok.View):
526    """Deliver a slip with only screening data.
527    This form page is available only in AAUE.
528    """
529    grok.context(ICustomApplicant)
530    grok.name('screening_invitation.pdf')
531    grok.require('waeup.viewApplication')
532    prefix = 'form'
533
534    label = u'Screening Invitation Letter'
535
536    form_fields = []
537
538    @property
539    def note(self):
540        if self.context.screening_date:
541            year = self.context.__parent__.year
542            session = '%s/%s' % (year, year + 1)
543            sdate = self.context.screening_date
544            stime = ''
545            if '@' in self.context.screening_date:
546                sdate = self.context.screening_date.split('@')[0].strip()
547                stime = self.context.screening_date.split('@')[1].strip()
548            return """
549<br /><br /><br /><br /><font size='12'>
550Dear %s,
551<br /><br />
552You are invited to the Ambrose Alli University %s Admissions Screening Exercise.
553<br /><br />
554<strong>Date: %s
555<br /><br />
556Time: %s
557<br /><br />
558Venue: %s
559</strong>
560<br /><br />
561Please bring this letter of invitation and the downloaded application form along with you on your screening date.
562<br /><br />
563You are expected to be available 30 minutes before the commencement of your Screening.
564</font>
565
566""" % (
567       self.context.display_fullname,
568       session,
569       sdate,
570       stime,
571       self.context.screening_venue)
572        return
573
574    @property
575    def title(self):
576        return None
577
578    def update(self):
579        if not self.context.screening_date or not self.context.state in (
580            'submitted', 'admitted', 'created'):
581            self.flash(_('Forbidden'), type="warning")
582            self.redirect(self.url(self.context))
583
584    def render(self):
585        applicantview = ApplicantBaseDisplayFormPage(self.context, self.request)
586        students_utils = getUtility(IStudentsUtils)
587        return students_utils.renderPDF(self,'screening_data.pdf',
588            self.context, applicantview, note=self.note)
Note: See TracBrowser for help on using the repository browser.