source: main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/students/browser.py @ 12911

Last change on this file since 12911 was 12907, checked in by Henrik Bettermann, 9 years ago

Implement automatic matric_number assignment. Students can induce the
assignment if they have paid school fees by clicking the
'Get Matriculation Number' button. Numbering is per department and session.

  • Property svn:keywords set to Id
File size: 19.8 KB
Line 
1## $Id: browser.py 12907 2015-05-06 10:13:38Z henrik $
2##
3## Copyright (C) 2012 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##
18import grok
19from zope.i18n import translate
20from zope.formlib.textwidgets import BytesDisplayWidget
21from zope.component import getUtility
22from waeup.kofa.interfaces import IKofaUtils
23from waeup.kofa.browser.layout import UtilityView
24from waeup.kofa.students.interfaces import IStudentsUtils
25from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget
26from waeup.kofa.students.browser import (
27    StartClearancePage, BedTicketAddPage, ExportPDFAdmissionSlipPage,
28    StudentBasePDFFormPage)
29from waeup.kwarapoly.students.interfaces import (
30    ICustomStudent, ICustomStudentBase, ICustomStudentStudyLevel,
31    ICustomUGStudentClearance, ICustomPGStudentClearance)
32from waeup.kwarapoly.interfaces import MessageFactory as _
33from waeup.kofa.students.workflow import (
34    ADMITTED, PAID, REQUESTED, RETURNING, CLEARED, REGISTERED,
35    VALIDATED, GRADUATED, TRANSCRIPT, CREATED, CLEARANCE)
36from kofacustom.nigeria.students.browser import (
37    NigeriaOnlinePaymentDisplayFormPage,
38    NigeriaOnlinePaymentAddFormPage,
39    NigeriaExportPDFPaymentSlipPage,
40    NigeriaStudentClearanceDisplayFormPage,
41    NigeriaExportPDFClearanceSlipPage,
42    NigeriaStudentClearanceEditFormPage,
43    NigeriaExportPDFCourseRegistrationSlipPage,
44    NigeriaStudentPersonalDisplayFormPage,
45    NigeriaStudentClearanceManageFormPage,
46    NigeriaStudentPersonalEditFormPage,
47    NigeriaStudentPersonalManageFormPage,
48    NigeriaStudentBaseEditFormPage
49    )
50
51from waeup.kwarapoly.students.interfaces import (
52    ICustomStudent,
53    ICustomStudentOnlinePayment,
54    ICustomStudentPersonal,
55    ICustomStudentPersonalEdit
56    )
57
58
59class CustomStudentBaseEditFormPage(NigeriaStudentBaseEditFormPage):
60    """ View to edit student base data
61    """
62    form_fields = grok.AutoFields(ICustomStudentBase).select(
63        'email', 'phone', 'sex')
64
65
66class CustomStudentPersonalDisplayFormPage(
67    NigeriaStudentPersonalDisplayFormPage):
68    """ Page to display student personal data
69    """
70    form_fields = grok.AutoFields(ICustomStudentPersonal)
71    form_fields['perm_address'].custom_widget = BytesDisplayWidget
72    form_fields['next_kin_address'].custom_widget = BytesDisplayWidget
73    form_fields['corr_address'].custom_widget = BytesDisplayWidget
74    form_fields['sponsor_address'].custom_widget = BytesDisplayWidget
75    form_fields[
76        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
77
78
79class CustomStudentPersonalEditFormPage(NigeriaStudentPersonalEditFormPage):
80    """ Page to edit personal data
81    """
82    form_fields = grok.AutoFields(ICustomStudentPersonalEdit).omit(
83        'personal_updated')
84
85
86class CustomStudentPersonalManageFormPage(
87    NigeriaStudentPersonalManageFormPage):
88    """ Page to edit personal data
89    """
90    form_fields = grok.AutoFields(ICustomStudentPersonal)
91    form_fields['personal_updated'].for_display = True
92    form_fields[
93        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
94
95
96class CustomOnlinePaymentDisplayFormPage(NigeriaOnlinePaymentDisplayFormPage):
97    """ Page to view an online payment ticket
98    """
99    grok.context(ICustomStudentOnlinePayment)
100    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
101        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
102    form_fields[
103        'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
104    form_fields[
105        'payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
106
107
108class CustomOnlinePaymentAddFormPage(NigeriaOnlinePaymentAddFormPage):
109    """ Page to add an online payment ticket
110    """
111    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).select(
112        'p_category')
113
114
115class CustomExportPDFPaymentSlipPage(NigeriaExportPDFPaymentSlipPage):
116    """Deliver a PDF slip of the context.
117    """
118    grok.context(ICustomStudentOnlinePayment)
119    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
120        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
121    form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget(
122        'le')
123    form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget(
124        'le')
125
126
127class CustomStartClearancePage(StartClearancePage):
128
129    with_ac = False
130
131
132class CustomStudentClearanceDisplayFormPage(
133    NigeriaStudentClearanceDisplayFormPage):
134    """ Page to display student clearance data
135    """
136
137    @property
138    def form_fields(self):
139        if self.context.is_postgrad:
140            form_fields = grok.AutoFields(
141                ICustomPGStudentClearance).omit('clearance_locked')
142        else:
143            form_fields = grok.AutoFields(
144                ICustomUGStudentClearance).omit('clearance_locked')
145        if not getattr(self.context, 'officer_comment'):
146            form_fields = form_fields.omit('officer_comment')
147        else:
148            form_fields['officer_comment'].custom_widget = BytesDisplayWidget
149        return form_fields
150
151
152class CustomExportPDFClearanceSlipPage(NigeriaExportPDFClearanceSlipPage):
153    """Deliver a PDF slip of the context.
154    """
155
156    @property
157    def form_fields(self):
158        if self.context.is_postgrad:
159            form_fields = grok.AutoFields(
160                ICustomPGStudentClearance).omit('clearance_locked')
161        else:
162            form_fields = grok.AutoFields(
163                ICustomUGStudentClearance).omit('clearance_locked')
164        if not getattr(self.context, 'officer_comment'):
165            form_fields = form_fields.omit('officer_comment')
166        return form_fields
167
168
169class CustomStudentClearanceManageFormPage(
170    NigeriaStudentClearanceManageFormPage):
171    """ Page to edit student clearance data
172    """
173
174    @property
175    def form_fields(self):
176        if self.context.is_postgrad:
177            form_fields = grok.AutoFields(
178                ICustomPGStudentClearance).omit('clr_code')
179        else:
180            form_fields = grok.AutoFields(
181                ICustomUGStudentClearance).omit('clr_code')
182        return form_fields
183
184
185class CustomStudentClearanceEditFormPage(NigeriaStudentClearanceEditFormPage):
186    """ View to edit student clearance data by student
187    """
188
189    def dataNotComplete(self):
190        return False
191
192    @property
193    def form_fields(self):
194        if self.context.is_postgrad:
195            form_fields = grok.AutoFields(ICustomPGStudentClearance).omit(
196            'clearance_locked', 'nysc_location', 'clr_code', 'officer_comment',
197            'physical_clearance_date')
198        else:
199            form_fields = grok.AutoFields(ICustomUGStudentClearance).omit(
200            'clearance_locked', 'clr_code', 'officer_comment',
201            'physical_clearance_date')
202        return form_fields
203
204
205class BedTicketAddPage(BedTicketAddPage):
206    """ Page to add an online payment ticket
207    """
208    buttonname = _('Create bed ticket')
209    notice = ''
210    with_ac = False
211
212
213class CustomExportPDFAdmissionSlipPage(ExportPDFAdmissionSlipPage):
214    """Deliver a PDF Admission slip.
215    """
216    grok.context(ICustomStudent)
217
218    omit_fields = ('date_of_birth', 'current_level')
219
220    form_fields = grok.AutoFields(ICustomStudent).select(
221        'student_id', 'reg_number')
222
223    def render(self):
224        if self.context.state in (CREATED, ADMITTED,
225                                  CLEARANCE, REQUESTED, CLEARED):
226            self.flash('Not allowed.')
227            self.redirect(self.url(self.context))
228            return
229        students_utils = getUtility(IStudentsUtils)
230        return students_utils.renderPDFAdmissionLetter(self,
231            self.context.student, omit_fields=self.omit_fields)
232
233
234class ExportPDFAdmissionNotificationPage(UtilityView, grok.View):
235    """Deliver a PDF Admission notification slip.
236    """
237    grok.context(ICustomStudent)
238    grok.name('admission_notification.pdf')
239    grok.require('waeup.viewStudent')
240    prefix = 'form'
241    label = 'Notification of Provisional Admission'
242
243    omit_fields = ('date_of_birth', 'current_level')
244
245    form_fields = grok.AutoFields(ICustomStudent).select(
246        'student_id', 'reg_number', 'sex', 'lga')
247
248    def render(self):
249        if self.context.state not in (ADMITTED, CLEARANCE, REQUESTED, CLEARED):
250            self.flash('Not allowed.')
251            self.redirect(self.url(self.context))
252            return
253        students_utils = getUtility(IStudentsUtils)
254        pre_text = ''
255        post_text = post_text_freshers
256        return students_utils.renderPDFAdmissionLetter(self,
257            self.context.student, omit_fields=self.omit_fields,
258            pre_text=pre_text, post_text=post_text)
259
260
261# copied from waeup.aaue
262class CustomExportPDFCourseRegistrationSlipPage(
263    NigeriaExportPDFCourseRegistrationSlipPage):
264    """Deliver a PDF slip of the context.
265    """
266    grok.context(ICustomStudentStudyLevel)
267    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
268        'level_session', 'level_verdict',
269        'validated_by', 'validation_date', 'gpa', 'level')
270
271    omit_fields = ('password', 'suspended', 'suspended_comment',
272        'phone', 'adm_code', 'sex', 'email', 'date_of_birth',
273        'department', 'current_mode', 'current_level')
274
275    @property
276    def title(self):
277        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
278        return translate(_('Credits Registered'), 'waeup.kofa',
279            target_language=portal_language)
280
281    def _signatures(self):
282        """Signatures as inserted at document bottom.
283
284        As Kwarapoly requires a fancy 'certificate' there, the pre-
285        and post-fields of signatures are quite large and unusual
286        here.
287
288        This is also a workaround, as we cannot easily insert text
289        with signature fields in documents using reportlab platypus.
290
291        The signature boxes we return here contain context infos
292        (therefore this has to be a method) and return the following
293        layout:
294
295            +-----------------------------------------+
296            | (Empty pre text)                        |
297            +-------------+---------------------------+
298            |Date         | Students Signature        |
299            +-------------+---------------------------+
300            | (Empty post text)                       |
301            +=========================================+
302            |            Certification                |
303            +-------------+---------------------------+
304            |Date         | Director Signature, etc.  |
305            +-------------+---------------------------+
306            |NOTE: This form is the ...               |
307            +-----------------------------------------+
308
309
310        """
311        return (
312            [
313                ('', _('Student\'s Signature'), ''),
314                ],
315            [((
316                    "<br/>"
317                    + "&nbsp;" * 70 +
318                    "<u><b><font size='14'>Certification</font></b></u><br/>"
319                    "<br/><b><i>"
320                    "This is to certify that "
321                    "<font size='13'>"
322                    + self.context.student.display_fullname +
323                    "</font>"
324                    " has paid the full School Fees, duly registered and "
325                    "therefore, is cleared to sit for examination in the "
326                    "courses listed above."
327                    "</i></b><br/><br/>"
328                    ),
329              "Institute Director\'s Signature and Official Stamp",
330              (
331                    "<b><u>NOTE:</u></b> This form is the property of "
332                    "Kwara State Polytechnic, it is not transferable "
333                    "and students must properly fill it, get it duly "
334                    "endorsed and present it before they can be admitted "
335                    "into Examination Venues."
336                    )), ]
337            )
338
339    def render(self):
340        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
341        Code = translate('Code', 'waeup.kofa', target_language=portal_language)
342        Title = translate('Title', 'waeup.kofa',
343                          target_language=portal_language)
344        Cred = translate(
345            'Cred.', 'waeup.kofa', target_language=portal_language)
346        Score = translate('Score', 'waeup.kofa',
347                          target_language=portal_language)
348        Grade = translate('Grade', 'waeup.kofa',
349                          target_language=portal_language)
350        Signature = translate(_('HOD\'s Signature'), 'waeup.kwarapoly',
351            target_language=portal_language)
352        studentview = StudentBasePDFFormPage(self.context.student,
353            self.request, self.omit_fields)
354        students_utils = getUtility(IStudentsUtils)
355
356        tabledata = []
357        tableheader = []
358        for i in range(1, 7):
359            tabledata.append(sorted(
360                [value for value in self.context.values()
361                 if value.semester == i],
362                key=lambda value: str(value.semester) + value.code))
363            tableheader.append([(Code, 'code', 2.0),
364                               (Title, 'title', 7),
365                               (Cred, 'credits', 1.5),
366                               (Score, 'score', 1.4),
367                               (Grade, 'grade', 1.4),
368                               (Signature, 'dummy', 3),
369                               ])
370        topMargin = 1.5 + (self.label.count('\n') * 0.2)
371        return students_utils.renderPDF(
372            self, 'course_registration_slip.pdf',
373            self.context.student, studentview,
374            tableheader=tableheader,
375            tabledata=tabledata,
376            signatures=self._signatures(),
377            topMargin=topMargin,
378            omit_fields=self.omit_fields,
379            )
380
381
382class ExportPDFRegistrationSlipPage(grok.View):
383    """Deliver a PDF slip of the context.
384    """
385    grok.context(ICustomStudent)
386    grok.name('registration_slip.pdf')
387    grok.require('waeup.viewStudent')
388    prefix = 'form'
389    omit_fields = (
390        'suspended', 'phone',
391        'adm_code', 'suspended_comment', 'email',
392        'current_mode', 'matric_number', 'date_of_birth', 'current_level')
393    title = 'Clearance and Personal Data'
394    label = 'Profile Registration Slip'
395
396    form_fields = grok.AutoFields(ICustomStudent).select(
397        'date_of_birth', 'lga', 'nationality',
398        'perm_address', 'corr_address',
399        'marit_stat', 'sponsor_name', 'sponsor_address',
400        )
401
402    def render(self):
403        if self.context.state in (CREATED, ADMITTED,
404                                  CLEARANCE, REQUESTED, CLEARED):
405            self.flash('Not allowed.')
406            self.redirect(self.url(self.context))
407            return
408        studentview = StudentBasePDFFormPage(self.context.student,
409            self.request, self.omit_fields)
410        students_utils = getUtility(IStudentsUtils)
411        return students_utils.renderPDF(
412            self, 'registration_slip.pdf',
413            self.context.student, studentview, signatures=None,
414            omit_fields=self.omit_fields,
415            note=post_text_registration)
416
417post_text_registration = """<br /><br />
418<strong>IMPORTANT NOTICE</strong>
419<br /><br />
420This registration slip must be supplied before attendance of lectures. Note that:
421<br /><br />
4221. All fees due must be paid in full.
423<br /><br />
4242. All information required must be available on the Registration Form.
425<br /><br />
4263. The Signature of all appropriate Polytechnic Authority must be obtained.
427<br /><br />
4284. The endorsed Registration Form should be returned to the respective
429   Institutes and Administrative Offices.
430<br /><br />
4315. No student may change his subject or course of study as shown in the
432   signed Registration Form without clearance from the Admissions Office
433   and the Director of the appropriate Institute.
434<br /><br />
4356. All candidates admitted into the HND I programmes should submit the
436   photocopies of their original certificates of ND and SSCE with scratch
437   card online for clearance latest a week after the admission is offered.
438   Failure to comply with this directive may lead to the forfeiture of
439   the admission.
440<br /><br />
441<!-- image size: 229x100 pixels; ratio (2.29:1) must be kept -->
442<img src="${signature_img_path}" valign="-30"
443     height="75" width="172" />
444<br />
445M.O. Adebayo<br />
446Admission Officer<br />
447For: Registrar
448"""
449
450post_text_freshers = """
451<strong>INSTRUCTIONS TO FRESHERS</strong>
452<br /><br />
453You are hereby offered a provisional admission for the 2014/2015 session subject to the conditions that:
454<br /><br />
4551. The information given in your Application Form is correct.
456<br /><br />
4572. The original of your Credentials are presented for scrutiny.
458<br /><br />
4593. If at any time the Credentials submitted are found to be false/fake
460   or incorrect, your admission shall be withdrawn.
461<br /><br />
4624. The name by which you are admitted and registered shall remain same
463   throughout the duration of your programme.
464<br /><br />
4655. You are to fill and submit the Undertaking Form at the point of
466   registration failure which your admission will be forfeited.
467<br /><br />
4686. You will dress decently covering your nakedness at all times.
469<br /><br />
4707. Payment of school fees will be once and in full and should be by
471   Interswitch to the designated Banks. Failure to pay fees by the
472   mode mentioned above by the closing date means you have declined
473   the offer and your place will be given to another eligible
474   candidate immediately.
475<br /><br />
4768. You present a Certificate of medical fitness from the Polytechnic
477   Clinic.
478<br /><br />
4799. All indigenes of Kwara State are required to present Certificate of
480   Citizenship.
481<br /><br />
48210. The Polytechnic reserves the right to withdraw your admission at
483    any stage, if you are found to be a cultist or an expelled
484    individual from any tertiary institution.
485<br /><br />
48611. You are prepared to take up accommodation provided on the campuses
487    by the authority.
488<br /><br />
48912. There will be no refund of school fees once paid.
490<br /><br />
49113. If you accept this offer with the above stated conditions, please
492    make a photocopy of this document and return it to the Admission
493    Office immediately.
494<br /><br />
49514. You possess the entry requirement for the programme you are admitted.
496<br /><br />
497
498<!-- image size: 229x100 pixels; ratio (2.29:1) must be kept -->
499<img src="${signature_img_path}" valign="-30"
500     height="75" width="172" />
501<br />
502M.O. Adebayo<br />
503Admission Officer<br />
504For: Registrar
505"""
506# XXX: a note for <img /> tag used here (from reportlab docs):
507#   The valign attribute may be set to a css like value from "baseline", "sub",
508#   "super", "top", "text-top", "middle", "bottom", "text-bottom";
509#   the value may also be a numeric percentage or an absolute value.
510#
511# Burn this remark after reading.
512
513
514class StudentGetMatricNumberPage(UtilityView, grok.View):
515    """ Construct and set the matriculation number.
516    """
517    grok.context(ICustomStudent)
518    grok.name('get_matric_number')
519    grok.require('waeup.viewStudent')
520
521    def update(self):
522        students_utils = getUtility(IStudentsUtils)
523        msg, mnumber = students_utils.setMatricNumber(self.context)
524        if msg:
525            self.flash(msg, type="danger")
526        else:
527            self.flash(_('Matriculation number %s assigned.' % mnumber))
528            self.context.writeLogMessage(self, '%s assigned' % mnumber)
529        self.redirect(self.url(self.context))
530        return
531
532    def render(self):
533        return
Note: See TracBrowser for help on using the repository browser.