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

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

Customize ApplicantBaseDisplayFormPage? and CustomExportPDFPaymentSlipPage.

  • Property svn:keywords set to Id
File size: 15.8 KB
Line 
1## $Id: browser.py 15580 2019-09-04 08:26:25Z 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 == 'conv':
173            form_fields = grok.AutoFields(ICustomSpecialApplicant)
174            form_fields = form_fields.omit(
175                'special_application', 'carryover_courses',
176                'locked', 'suspended')
177            return form_fields
178        form_fields = grok.AutoFields(ICustomUGApplicant)
179        if self.context.is_nd:
180            for field in ND_OMIT_DISPLAY_FIELDS:
181                form_fields = form_fields.omit(field)
182        else:
183            form_fields = grok.AutoFields(ICustomUGApplicant)
184            for field in UG_OMIT_DISPLAY_FIELDS:
185                form_fields = form_fields.omit(field)
186        form_fields['notice'].custom_widget = BytesDisplayWidget
187        form_fields['jamb_subjects'].custom_widget = BytesDisplayWidget
188        if not getattr(self.context, 'student_id'):
189            form_fields = form_fields.omit('student_id')
190        if not getattr(self.context, 'screening_score'):
191            form_fields = form_fields.omit('screening_score')
192        if not getattr(self.context, 'screening_venue'):
193            form_fields = form_fields.omit('screening_venue')
194        if not getattr(self.context, 'screening_date'):
195            form_fields = form_fields.omit('screening_date')
196        return form_fields
197
198class CustomPDFApplicationSlip(PDFApplicationSlip):
199
200    def _reduced_slip(self):
201        return getattr(self.context, 'result_uploaded', False)
202
203    @property
204    def note(self):
205        note = getattr(self.context.__parent__, 'application_slip_notice', None)
206        if note:
207            return '<br /><br />' + note
208        pronoun = 'S/he'
209        if self.context.sex == 'm':
210            pronoun = 'He'
211        else:
212            pronoun = 'She'
213        return '''<br /><br />
214The applicant has declared that:
215
216a) %s is not a member of any secret cult and will not join any.
217b) %s will not engage in any cult activities.
218c) %s will not be involved in any acts of terrorism, rape, robbery, fighting, illegal gathering and
219any activities that could disrupt peace on campus.
220d) %s does not have and will not acquire any form of weapon, fire arms, gun knife or any weapon
221that can cause damage to life and property.
222e) %s will strive to be worthy in character and learning at all times.
223
224The applicant has acknowledged that offences will automatically lead to the expulsion from the
225Polytechnic and also be dealt with in accordance with the law of the land.
226
227The applicant promises to abide by all the Rules and Regulations of the Polytechnic if offered
228admission.
229''' % (
230    pronoun, pronoun, pronoun, pronoun, pronoun)
231
232    @property
233    def form_fields(self):
234        if self.target == 'conv':
235            form_fields = grok.AutoFields(ICustomSpecialApplicant)
236            form_fields = form_fields.omit(
237                'special_application', 'carryover_courses',
238                'locked', 'suspended')
239            return form_fields
240        form_fields = grok.AutoFields(ICustomUGApplicant)
241        if self.context.is_nd:
242            for field in ND_OMIT_PDF_FIELDS:
243                form_fields = form_fields.omit(field)
244        else:
245            form_fields = grok.AutoFields(ICustomUGApplicant)
246            for field in UG_OMIT_PDF_FIELDS:
247                form_fields = form_fields.omit(field)
248        if not getattr(self.context, 'student_id'):
249            form_fields = form_fields.omit('student_id')
250        if not getattr(self.context, 'screening_score'):
251            form_fields = form_fields.omit('screening_score')
252        if not getattr(self.context, 'screening_venue'):
253            form_fields = form_fields.omit('screening_venue')
254        if not getattr(self.context, 'screening_date'):
255            form_fields = form_fields.omit('screening_date')
256        return form_fields
257
258class CustomApplicantManageFormPage(ApplicantManageFormPage):
259    """A full edit view for applicant data.
260    """
261
262    @property
263    def form_fields(self):
264        if self.context.special:
265            form_fields = grok.AutoFields(ICustomSpecialApplicant)
266            form_fields['applicant_id'].for_display = True
267            return form_fields
268        if self.target == 'conv':
269            form_fields = grok.AutoFields(ICustomSpecialApplicant)
270            form_fields = form_fields.omit(
271                'special_application', 'carryover_courses',
272                'locked', 'suspended')
273            form_fields['applicant_id'].for_display = True
274            return form_fields
275        form_fields = grok.AutoFields(ICustomUGApplicant)
276        if self.context.is_nd:
277            for field in ND_OMIT_MANAGE_FIELDS:
278                form_fields = form_fields.omit(field)
279        else:
280            for field in UG_OMIT_MANAGE_FIELDS:
281                form_fields = form_fields.omit(field)
282        form_fields['student_id'].for_display = True
283        form_fields['applicant_id'].for_display = True
284        return form_fields
285
286    def setUpWidgets(self, ignore_request=False):
287        super(CustomApplicantManageFormPage,self).setUpWidgets(ignore_request)
288        if self.context.special:
289            self.widgets['carryover_courses'].height = 3
290        return
291
292class CustomApplicantEditFormPage(ApplicantEditFormPage):
293    """An applicant-centered edit view for applicant data.
294    """
295
296    def unremovable(self, ticket):
297        return True
298
299    @property
300    def form_fields(self):
301        if self.context.special:
302            form_fields = grok.AutoFields(ICustomSpecialApplicant).omit(
303                'locked', 'suspended')
304            form_fields['applicant_id'].for_display = True
305            return form_fields
306        if self.target == 'conv':
307            form_fields = grok.AutoFields(ICustomSpecialApplicant)
308            form_fields = form_fields.omit(
309                'special_application', 'carryover_courses',
310                'locked', 'suspended')
311            form_fields['applicant_id'].for_display = True
312            return form_fields
313        form_fields = grok.AutoFields(ICustomUGApplicantEdit)
314        if self.context.is_nd:
315            for field in ND_OMIT_EDIT_FIELDS:
316                form_fields = form_fields.omit(field)
317        elif self.target is not None and self.target.startswith('pre'):
318            for field in PRE_OMIT_EDIT_FIELDS:
319                form_fields = form_fields.omit(field)
320        else:
321            for field in UG_OMIT_EDIT_FIELDS:
322                form_fields = form_fields.omit(field)
323        form_fields['applicant_id'].for_display = True
324        form_fields['reg_number'].for_display = True
325        return form_fields
326
327    def setUpWidgets(self, ignore_request=False):
328        super(CustomApplicantEditFormPage,self).setUpWidgets(ignore_request)
329        if self.context.special:
330            self.widgets['carryover_courses'].height = 3
331        return
332
333    @property
334    def display_actions(self):
335        state = IWorkflowState(self.context).getState()
336        # If the form is unlocked, applicants are allowed to save the form
337        # and remove unused tickets.
338        actions = [[_('Save')], []]
339        # Only in state started they can also add tickets.
340        if state == STARTED:
341            actions = [[_('Save')],
342                [_('Add online payment ticket'),]]
343        # In state paid, they can submit the data and further add tickets
344        # if the application is special.
345        elif self.context.special and state == PAID:
346            actions = [[_('Save'), _('Finally Submit')],
347                [_('Add online payment ticket'),]]
348        elif state == PAID:
349            actions = [[_('Save'), _('Finally Submit')], []]
350        return actions
351
352class CustomApplicationFeePaymentAddPage(ApplicationFeePaymentAddPage):
353    """ Page to add an online payment ticket
354    """
355
356    @property
357    def custom_requirements(self):
358        store = getUtility(IExtFileStore)
359        if not store.getFileByContext(self.context, attr=u'passport.jpg') \
360            and self.context.__parent__.with_picture:
361            return _('Upload your passport photo before making payment.')
362        return ''
363
364class CustomApplicantBaseDisplayFormPage(ApplicantBaseDisplayFormPage):
365
366    grok.context(ICustomApplicant)
367
368    @property
369    def form_fields(self):
370        if self.context.__parent__.prefix in ('conv', 'special'):
371            form_fields = grok.AutoFields(ICustomApplicant).select(
372                'applicant_id', 'reg_number', 'email')
373            form_fields['reg_number'].field.title = u'Identification Number'
374            return form_fields
375        form_fields = grok.AutoFields(ICustomApplicant).select(
376            'applicant_id', 'reg_number', 'email', 'course1')
377        return form_fields
378
379class CustomExportPDFPaymentSlipPage(NigeriaExportPDFPaymentSlipPage):
380    """Deliver a PDF slip of the context.
381    """
382
383    grok.context(ICustomApplicantOnlinePayment)
384
385    @property
386    def form_fields(self):
387        form_fields = grok.AutoFields(ICustomApplicantOnlinePayment).omit(
388            'ac', 'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item',
389            'p_split_data')
390        form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
391        form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
392        if self.context.__parent__.__parent__.prefix in ('conv', 'special'):
393            form_fields = form_fields.omit('p_session')
394        return form_fields
395
396    def render(self):
397        if self.payment_slip_download_warning:
398            self.flash(self.payment_slip_download_warning, type='danger')
399            self.redirect(self.url(self.context))
400            return
401        applicantview = CustomApplicantBaseDisplayFormPage(self.context.__parent__,
402            self.request)
403        students_utils = getUtility(IStudentsUtils)
404        return students_utils.renderPDF(self,'payment_slip.pdf',
405            self.context.__parent__, applicantview, note=self.note)
Note: See TracBrowser for help on using the repository browser.