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

Last change on this file since 14920 was 14840, checked in by Henrik Bettermann, 7 years ago

Show screening date and venue only if student has submitted the application.

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