source: main/kofacustom.dspg/trunk/src/kofacustom/dspg/applicants/browser.py @ 17403

Last change on this file since 17403 was 16729, checked in by Henrik Bettermann, 3 years ago

Student ID field should be for remedial course slip only

  • Property svn:keywords set to Id
File size: 16.7 KB
RevLine 
[10765]1## $Id: browser.py 16729 2021-12-02 06:30:02Z 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
[15498]21from zope.component import getUtility, queryUtility
[14721]22from zope.i18n import translate
[14807]23from hurry.workflow.interfaces import IWorkflowState
[14721]24from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget
25from zope.formlib.textwidgets import BytesDisplayWidget
[14732]26from waeup.kofa.interfaces import IExtFileStore, IKofaUtils
[15498]27from waeup.kofa.browser.pages import doll_up
[14721]28from waeup.kofa.applicants.interfaces import (
[15498]29    IApplicant, IApplicantEdit,
30    IApplicantsContainer,)
[14721]31from waeup.kofa.applicants.browser import (ApplicantDisplayFormPage,
32    ApplicantManageFormPage, ApplicantEditFormPage,
[15498]33    ApplicantsContainerPage, ApplicationFeePaymentAddPage,
34    ExportJobContainerOverview,
35    ExportJobContainerJobStart,
[15580]36    ExportJobContainerDownload,
37    ApplicantBaseDisplayFormPage)
[15498]38from waeup.kofa.browser.viewlets import ManageActionButton
[14721]39from waeup.kofa.applicants.viewlets import (
40    PaymentReceiptActionButton, PDFActionButton)
41from waeup.kofa.applicants.pdf import PDFApplicationSlip
[14807]42from waeup.kofa.applicants.workflow import ADMITTED, PAID, STARTED
[15580]43from waeup.kofa.students.interfaces import IStudentsUtils
[14721]44from kofacustom.nigeria.applicants.interfaces import (
45    UG_OMIT_DISPLAY_FIELDS,
46    UG_OMIT_PDF_FIELDS,
47    UG_OMIT_MANAGE_FIELDS,
48    UG_OMIT_EDIT_FIELDS
49    )
[15580]50from kofacustom.nigeria.applicants.browser import (
51    NigeriaExportPDFPaymentSlipPage)
[14721]52from kofacustom.dspg.applicants.interfaces import (
53    ICustomUGApplicant, ICustomUGApplicantEdit,
[14865]54    ICustomSpecialApplicant,
[15580]55    ICustomApplicantOnlinePayment,
56    ICustomApplicant,
[14721]57    ND_OMIT_DISPLAY_FIELDS,
58    ND_OMIT_PDF_FIELDS,
59    ND_OMIT_MANAGE_FIELDS,
60    ND_OMIT_EDIT_FIELDS
61    )
[10765]62
[14732]63from kofacustom.dspg.interfaces import MessageFactory as _
64
[14721]65UG_OMIT_EDIT_FIELDS = [
66    value for value in UG_OMIT_EDIT_FIELDS
67        if not value in ('jamb_subjects', 'jamb_score', 'jamb_reg_number')]
[10765]68
[14721]69PRE_OMIT_EDIT_FIELDS = UG_OMIT_EDIT_FIELDS + [
70    'firstname',
71    'middlename',
72    'lastname',
73    #'sex',
74    'jamb_score'
75    ]
76
[15498]77##### Bursary exports
78
79class BursaryExportPaymentsActionButton(ManageActionButton):
80    """ 'Export payment data' button for faculties.
81    """
82    grok.context(IApplicantsContainer)
83    grok.view(ApplicantsContainerPage)
84    grok.require('waeup.exportBursaryData')
85    icon = 'actionicon_down.png'
86    text = _('Export payment data')
87    grok.order(4)
88
[15518]89    @property
90    def target_url(self):
91        return self.view.url(self.view.context) + '/exports/bursary.html'
92
[15498]93class BursaryExportJobContainerOverview(ExportJobContainerOverview):
94    """Page that lists active applicant data export jobs and provides links
95    to discard or download CSV files.
96    """
97    grok.require('waeup.exportBursaryData')
[15518]98    grok.name('bursary.html')
[15498]99
100    def update(self, CREATE=None, DISCARD=None, job_id=None):
101        if CREATE:
102            self.redirect(self.url('@@start_bursary_export'))
103            return
104        if DISCARD and job_id:
105            entry = self.context.entry_from_job_id(job_id)
106            self.context.delete_export_entry(entry)
107            ob_class = self.__implemented__.__name__.replace('waeup.kofa.','')
108            self.context.logger.info(
109                '%s - discarded: job_id=%s' % (ob_class, job_id))
110            self.flash(_('Discarded export') + ' %s' % job_id)
111        self.entries = doll_up(self, user=self.request.principal.id)
112        return
113
114class  BursaryExportJobContainerJobStart(ExportJobContainerJobStart):
115    """View that starts export job.
116    """
117    grok.require('waeup.exportBursaryData')
118    grok.name('start_bursary_export')
119
120    def update(self):
121        utils = queryUtility(IKofaUtils)
122        if not utils.expensive_actions_allowed():
123            self.flash(_(
124                "Currently, exporters cannot be started due to high "
125                "system load. Please try again later."), type='danger')
126            self.entries = doll_up(self, user=None)
127            return
128
129        ob_class = self.__implemented__.__name__.replace('waeup.kofa.','')
130        container_code = self.context.__parent__.code
131        # Start payments exporter
132        exporter = 'applicantpayments'
133        job_id = self.context.start_export_job(exporter,
134                                      self.request.principal.id,
135                                      container=container_code)
136        self.context.logger.info(
137            '%s - exported: %s (%s), job_id=%s'
138            % (ob_class, exporter, container_code, job_id))
139
140        self.flash(_('Exports started.'))
[15518]141        self.redirect(self.url(self.context, 'bursary.html'))
[15498]142        return
143
144    def render(self):
145        return
146
147class BursaryExportJobContainerDownload(ExportJobContainerDownload):
148    """Page that downloads a students export csv file.
149
150    """
151    grok.require('waeup.exportBursaryData')
152
[14721]153class CustomApplicantsContainerPage(ApplicantsContainerPage):
154    """The standard view for regular applicant containers.
155    """
156
157    @property
158    def form_fields(self):
159        form_fields = super(CustomApplicantsContainerPage, self).form_fields
160        if self.request.principal.id == 'zope.anybody':
161            return form_fields.omit('application_fee')
162        return form_fields
163
164class CustomApplicantDisplayFormPage(ApplicantDisplayFormPage):
165    """A display view for applicant data.
166    """
167
168    @property
169    def form_fields(self):
170        if self.context.special:
[14865]171            return grok.AutoFields(ICustomSpecialApplicant)
[16026]172        if self.target in ('conv', 'hndfincl', 'ndfincl'):
[15576]173            form_fields = grok.AutoFields(ICustomSpecialApplicant)
174            form_fields = form_fields.omit(
[15592]175                'special_application',
176                'carryover_courses_1',
177                'carryover_courses_2',
[16729]178                'locked', 'suspended',
179                'student_id')
[15576]180            return form_fields
[14721]181        form_fields = grok.AutoFields(ICustomUGApplicant)
182        if self.context.is_nd:
183            for field in ND_OMIT_DISPLAY_FIELDS:
184                form_fields = form_fields.omit(field)
185        else:
186            form_fields = grok.AutoFields(ICustomUGApplicant)
187            for field in UG_OMIT_DISPLAY_FIELDS:
188                form_fields = form_fields.omit(field)
189        form_fields['notice'].custom_widget = BytesDisplayWidget
190        form_fields['jamb_subjects'].custom_widget = BytesDisplayWidget
191        if not getattr(self.context, 'student_id'):
192            form_fields = form_fields.omit('student_id')
193        if not getattr(self.context, 'screening_score'):
194            form_fields = form_fields.omit('screening_score')
195        if not getattr(self.context, 'screening_venue'):
196            form_fields = form_fields.omit('screening_venue')
197        if not getattr(self.context, 'screening_date'):
198            form_fields = form_fields.omit('screening_date')
199        return form_fields
200
201class CustomPDFApplicationSlip(PDFApplicationSlip):
202
203    def _reduced_slip(self):
204        return getattr(self.context, 'result_uploaded', False)
205
206    @property
207    def note(self):
[15588]208        if self.context.special:
[15592]209            return '''<br /><br />
210Head of Department Signature:
211'''
[14721]212        note = getattr(self.context.__parent__, 'application_slip_notice', None)
213        if note:
214            return '<br /><br />' + note
215        pronoun = 'S/he'
216        if self.context.sex == 'm':
217            pronoun = 'He'
218        else:
219            pronoun = 'She'
220        return '''<br /><br />
221The applicant has declared that:
222
223a) %s is not a member of any secret cult and will not join any.
224b) %s will not engage in any cult activities.
225c) %s will not be involved in any acts of terrorism, rape, robbery, fighting, illegal gathering and
226any activities that could disrupt peace on campus.
227d) %s does not have and will not acquire any form of weapon, fire arms, gun knife or any weapon
228that can cause damage to life and property.
229e) %s will strive to be worthy in character and learning at all times.
230
231The applicant has acknowledged that offences will automatically lead to the expulsion from the
232Polytechnic and also be dealt with in accordance with the law of the land.
233
234The applicant promises to abide by all the Rules and Regulations of the Polytechnic if offered
235admission.
236''' % (
237    pronoun, pronoun, pronoun, pronoun, pronoun)
238
239    @property
240    def form_fields(self):
[16726]241        if self.context.special:
242            return grok.AutoFields(ICustomSpecialApplicant)
243        if self.target in ('conv', 'hndfincl', 'ndfincl'):
[15576]244            form_fields = grok.AutoFields(ICustomSpecialApplicant)
245            form_fields = form_fields.omit(
[15588]246                'special_application',
[16729]247                'locked', 'suspended', 'applicant_id', 'student_id')
[15592]248            if not self.context.carryover_courses_1:
249                form_fields = form_fields.omit('carryover_courses_1')
250            if not self.context.carryover_courses_2:
251                form_fields = form_fields.omit('carryover_courses_2')
[15576]252            return form_fields
[14721]253        form_fields = grok.AutoFields(ICustomUGApplicant)
254        if self.context.is_nd:
255            for field in ND_OMIT_PDF_FIELDS:
256                form_fields = form_fields.omit(field)
257        else:
258            form_fields = grok.AutoFields(ICustomUGApplicant)
259            for field in UG_OMIT_PDF_FIELDS:
260                form_fields = form_fields.omit(field)
261        if not getattr(self.context, 'student_id'):
262            form_fields = form_fields.omit('student_id')
263        if not getattr(self.context, 'screening_score'):
264            form_fields = form_fields.omit('screening_score')
265        if not getattr(self.context, 'screening_venue'):
266            form_fields = form_fields.omit('screening_venue')
267        if not getattr(self.context, 'screening_date'):
268            form_fields = form_fields.omit('screening_date')
269        return form_fields
270
271class CustomApplicantManageFormPage(ApplicantManageFormPage):
272    """A full edit view for applicant data.
273    """
274
275    @property
276    def form_fields(self):
277        if self.context.special:
[14865]278            form_fields = grok.AutoFields(ICustomSpecialApplicant)
[14721]279            form_fields['applicant_id'].for_display = True
280            return form_fields
[16026]281        if self.target in ('conv', 'hndfincl', 'ndfincl'):
[15576]282            form_fields = grok.AutoFields(ICustomSpecialApplicant)
283            form_fields = form_fields.omit(
[15592]284                'special_application',
285                'carryover_courses_1',
286                'carryover_courses_2',
[16729]287                'locked', 'suspended',
288                'student_id')
[15579]289            form_fields['applicant_id'].for_display = True
[15576]290            return form_fields
[14721]291        form_fields = grok.AutoFields(ICustomUGApplicant)
292        if self.context.is_nd:
293            for field in ND_OMIT_MANAGE_FIELDS:
294                form_fields = form_fields.omit(field)
295        else:
296            for field in UG_OMIT_MANAGE_FIELDS:
297                form_fields = form_fields.omit(field)
298        form_fields['student_id'].for_display = True
299        form_fields['applicant_id'].for_display = True
300        return form_fields
301
[14865]302    def setUpWidgets(self, ignore_request=False):
303        super(CustomApplicantManageFormPage,self).setUpWidgets(ignore_request)
304        if self.context.special:
[15592]305            self.widgets['carryover_courses_1'].height = 3
306            self.widgets['carryover_courses_2'].height = 3
[14865]307        return
308
[14721]309class CustomApplicantEditFormPage(ApplicantEditFormPage):
310    """An applicant-centered edit view for applicant data.
311    """
312
[14807]313    def unremovable(self, ticket):
314        return True
315
[14721]316    @property
317    def form_fields(self):
318        if self.context.special:
[14865]319            form_fields = grok.AutoFields(ICustomSpecialApplicant).omit(
[14721]320                'locked', 'suspended')
321            form_fields['applicant_id'].for_display = True
322            return form_fields
[16026]323        if self.target in ('conv', 'hndfincl', 'ndfincl'):
[15576]324            form_fields = grok.AutoFields(ICustomSpecialApplicant)
325            form_fields = form_fields.omit(
[15592]326                'special_application',
327                'carryover_courses_1',
328                'carryover_courses_2',
[16729]329                'locked', 'suspended',
330                'student_id')
[15579]331            form_fields['applicant_id'].for_display = True
[15576]332            return form_fields
[14721]333        form_fields = grok.AutoFields(ICustomUGApplicantEdit)
334        if self.context.is_nd:
335            for field in ND_OMIT_EDIT_FIELDS:
336                form_fields = form_fields.omit(field)
337        elif self.target is not None and self.target.startswith('pre'):
338            for field in PRE_OMIT_EDIT_FIELDS:
339                form_fields = form_fields.omit(field)
340        else:
341            for field in UG_OMIT_EDIT_FIELDS:
342                form_fields = form_fields.omit(field)
343        form_fields['applicant_id'].for_display = True
344        form_fields['reg_number'].for_display = True
[14732]345        return form_fields
346
[14865]347    def setUpWidgets(self, ignore_request=False):
348        super(CustomApplicantEditFormPage,self).setUpWidgets(ignore_request)
349        if self.context.special:
[15592]350            self.widgets['carryover_courses_1'].height = 3
351            self.widgets['carryover_courses_2'].height = 3
[14865]352        return
353
[14807]354    @property
355    def display_actions(self):
356        state = IWorkflowState(self.context).getState()
357        # If the form is unlocked, applicants are allowed to save the form
358        # and remove unused tickets.
359        actions = [[_('Save')], []]
360        # Only in state started they can also add tickets.
361        if state == STARTED:
362            actions = [[_('Save')],
363                [_('Add online payment ticket'),]]
364        # In state paid, they can submit the data and further add tickets
365        # if the application is special.
366        elif self.context.special and state == PAID:
367            actions = [[_('Save'), _('Finally Submit')],
368                [_('Add online payment ticket'),]]
369        elif state == PAID:
370            actions = [[_('Save'), _('Finally Submit')], []]
371        return actions
372
[14732]373class CustomApplicationFeePaymentAddPage(ApplicationFeePaymentAddPage):
374    """ Page to add an online payment ticket
375    """
376
377    @property
378    def custom_requirements(self):
379        store = getUtility(IExtFileStore)
[15576]380        if not store.getFileByContext(self.context, attr=u'passport.jpg') \
381            and self.context.__parent__.with_picture:
[14732]382            return _('Upload your passport photo before making payment.')
[15580]383        return ''
384
385class CustomApplicantBaseDisplayFormPage(ApplicantBaseDisplayFormPage):
386
387    grok.context(ICustomApplicant)
388
389    @property
390    def form_fields(self):
[16026]391        if self.context.__parent__.prefix in (
392            'conv', 'special', 'hndfincl', 'ndfincl'):
[15580]393            form_fields = grok.AutoFields(ICustomApplicant).select(
394                'applicant_id', 'reg_number', 'email')
395            form_fields['reg_number'].field.title = u'Identification Number'
396            return form_fields
397        form_fields = grok.AutoFields(ICustomApplicant).select(
398            'applicant_id', 'reg_number', 'email', 'course1')
399        return form_fields
400
401class CustomExportPDFPaymentSlipPage(NigeriaExportPDFPaymentSlipPage):
402    """Deliver a PDF slip of the context.
403    """
404
405    grok.context(ICustomApplicantOnlinePayment)
406
407    @property
408    def form_fields(self):
409        form_fields = grok.AutoFields(ICustomApplicantOnlinePayment).omit(
410            'ac', 'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item',
411            'p_split_data')
412        form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
413        form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
[16026]414        if self.context.__parent__.__parent__.prefix in (
415            'conv', 'special', 'hndfincl', 'ndfincl'):
[15580]416            form_fields = form_fields.omit('p_session')
417        return form_fields
418
419    def render(self):
420        if self.payment_slip_download_warning:
421            self.flash(self.payment_slip_download_warning, type='danger')
422            self.redirect(self.url(self.context))
423            return
424        applicantview = CustomApplicantBaseDisplayFormPage(self.context.__parent__,
425            self.request)
426        students_utils = getUtility(IStudentsUtils)
427        return students_utils.renderPDF(self,'payment_slip.pdf',
428            self.context.__parent__, applicantview, note=self.note)
Note: See TracBrowser for help on using the repository browser.