source: main/kofacustom.iuokada/trunk/src/kofacustom/iuokada/applicants/browser.py @ 17385

Last change on this file since 17385 was 17159, checked in by Henrik Bettermann, 2 years ago

Extend max_applicants.

  • Property svn:keywords set to Id
File size: 17.5 KB
RevLine 
[10765]1## $Id: browser.py 17159 2022-11-09 12:23:24Z 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
[15878]21import os
[16075]22from zope.component import getUtility, queryUtility
23from zope.catalog.interfaces import ICatalog
[15807]24from zope.formlib.textwidgets import BytesDisplayWidget
[15878]25from waeup.kofa.interfaces import (
26    IExtFileStore, IFileStoreNameChooser)
27from waeup.kofa.utils.helpers import string_from_bytes, file_size, now
[10765]28from waeup.kofa.applicants.browser import (
[16062]29    ApplicantRegistrationPage, ApplicantsContainerPage, AdditionalFile,
30    RefereeReportAddFormPage, ExportPDFReportSlipPage, ExportPDFReportSlipPage2,
[16328]31    RefereeReportDisplayFormPage,
[16559]32    ApplicantsContainerManageFormPage,
[17024]33    ApplicantAddFormPage,
34    ApplicantsRootPage)
[15809]35from waeup.kofa.applicants.interfaces import (
[16070]36    ISpecialApplicant, IApplicantsContainer, AppCatCertificateSource)
[10765]37from kofacustom.nigeria.applicants.browser import (
38    NigeriaApplicantDisplayFormPage,
[15807]39    NigeriaApplicantManageFormPage,
40    NigeriaApplicantEditFormPage,
[10765]41    NigeriaPDFApplicationSlip)
[15809]42from waeup.kofa.widgets.datewidget import (
43    FriendlyDateDisplayWidget,
44    FriendlyDatetimeDisplayWidget)
[15807]45from kofacustom.nigeria.applicants.interfaces import (
[15884]46    OMIT_DISPLAY_FIELDS,
47    #UG_OMIT_DISPLAY_FIELDS,
48    #UG_OMIT_PDF_FIELDS,
49    #UG_OMIT_MANAGE_FIELDS,
50    #UG_OMIT_EDIT_FIELDS,
[16049]51    #PG_OMIT_DISPLAY_FIELDS,
52    #PG_OMIT_PDF_FIELDS,
53    #PG_OMIT_MANAGE_FIELDS,
54    #PG_OMIT_EDIT_FIELDS,
[15807]55    )
56from kofacustom.iuokada.applicants.interfaces import (
[15878]57    ICustomPGApplicant, ICustomUGApplicant, ICustomApplicant,
[15807]58    ICustomPGApplicantEdit, ICustomUGApplicantEdit,
[16142]59    ICustomApplicantOnlinePayment, ICustomApplicantRefereeReport,
[16281]60    ITranscriptApplicant, ICustomApplicantRegisterUpdate
[15807]61    )
[15563]62from kofacustom.iuokada.interfaces import MessageFactory as _
[10765]63
[15878]64MAX_FILE_UPLOAD_SIZE = 1024 * 500
65
[15884]66# UG students are all undergraduate students.
67UG_OMIT_DISPLAY_FIELDS = OMIT_DISPLAY_FIELDS + (
68    #'jamb_subjects_list',
69    'programme_type',)
70UG_OMIT_PDF_FIELDS = UG_OMIT_DISPLAY_FIELDS + ('phone',)
71UG_OMIT_MANAGE_FIELDS = (
72    'special_application',
73    #'jamb_subjects_list',
[16075]74    'programme_type',
75    'course1',
76    'course2',)
[15884]77UG_OMIT_EDIT_FIELDS = UG_OMIT_MANAGE_FIELDS + OMIT_DISPLAY_FIELDS + (
78    'student_id',
79    'notice',
80    'screening_score',
81    'screening_venue',
82    'screening_date',
83    #'jamb_age',
84    #'jamb_subjects',
85    #'jamb_score',
86    #'jamb_reg_number',
[16075]87    'aggregate',
88    )
[15884]89
[16559]90JUPEB_OMIT_FIELDS = (
91    'jamb_age',
92    'jamb_subjects',
93    'jamb_score',
94    'jamb_reg_number',
95    'jamb_subjects_list',
96    'jamb_fname',
97    )
98
[16049]99# PG has its own interface
[16053]100PG_OMIT_DISPLAY_FIELDS = OMIT_DISPLAY_FIELDS + (
101    'employer',
102    'emp_position',
103    'emp_start',
104    'emp_end',
105    'emp_reason',
106    'employer2',
107    'emp2_position',
108    'emp2_start',
109    'emp2_end',
110    'emp2_reason',
111    )
[16049]112PG_OMIT_PDF_FIELDS = PG_OMIT_DISPLAY_FIELDS + ('phone',)
113PG_OMIT_MANAGE_FIELDS = (
114    'special_application',
115    'employer',
116    'emp_position',
117    'emp_start',
118    'emp_end',
119    'emp_reason',
120    'employer2',
121    'emp2_position',
122    'emp2_start',
123    'emp2_end',
124    'emp2_reason',
[16075]125    'course1',
126    'course2',
[16049]127    )
128PG_OMIT_EDIT_FIELDS = PG_OMIT_MANAGE_FIELDS + PG_OMIT_DISPLAY_FIELDS + (
[16053]129    'student_id',
130    'notice',
131    'screening_score',
132    'screening_venue',
[16049]133    'screening_date',)
134
[16209]135TRANS_OMIT_FIELDS = ('suspended', 'applicant_id',)
[16142]136
137TRANS_SHORT_OMIT_FIELDS = TRANS_OMIT_FIELDS + (
[16165]138    #'date_of_birth',
[16142]139    'sex',
140    'nationality',
[16165]141    #'entry_mode',
142    #'entry_session',
[16142]143    'end_session',
[16165]144    #'course_studied',
145    #'course_changed',
146    #'change_level',
[16142]147    )
148
[17024]149class CustomApplicantsRootPage(ApplicantsRootPage):
150   
151    grok.template('applicantsrootpage')
152
[15809]153class CustomApplicantsContainerPage(ApplicantsContainerPage):
154    """The standard view for regular applicant containers.
155    """
156
157    @property
158    def form_fields(self):
159        form_fields = grok.AutoFields(IApplicantsContainer).omit(
160            'title', 'description')
161        if self.request.principal.id == 'zope.anybody':
162            form_fields = form_fields.omit(
163                'code', 'prefix', 'year', 'mode', 'hidden',
164                'strict_deadline', 'application_category',
[15883]165                'application_slip_notice',
166                'application_fee', 'with_picture',
167                'startdate', 'enddate')
[15809]168        return form_fields
169
[16328]170class CustomApplicantsContainerManageFormPage(ApplicantsContainerManageFormPage):
171
[17159]172    max_applicants = 5000
[16328]173
[15793]174class CustomApplicantDisplayFormPage(NigeriaApplicantDisplayFormPage):
175    """A display view for applicant data.
176    """
[15807]177
[15793]178    @property
179    def form_fields(self):
[16165]180        if self.target is not None and self.target == 'tscf':
[16142]181            form_fields = grok.AutoFields(ITranscriptApplicant)
182            for field in TRANS_OMIT_FIELDS:
183                form_fields = form_fields.omit(field)
184            form_fields['dispatch_address'].custom_widget = BytesDisplayWidget
[16165]185            #form_fields['perm_address'].custom_widget = BytesDisplayWidget
[16142]186            return form_fields
[16165]187        if self.target is not None and self.target == 'tscs':
[16142]188            form_fields = grok.AutoFields(ITranscriptApplicant)
189            for field in TRANS_SHORT_OMIT_FIELDS:
190                form_fields = form_fields.omit(field)
191            form_fields['dispatch_address'].custom_widget = BytesDisplayWidget
[16165]192            #form_fields['perm_address'].custom_widget = BytesDisplayWidget
[16142]193            return form_fields
[15807]194        if self.target is not None and self.target.startswith('pg'):
195            form_fields = grok.AutoFields(ICustomPGApplicant)
196            for field in PG_OMIT_DISPLAY_FIELDS:
197                form_fields = form_fields.omit(field)
[15793]198        else:
[15807]199            form_fields = grok.AutoFields(ICustomUGApplicant)
200            for field in UG_OMIT_DISPLAY_FIELDS:
201                form_fields = form_fields.omit(field)
[16559]202        if self.target is not None and self.target == 'pre':
203            for field in JUPEB_OMIT_FIELDS:
204                form_fields = form_fields.omit(field)
[15807]205        #form_fields['perm_address'].custom_widget = BytesDisplayWidget
206        form_fields['notice'].custom_widget = BytesDisplayWidget
207        if not getattr(self.context, 'student_id'):
208            form_fields = form_fields.omit('student_id')
209        if not getattr(self.context, 'screening_score'):
210            form_fields = form_fields.omit('screening_score')
211        if not getattr(self.context, 'screening_venue') or self._not_paid():
212            form_fields = form_fields.omit('screening_venue')
213        if not getattr(self.context, 'screening_date') or self._not_paid():
214            form_fields = form_fields.omit('screening_date')
[15793]215        return form_fields
216
[16165]217def getCerts(view, coursex):
218    yield(dict(code='', title='--', selected=''))
219    appcatcertificatesource = AppCatCertificateSource().factory
220    for cert in appcatcertificatesource.getValues(view.context):
221        selected = ''
222        course = getattr(view.context, coursex)
223        if course is not None and course.code == cert.code:
224            selected = 'selected'
225        title = appcatcertificatesource.getTitle(view.context, cert)
226        yield(dict(code=cert.code, title=title, selected=selected))
227
[16220]228def saveCourses(view):
[16165]229    """In custom packages we needed to customize the certificate
230    select widget. We just save course1 and course2 if these customized
231    fields appear in the form.
232    """
[16220]233    changed_courses = []
[16165]234    form = view.request.form
235    course1 = form.get('custom.course1', None)
[16220]236    if not course1:
237        return 'Please select your 1st Choice Course of Study.', None
238    cat = queryUtility(ICatalog, name='certificates_catalog')
239    results = list(
240        cat.searchResults(code=(course1, course1)))
241    new_course1 = results[0]
242    old_course1 = view.context.course1
243    if old_course1 != new_course1:
244        view.context.course1 = new_course1
245        changed_courses.append('course1')
246    new_course2 = None
247    old_course2 = view.context.course2
[16165]248    course2 = form.get('custom.course2', None)
249    if course2:
250        results = list(
251            cat.searchResults(code=(course2, course2)))
[16220]252        new_course2 = results[0]
253    if old_course2 != new_course2:
254        view.context.course2 = new_course2
255        changed_courses.append('course2')
256    return None, changed_courses
[16165]257
258def display_fileupload(view, filename):
259    if view.target.startswith('tsc'):
260        return False
261    if filename[1] == 'res_stat.pdf':
262        if view.context.subtype != 'transfer':
263            return False
264    if filename[1] == 'jamb.pdf' \
265        and view.target is not None \
266        and view.target.startswith('pg'):
267        return False
268    if filename[1] == 'nysc.pdf' \
269        and view.target is not None \
270        and not view.target.startswith('pg'):
271        return False
272    return True
273
[15807]274class CustomApplicantManageFormPage(NigeriaApplicantManageFormPage):
275    """A full edit view for applicant data.
276    """
277
[16075]278    def getCerts(self, coursex):
[16165]279        return getCerts(self, coursex)
[16070]280
[16220]281    def saveCourses(self):
282        return saveCourses(self)
[16075]283
[16165]284    def display_fileupload(self, filename):
285        return display_fileupload(self, filename)
286
[16070]287    @property
[16051]288    def display_refereereports(self):
289        if self.context.refereereports:
290            return True
291        return False
292
293    @property
[15807]294    def form_fields(self):
[16165]295        self.course_selector = False
296        if self.target is not None and self.target == 'tscf':
[16142]297            form_fields = grok.AutoFields(ITranscriptApplicant)
298            for field in TRANS_OMIT_FIELDS:
299                form_fields = form_fields.omit(field)
300            return form_fields
[16165]301        if self.target is not None and self.target == 'tscs':
[16142]302            form_fields = grok.AutoFields(ITranscriptApplicant)
303            for field in TRANS_SHORT_OMIT_FIELDS:
304                form_fields = form_fields.omit(field)
305            return form_fields
[16165]306        self.course_selector = True
[15807]307        if self.target is not None and self.target.startswith('pg'):
308            form_fields = grok.AutoFields(ICustomPGApplicant)
309            for field in PG_OMIT_MANAGE_FIELDS:
310                form_fields = form_fields.omit(field)
311        else:
312            form_fields = grok.AutoFields(ICustomUGApplicant)
313            for field in UG_OMIT_MANAGE_FIELDS:
314                form_fields = form_fields.omit(field)
[16559]315        if self.target is not None and self.target == 'pre':
316            for field in JUPEB_OMIT_FIELDS:
317                form_fields = form_fields.omit(field)
[15807]318        form_fields['student_id'].for_display = True
319        form_fields['applicant_id'].for_display = True
320        return form_fields
321
322class CustomApplicantEditFormPage(NigeriaApplicantEditFormPage):
323    """An applicant-centered edit view for applicant data.
324    """
325
[16075]326    def getCerts(self, coursex):
[16165]327        return getCerts(self, coursex)
[16070]328
[16075]329    def saveCourses(self):
[16165]330        return saveCourses(self)
[16075]331
[15947]332    def display_fileupload(self, filename):
[16165]333        return display_fileupload(self, filename)
[15947]334
[16961]335    @property
336    def _finalsubmit_msg(self):
337        if self.context.subtype in ('transfer', 'de'):
338            return 'Form has been submitted. Please note: DE and Transfer applicants are to present their application form and the proof of JAMB local transfer form at the admissions office for application approval.'
339        return _('Form has been submitted.')
340
[15878]341    def dataNotComplete(self, data):
342        store = getUtility(IExtFileStore)
[16566]343        if self.context.subtype \
[16648]344            and self.context.subtype not in ('transfer', 'de', 'jupeb') \
[16565]345            and (not self.context.jamb_fname
346                 or not self.context.jamb_reg_number):
347            return _('JAMB fields must be filled.')
[16958]348        if self.context.subtype in ('transfer', 'de') \
349            and not self.context.ref_number:
350            return _('Reference Number field must be filled.')
[15878]351        if self.context.__parent__.with_picture:
352            store = getUtility(IExtFileStore)
353            if not store.getFileByContext(self.context, attr=u'passport.jpg'):
354                return _('No passport picture uploaded.')
[16077]355        if self.context.subtype == 'transfer' \
356            and self.context.__parent__.code!= 'ug2020' \
357            and not store.getFileByContext(self.context, attr=u'res_stat.pdf'):
[15878]358            return _('No statement of result pdf file uploaded.')
359        return False
360
[15807]361    @property
362    def form_fields(self):
[16165]363        self.course_selector = False
364        if self.target is not None and self.target == 'tscf':
[16142]365            form_fields = grok.AutoFields(ITranscriptApplicant)
366            for field in TRANS_OMIT_FIELDS:
367                form_fields = form_fields.omit(field)
[16209]368                form_fields = form_fields.omit('locked')
[16142]369            return form_fields
[16165]370        if self.target is not None and self.target == 'tscs':
[16142]371            form_fields = grok.AutoFields(ITranscriptApplicant)
372            for field in TRANS_SHORT_OMIT_FIELDS:
373                form_fields = form_fields.omit(field)
[16209]374                form_fields = form_fields.omit('locked')
[16142]375            return form_fields
[16165]376        self.course_selector = True
[15807]377        if self.target is not None and self.target.startswith('pg'):
378            form_fields = grok.AutoFields(ICustomPGApplicantEdit)
379            for field in PG_OMIT_EDIT_FIELDS:
380                form_fields = form_fields.omit(field)
381        else:
382            form_fields = grok.AutoFields(ICustomUGApplicantEdit)
383            for field in UG_OMIT_EDIT_FIELDS:
384                form_fields = form_fields.omit(field)
[16559]385        if self.target is not None and self.target == 'pre':
386            for field in JUPEB_OMIT_FIELDS:
387                form_fields = form_fields.omit(field)
[15807]388        form_fields['applicant_id'].for_display = True
389        form_fields['reg_number'].for_display = True
390        return form_fields
391
392class CustomPDFApplicationSlip(NigeriaPDFApplicationSlip):
393
394    @property
395    def form_fields(self):
396        if self.target is not None and self.target.startswith('pg'):
397            form_fields = grok.AutoFields(ICustomPGApplicant)
398            for field in PG_OMIT_PDF_FIELDS:
399                form_fields = form_fields.omit(field)
400        else:
401            form_fields = grok.AutoFields(ICustomUGApplicant)
402            for field in UG_OMIT_PDF_FIELDS:
403                form_fields = form_fields.omit(field)
404        if not getattr(self.context, 'student_id'):
405            form_fields = form_fields.omit('student_id')
406        if not getattr(self.context, 'screening_score'):
407            form_fields = form_fields.omit('screening_score')
408        if not getattr(self.context, 'screening_venue'):
409            form_fields = form_fields.omit('screening_venue')
410        if not getattr(self.context, 'screening_date'):
411            form_fields = form_fields.omit('screening_date')
412        return form_fields
413
[16281]414class CustomApplicantRegistrationPage(ApplicantRegistrationPage):
415    """Captcha'd registration page for applicants.
416    """
417    @property
418    def form_fields(self):
419        form_fields = None
420        if self.context.mode == 'update':
421            form_fields = grok.AutoFields(ICustomApplicantRegisterUpdate).select(
422                'lastname','reg_number','email')
423        else: #if self.context.mode == 'create':
424            form_fields = grok.AutoFields(ICustomUGApplicant).select(
425                'firstname', 'middlename', 'lastname', 'email', 'phone')
426        return form_fields
427
[16559]428class CustomApplicantAddFormPage(ApplicantAddFormPage):
429    """Add-form to add an applicant.
430    """
431    form_fields = grok.AutoFields(ICustomApplicant).select(
432        'firstname', 'middlename', 'lastname',
433        'email', 'phone')
434
[16062]435class RefereeReportAddFormPage(RefereeReportAddFormPage):
436    """Add-form to add an referee report. This form
437    is protected by a mandate.
438    """
439    form_fields = grok.AutoFields(
440        ICustomApplicantRefereeReport).omit('creation_date')
441
442class CustomRefereeReportDisplayFormPage(RefereeReportDisplayFormPage):
443    """A display view for referee reports.
444    """
445    form_fields = grok.AutoFields(ICustomApplicantRefereeReport)
446    form_fields[
447        'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
448
449
450class CustomExportPDFReportSlipPage(ExportPDFReportSlipPage):
451    form_fields = grok.AutoFields(ICustomApplicantRefereeReport)
452    form_fields[
453        'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
454
455class CustomExportPDFReportSlipPage2(ExportPDFReportSlipPage2):
456    form_fields = grok.AutoFields(ICustomApplicantRefereeReport)
457    form_fields[
458        'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
459
[15947]460class ResultStatement(AdditionalFile):
[16546]461    grok.name('res_stat')
[15878]462
[15947]463class JAMBResult(AdditionalFile):
[16546]464    grok.name('jamb')
[16014]465
466class FirstSitting(AdditionalFile):
[16546]467    grok.name('fst_sit_scan')
[16014]468
469class SecondSitting(AdditionalFile):
[16546]470    grok.name('scd_sit_scan')
[16014]471
472class HighQual(AdditionalFile):
[16546]473    grok.name('hq_scan')
[16017]474
475class AdvancedLevelResult(AdditionalFile):
[16546]476    grok.name('alr_scan')
[16052]477
478class NYSCCertificate(AdditionalFile):
[16546]479    grok.name('nysc')
[16501]480
481class PaymentReceipts(AdditionalFile):
[16546]482    grok.name('rmp')
Note: See TracBrowser for help on using the repository browser.