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

Last change on this file since 16189 was 16026, checked in by Henrik Bettermann, 5 years ago

Implement HND and ND Final Clearance Pack applications.

  • Property svn:keywords set to Id
File size: 16.6 KB
Line 
1## $Id: browser.py 16026 2020-03-06 14:07:50Z 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
21from zope.component import getUtility, queryUtility
22from zope.i18n import translate
23from hurry.workflow.interfaces import IWorkflowState
24from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget
25from zope.formlib.textwidgets import BytesDisplayWidget
26from waeup.kofa.interfaces import IExtFileStore, IKofaUtils
27from waeup.kofa.browser.pages import doll_up
28from waeup.kofa.applicants.interfaces import (
29    IApplicant, IApplicantEdit,
30    IApplicantsContainer,)
31from waeup.kofa.applicants.browser import (ApplicantDisplayFormPage,
32    ApplicantManageFormPage, ApplicantEditFormPage,
33    ApplicantsContainerPage, ApplicationFeePaymentAddPage,
34    ExportJobContainerOverview,
35    ExportJobContainerJobStart,
36    ExportJobContainerDownload,
37    ApplicantBaseDisplayFormPage)
38from waeup.kofa.browser.viewlets import ManageActionButton
39from waeup.kofa.applicants.viewlets import (
40    PaymentReceiptActionButton, PDFActionButton)
41from waeup.kofa.applicants.pdf import PDFApplicationSlip
42from waeup.kofa.applicants.workflow import ADMITTED, PAID, STARTED
43from waeup.kofa.students.interfaces import IStudentsUtils
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    )
50from kofacustom.nigeria.applicants.browser import (
51    NigeriaExportPDFPaymentSlipPage)
52from kofacustom.dspg.applicants.interfaces import (
53    ICustomUGApplicant, ICustomUGApplicantEdit,
54    ICustomSpecialApplicant,
55    ICustomApplicantOnlinePayment,
56    ICustomApplicant,
57    ND_OMIT_DISPLAY_FIELDS,
58    ND_OMIT_PDF_FIELDS,
59    ND_OMIT_MANAGE_FIELDS,
60    ND_OMIT_EDIT_FIELDS
61    )
62
63from kofacustom.dspg.interfaces import MessageFactory as _
64
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')]
68
69PRE_OMIT_EDIT_FIELDS = UG_OMIT_EDIT_FIELDS + [
70    'firstname',
71    'middlename',
72    'lastname',
73    #'sex',
74    'jamb_score'
75    ]
76
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
89    @property
90    def target_url(self):
91        return self.view.url(self.view.context) + '/exports/bursary.html'
92
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')
98    grok.name('bursary.html')
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.'))
141        self.redirect(self.url(self.context, 'bursary.html'))
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
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:
171            return grok.AutoFields(ICustomSpecialApplicant)
172        if self.target in ('conv', 'hndfincl', 'ndfincl'):
173            form_fields = grok.AutoFields(ICustomSpecialApplicant)
174            form_fields = form_fields.omit(
175                'special_application',
176                'carryover_courses_1',
177                'carryover_courses_2',
178                'locked', 'suspended')
179            return form_fields
180        form_fields = grok.AutoFields(ICustomUGApplicant)
181        if self.context.is_nd:
182            for field in ND_OMIT_DISPLAY_FIELDS:
183                form_fields = form_fields.omit(field)
184        else:
185            form_fields = grok.AutoFields(ICustomUGApplicant)
186            for field in UG_OMIT_DISPLAY_FIELDS:
187                form_fields = form_fields.omit(field)
188        form_fields['notice'].custom_widget = BytesDisplayWidget
189        form_fields['jamb_subjects'].custom_widget = BytesDisplayWidget
190        if not getattr(self.context, 'student_id'):
191            form_fields = form_fields.omit('student_id')
192        if not getattr(self.context, 'screening_score'):
193            form_fields = form_fields.omit('screening_score')
194        if not getattr(self.context, 'screening_venue'):
195            form_fields = form_fields.omit('screening_venue')
196        if not getattr(self.context, 'screening_date'):
197            form_fields = form_fields.omit('screening_date')
198        return form_fields
199
200class CustomPDFApplicationSlip(PDFApplicationSlip):
201
202    def _reduced_slip(self):
203        return getattr(self.context, 'result_uploaded', False)
204
205    @property
206    def note(self):
207        if self.context.special:
208            return '''<br /><br />
209Head of Department Signature:
210'''
211        note = getattr(self.context.__parent__, 'application_slip_notice', None)
212        if note:
213            return '<br /><br />' + note
214        pronoun = 'S/he'
215        if self.context.sex == 'm':
216            pronoun = 'He'
217        else:
218            pronoun = 'She'
219        return '''<br /><br />
220The applicant has declared that:
221
222a) %s is not a member of any secret cult and will not join any.
223b) %s will not engage in any cult activities.
224c) %s will not be involved in any acts of terrorism, rape, robbery, fighting, illegal gathering and
225any activities that could disrupt peace on campus.
226d) %s does not have and will not acquire any form of weapon, fire arms, gun knife or any weapon
227that can cause damage to life and property.
228e) %s will strive to be worthy in character and learning at all times.
229
230The applicant has acknowledged that offences will automatically lead to the expulsion from the
231Polytechnic and also be dealt with in accordance with the law of the land.
232
233The applicant promises to abide by all the Rules and Regulations of the Polytechnic if offered
234admission.
235''' % (
236    pronoun, pronoun, pronoun, pronoun, pronoun)
237
238    @property
239    def form_fields(self):
240        if self.target in ('conv', 'special', 'hndfincl', 'ndfincl'):
241            form_fields = grok.AutoFields(ICustomSpecialApplicant)
242            form_fields = form_fields.omit(
243                'special_application',
244                'locked', 'suspended', 'applicant_id')
245            if not self.context.carryover_courses_1:
246                form_fields = form_fields.omit('carryover_courses_1')
247            if not self.context.carryover_courses_2:
248                form_fields = form_fields.omit('carryover_courses_2')
249            return form_fields
250        form_fields = grok.AutoFields(ICustomUGApplicant)
251        if self.context.is_nd:
252            for field in ND_OMIT_PDF_FIELDS:
253                form_fields = form_fields.omit(field)
254        else:
255            form_fields = grok.AutoFields(ICustomUGApplicant)
256            for field in UG_OMIT_PDF_FIELDS:
257                form_fields = form_fields.omit(field)
258        if not getattr(self.context, 'student_id'):
259            form_fields = form_fields.omit('student_id')
260        if not getattr(self.context, 'screening_score'):
261            form_fields = form_fields.omit('screening_score')
262        if not getattr(self.context, 'screening_venue'):
263            form_fields = form_fields.omit('screening_venue')
264        if not getattr(self.context, 'screening_date'):
265            form_fields = form_fields.omit('screening_date')
266        return form_fields
267
268class CustomApplicantManageFormPage(ApplicantManageFormPage):
269    """A full edit view for applicant data.
270    """
271
272    @property
273    def form_fields(self):
274        if self.context.special:
275            form_fields = grok.AutoFields(ICustomSpecialApplicant)
276            form_fields['applicant_id'].for_display = True
277            return form_fields
278        if self.target in ('conv', 'hndfincl', 'ndfincl'):
279            form_fields = grok.AutoFields(ICustomSpecialApplicant)
280            form_fields = form_fields.omit(
281                'special_application',
282                'carryover_courses_1',
283                'carryover_courses_2',
284                'locked', 'suspended')
285            form_fields['applicant_id'].for_display = True
286            return form_fields
287        form_fields = grok.AutoFields(ICustomUGApplicant)
288        if self.context.is_nd:
289            for field in ND_OMIT_MANAGE_FIELDS:
290                form_fields = form_fields.omit(field)
291        else:
292            for field in UG_OMIT_MANAGE_FIELDS:
293                form_fields = form_fields.omit(field)
294        form_fields['student_id'].for_display = True
295        form_fields['applicant_id'].for_display = True
296        return form_fields
297
298    def setUpWidgets(self, ignore_request=False):
299        super(CustomApplicantManageFormPage,self).setUpWidgets(ignore_request)
300        if self.context.special:
301            self.widgets['carryover_courses_1'].height = 3
302            self.widgets['carryover_courses_2'].height = 3
303        return
304
305class CustomApplicantEditFormPage(ApplicantEditFormPage):
306    """An applicant-centered edit view for applicant data.
307    """
308
309    def unremovable(self, ticket):
310        return True
311
312    @property
313    def form_fields(self):
314        if self.context.special:
315            form_fields = grok.AutoFields(ICustomSpecialApplicant).omit(
316                'locked', 'suspended')
317            form_fields['applicant_id'].for_display = True
318            return form_fields
319        if self.target in ('conv', 'hndfincl', 'ndfincl'):
320            form_fields = grok.AutoFields(ICustomSpecialApplicant)
321            form_fields = form_fields.omit(
322                'special_application',
323                'carryover_courses_1',
324                'carryover_courses_2',
325                'locked', 'suspended')
326            form_fields['applicant_id'].for_display = True
327            return form_fields
328        form_fields = grok.AutoFields(ICustomUGApplicantEdit)
329        if self.context.is_nd:
330            for field in ND_OMIT_EDIT_FIELDS:
331                form_fields = form_fields.omit(field)
332        elif self.target is not None and self.target.startswith('pre'):
333            for field in PRE_OMIT_EDIT_FIELDS:
334                form_fields = form_fields.omit(field)
335        else:
336            for field in UG_OMIT_EDIT_FIELDS:
337                form_fields = form_fields.omit(field)
338        form_fields['applicant_id'].for_display = True
339        form_fields['reg_number'].for_display = True
340        return form_fields
341
342    def setUpWidgets(self, ignore_request=False):
343        super(CustomApplicantEditFormPage,self).setUpWidgets(ignore_request)
344        if self.context.special:
345            self.widgets['carryover_courses_1'].height = 3
346            self.widgets['carryover_courses_2'].height = 3
347        return
348
349    @property
350    def display_actions(self):
351        state = IWorkflowState(self.context).getState()
352        # If the form is unlocked, applicants are allowed to save the form
353        # and remove unused tickets.
354        actions = [[_('Save')], []]
355        # Only in state started they can also add tickets.
356        if state == STARTED:
357            actions = [[_('Save')],
358                [_('Add online payment ticket'),]]
359        # In state paid, they can submit the data and further add tickets
360        # if the application is special.
361        elif self.context.special and state == PAID:
362            actions = [[_('Save'), _('Finally Submit')],
363                [_('Add online payment ticket'),]]
364        elif state == PAID:
365            actions = [[_('Save'), _('Finally Submit')], []]
366        return actions
367
368class CustomApplicationFeePaymentAddPage(ApplicationFeePaymentAddPage):
369    """ Page to add an online payment ticket
370    """
371
372    @property
373    def custom_requirements(self):
374        store = getUtility(IExtFileStore)
375        if not store.getFileByContext(self.context, attr=u'passport.jpg') \
376            and self.context.__parent__.with_picture:
377            return _('Upload your passport photo before making payment.')
378        return ''
379
380class CustomApplicantBaseDisplayFormPage(ApplicantBaseDisplayFormPage):
381
382    grok.context(ICustomApplicant)
383
384    @property
385    def form_fields(self):
386        if self.context.__parent__.prefix in (
387            'conv', 'special', 'hndfincl', 'ndfincl'):
388            form_fields = grok.AutoFields(ICustomApplicant).select(
389                'applicant_id', 'reg_number', 'email')
390            form_fields['reg_number'].field.title = u'Identification Number'
391            return form_fields
392        form_fields = grok.AutoFields(ICustomApplicant).select(
393            'applicant_id', 'reg_number', 'email', 'course1')
394        return form_fields
395
396class CustomExportPDFPaymentSlipPage(NigeriaExportPDFPaymentSlipPage):
397    """Deliver a PDF slip of the context.
398    """
399
400    grok.context(ICustomApplicantOnlinePayment)
401
402    @property
403    def form_fields(self):
404        form_fields = grok.AutoFields(ICustomApplicantOnlinePayment).omit(
405            'ac', 'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item',
406            'p_split_data')
407        form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
408        form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
409        if self.context.__parent__.__parent__.prefix in (
410            'conv', 'special', 'hndfincl', 'ndfincl'):
411            form_fields = form_fields.omit('p_session')
412        return form_fields
413
414    def render(self):
415        if self.payment_slip_download_warning:
416            self.flash(self.payment_slip_download_warning, type='danger')
417            self.redirect(self.url(self.context))
418            return
419        applicantview = CustomApplicantBaseDisplayFormPage(self.context.__parent__,
420            self.request)
421        students_utils = getUtility(IStudentsUtils)
422        return students_utils.renderPDF(self,'payment_slip.pdf',
423            self.context.__parent__, applicantview, note=self.note)
Note: See TracBrowser for help on using the repository browser.