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

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

Add components to enable exports in applicants section for bursary officers.

  • Property svn:keywords set to Id
File size: 12.4 KB
Line 
1## $Id: browser.py 15498 2019-07-15 19:44:05Z 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)
37from waeup.kofa.browser.viewlets import ManageActionButton
38from waeup.kofa.applicants.viewlets import (
39    PaymentReceiptActionButton, PDFActionButton)
40from waeup.kofa.applicants.pdf import PDFApplicationSlip
41from waeup.kofa.applicants.workflow import ADMITTED, PAID, STARTED
42from kofacustom.nigeria.applicants.interfaces import (
43    UG_OMIT_DISPLAY_FIELDS,
44    UG_OMIT_PDF_FIELDS,
45    UG_OMIT_MANAGE_FIELDS,
46    UG_OMIT_EDIT_FIELDS
47    )
48from kofacustom.dspg.applicants.interfaces import (
49    ICustomUGApplicant, ICustomUGApplicantEdit,
50    ICustomSpecialApplicant,
51    ND_OMIT_DISPLAY_FIELDS,
52    ND_OMIT_PDF_FIELDS,
53    ND_OMIT_MANAGE_FIELDS,
54    ND_OMIT_EDIT_FIELDS
55    )
56
57from kofacustom.dspg.interfaces import MessageFactory as _
58
59UG_OMIT_EDIT_FIELDS = [
60    value for value in UG_OMIT_EDIT_FIELDS
61        if not value in ('jamb_subjects', 'jamb_score', 'jamb_reg_number')]
62
63PRE_OMIT_EDIT_FIELDS = UG_OMIT_EDIT_FIELDS + [
64    'firstname',
65    'middlename',
66    'lastname',
67    #'sex',
68    'jamb_score'
69    ]
70
71##### Bursary exports
72
73class BursaryExportPaymentsActionButton(ManageActionButton):
74    """ 'Export payment data' button for faculties.
75    """
76    grok.context(IApplicantsContainer)
77    grok.view(ApplicantsContainerPage)
78    grok.require('waeup.exportBursaryData')
79    icon = 'actionicon_down.png'
80    text = _('Export payment data')
81    target = 'bursary_exports/index2.html'
82    grok.order(4)
83
84class BursaryExportJobContainerOverview(ExportJobContainerOverview):
85    """Page that lists active applicant data export jobs and provides links
86    to discard or download CSV files.
87    """
88    grok.require('waeup.exportBursaryData')
89    grok.name('index2.html')
90
91    def update(self, CREATE=None, DISCARD=None, job_id=None):
92        if CREATE:
93            self.redirect(self.url('@@start_bursary_export'))
94            return
95        if DISCARD and job_id:
96            entry = self.context.entry_from_job_id(job_id)
97            self.context.delete_export_entry(entry)
98            ob_class = self.__implemented__.__name__.replace('waeup.kofa.','')
99            self.context.logger.info(
100                '%s - discarded: job_id=%s' % (ob_class, job_id))
101            self.flash(_('Discarded export') + ' %s' % job_id)
102        self.entries = doll_up(self, user=self.request.principal.id)
103        return
104
105class  BursaryExportJobContainerJobStart(ExportJobContainerJobStart):
106    """View that starts export job.
107    """
108    grok.require('waeup.exportBursaryData')
109    grok.name('start_bursary_export')
110
111    def update(self):
112        utils = queryUtility(IKofaUtils)
113        if not utils.expensive_actions_allowed():
114            self.flash(_(
115                "Currently, exporters cannot be started due to high "
116                "system load. Please try again later."), type='danger')
117            self.entries = doll_up(self, user=None)
118            return
119
120        ob_class = self.__implemented__.__name__.replace('waeup.kofa.','')
121        container_code = self.context.__parent__.code
122        # Start payments exporter
123        exporter = 'applicantpayments'
124        job_id = self.context.start_export_job(exporter,
125                                      self.request.principal.id,
126                                      container=container_code)
127        self.context.logger.info(
128            '%s - exported: %s (%s), job_id=%s'
129            % (ob_class, exporter, container_code, job_id))
130
131        self.flash(_('Exports started.'))
132        self.redirect(self.url(self.context))
133        return
134
135    def render(self):
136        return
137
138class BursaryExportJobContainerDownload(ExportJobContainerDownload):
139    """Page that downloads a students export csv file.
140
141    """
142    grok.require('waeup.exportBursaryData')
143    grok.context(IApplicantsContainer)
144
145class CustomApplicantsContainerPage(ApplicantsContainerPage):
146    """The standard view for regular applicant containers.
147    """
148
149    @property
150    def form_fields(self):
151        form_fields = super(CustomApplicantsContainerPage, self).form_fields
152        if self.request.principal.id == 'zope.anybody':
153            return form_fields.omit('application_fee')
154        return form_fields
155
156class CustomApplicantDisplayFormPage(ApplicantDisplayFormPage):
157    """A display view for applicant data.
158    """
159
160    @property
161    def form_fields(self):
162        if self.context.special:
163            return grok.AutoFields(ICustomSpecialApplicant)
164        form_fields = grok.AutoFields(ICustomUGApplicant)
165        if self.context.is_nd:
166            for field in ND_OMIT_DISPLAY_FIELDS:
167                form_fields = form_fields.omit(field)
168        else:
169            form_fields = grok.AutoFields(ICustomUGApplicant)
170            for field in UG_OMIT_DISPLAY_FIELDS:
171                form_fields = form_fields.omit(field)
172        form_fields['notice'].custom_widget = BytesDisplayWidget
173        form_fields['jamb_subjects'].custom_widget = BytesDisplayWidget
174        if not getattr(self.context, 'student_id'):
175            form_fields = form_fields.omit('student_id')
176        if not getattr(self.context, 'screening_score'):
177            form_fields = form_fields.omit('screening_score')
178        if not getattr(self.context, 'screening_venue'):
179            form_fields = form_fields.omit('screening_venue')
180        if not getattr(self.context, 'screening_date'):
181            form_fields = form_fields.omit('screening_date')
182        return form_fields
183
184class CustomPDFApplicationSlip(PDFApplicationSlip):
185
186    def _reduced_slip(self):
187        return getattr(self.context, 'result_uploaded', False)
188
189    @property
190    def note(self):
191        note = getattr(self.context.__parent__, 'application_slip_notice', None)
192        if note:
193            return '<br /><br />' + note
194        pronoun = 'S/he'
195        if self.context.sex == 'm':
196            pronoun = 'He'
197        else:
198            pronoun = 'She'
199        return '''<br /><br />
200The applicant has declared that:
201
202a) %s is not a member of any secret cult and will not join any.
203b) %s will not engage in any cult activities.
204c) %s will not be involved in any acts of terrorism, rape, robbery, fighting, illegal gathering and
205any activities that could disrupt peace on campus.
206d) %s does not have and will not acquire any form of weapon, fire arms, gun knife or any weapon
207that can cause damage to life and property.
208e) %s will strive to be worthy in character and learning at all times.
209
210The applicant has acknowledged that offences will automatically lead to the expulsion from the
211Polytechnic and also be dealt with in accordance with the law of the land.
212
213The applicant promises to abide by all the Rules and Regulations of the Polytechnic if offered
214admission.
215''' % (
216    pronoun, pronoun, pronoun, pronoun, pronoun)
217
218    @property
219    def form_fields(self):
220        form_fields = grok.AutoFields(ICustomUGApplicant)
221        if self.context.is_nd:
222            for field in ND_OMIT_PDF_FIELDS:
223                form_fields = form_fields.omit(field)
224        else:
225            form_fields = grok.AutoFields(ICustomUGApplicant)
226            for field in UG_OMIT_PDF_FIELDS:
227                form_fields = form_fields.omit(field)
228        if not getattr(self.context, 'student_id'):
229            form_fields = form_fields.omit('student_id')
230        if not getattr(self.context, 'screening_score'):
231            form_fields = form_fields.omit('screening_score')
232        if not getattr(self.context, 'screening_venue'):
233            form_fields = form_fields.omit('screening_venue')
234        if not getattr(self.context, 'screening_date'):
235            form_fields = form_fields.omit('screening_date')
236        return form_fields
237
238class CustomApplicantManageFormPage(ApplicantManageFormPage):
239    """A full edit view for applicant data.
240    """
241
242    @property
243    def form_fields(self):
244        if self.context.special:
245            form_fields = grok.AutoFields(ICustomSpecialApplicant)
246            form_fields['applicant_id'].for_display = True
247            return form_fields
248        form_fields = grok.AutoFields(ICustomUGApplicant)
249        if self.context.is_nd:
250            for field in ND_OMIT_MANAGE_FIELDS:
251                form_fields = form_fields.omit(field)
252        else:
253            for field in UG_OMIT_MANAGE_FIELDS:
254                form_fields = form_fields.omit(field)
255        form_fields['student_id'].for_display = True
256        form_fields['applicant_id'].for_display = True
257        return form_fields
258
259    def setUpWidgets(self, ignore_request=False):
260        super(CustomApplicantManageFormPage,self).setUpWidgets(ignore_request)
261        if self.context.special:
262            self.widgets['carryover_courses'].height = 3
263        return
264
265class CustomApplicantEditFormPage(ApplicantEditFormPage):
266    """An applicant-centered edit view for applicant data.
267    """
268
269    def unremovable(self, ticket):
270        return True
271
272    @property
273    def form_fields(self):
274        if self.context.special:
275            form_fields = grok.AutoFields(ICustomSpecialApplicant).omit(
276                'locked', 'suspended')
277            form_fields['applicant_id'].for_display = True
278            return form_fields
279        form_fields = grok.AutoFields(ICustomUGApplicantEdit)
280        if self.context.is_nd:
281            for field in ND_OMIT_EDIT_FIELDS:
282                form_fields = form_fields.omit(field)
283        elif self.target is not None and self.target.startswith('pre'):
284            for field in PRE_OMIT_EDIT_FIELDS:
285                form_fields = form_fields.omit(field)
286        else:
287            for field in UG_OMIT_EDIT_FIELDS:
288                form_fields = form_fields.omit(field)
289        form_fields['applicant_id'].for_display = True
290        form_fields['reg_number'].for_display = True
291        return form_fields
292
293    def setUpWidgets(self, ignore_request=False):
294        super(CustomApplicantEditFormPage,self).setUpWidgets(ignore_request)
295        if self.context.special:
296            self.widgets['carryover_courses'].height = 3
297        return
298
299    @property
300    def display_actions(self):
301        state = IWorkflowState(self.context).getState()
302        # If the form is unlocked, applicants are allowed to save the form
303        # and remove unused tickets.
304        actions = [[_('Save')], []]
305        # Only in state started they can also add tickets.
306        if state == STARTED:
307            actions = [[_('Save')],
308                [_('Add online payment ticket'),]]
309        # In state paid, they can submit the data and further add tickets
310        # if the application is special.
311        elif self.context.special and state == PAID:
312            actions = [[_('Save'), _('Finally Submit')],
313                [_('Add online payment ticket'),]]
314        elif state == PAID:
315            actions = [[_('Save'), _('Finally Submit')], []]
316        return actions
317
318class CustomApplicationFeePaymentAddPage(ApplicationFeePaymentAddPage):
319    """ Page to add an online payment ticket
320    """
321
322    @property
323    def custom_requirements(self):
324        store = getUtility(IExtFileStore)
325        if not store.getFileByContext(self.context, attr=u'passport.jpg'):
326            return _('Upload your passport photo before making payment.')
327        return ''
Note: See TracBrowser for help on using the repository browser.