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

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

Make affidavit of good conduct compulsory.

  • Property svn:keywords set to Id
File size: 20.9 KB
Line 
1## $Id: browser.py 13308 2015-10-12 07:16:26Z 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, ExportPDFAdmissionSlip,
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    NigeriaExportPDFPaymentSlip,
40    NigeriaStudentClearanceDisplayFormPage,
41    NigeriaExportPDFClearanceSlip,
42    NigeriaStudentClearanceEditFormPage,
43    NigeriaExportPDFCourseRegistrationSlip,
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 CustomExportPDFPaymentSlip(NigeriaExportPDFPaymentSlip):
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 CustomExportPDFClearanceSlip(NigeriaExportPDFClearanceSlip):
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        store = getUtility(IExtFileStore)
191        #if not store.getFileByContext(self.context, attr=u'birth_certificate.jpg'):
192        #    return _('No birth certificate uploaded.')
193        #if not store.getFileByContext(self.context, attr=u'ref_let.jpg'):
194        #    return _('No guarantor/referee letter uploaded.')
195        #if not store.getFileByContext(self.context, attr=u'acc_let.jpg'):
196        #    return _('No acceptance letter uploaded.')
197        #if not store.getFileByContext(self.context, attr=u'fst_sit_scan.jpg'):
198        #    return _('No first sitting result uploaded.')
199        if not store.getFileByContext(self.context, attr=u'stat_dec'):
200            return _('No affidavit of good conduct uploaded.')
201        return False
202
203    @property
204    def form_fields(self):
205        if self.context.is_postgrad:
206            form_fields = grok.AutoFields(ICustomPGStudentClearance).omit(
207            'clearance_locked', 'nysc_location', 'clr_code', 'officer_comment',
208            'physical_clearance_date')
209        else:
210            form_fields = grok.AutoFields(ICustomUGStudentClearance).omit(
211            'clearance_locked', 'clr_code', 'officer_comment',
212            'physical_clearance_date')
213        return form_fields
214
215
216class BedTicketAddPage(BedTicketAddPage):
217    """ Page to add an online payment ticket
218    """
219    buttonname = _('Create bed ticket')
220    notice = ''
221    with_ac = False
222
223
224class CustomExportPDFAdmissionSlip(ExportPDFAdmissionSlip):
225    """Deliver a PDF Admission slip.
226    """
227    grok.context(ICustomStudent)
228
229    omit_fields = ('date_of_birth', 'current_level')
230
231    form_fields = grok.AutoFields(ICustomStudent).select(
232        'student_id', 'reg_number')
233
234    @property
235    def label(self):
236        line0 = 'Registration Bio Data - B'
237        line1 = 'Admission Letter of %s' % self.context.display_fullname
238        return '%s\n%s' % (line0, line1)
239
240    def render(self):
241        if self.context.state in (CREATED, ADMITTED,
242                                  CLEARANCE, REQUESTED, CLEARED):
243            self.flash('Not allowed.')
244            self.redirect(self.url(self.context))
245            return
246        students_utils = getUtility(IStudentsUtils)
247        return students_utils.renderPDFAdmissionLetter(self,
248            self.context.student, omit_fields=self.omit_fields)
249
250
251class ExportPDFAdmissionNotificationPage(UtilityView, grok.View):
252    """Deliver a PDF Admission notification slip.
253    """
254    grok.context(ICustomStudent)
255    grok.name('admission_notification.pdf')
256    grok.require('waeup.viewStudent')
257    prefix = 'form'
258    label = 'Registration Bio Data - A\nNotification of Provisional Admission'
259
260    omit_fields = ('date_of_birth', 'current_level')
261
262    form_fields = grok.AutoFields(ICustomStudent).select(
263        'student_id', 'reg_number', 'sex', 'lga')
264
265    def render(self):
266        if self.context.state not in (ADMITTED, CLEARANCE, REQUESTED, CLEARED):
267            self.flash('Not allowed.')
268            self.redirect(self.url(self.context))
269            return
270        students_utils = getUtility(IStudentsUtils)
271        pre_text = ''
272        post_text = post_text_freshers
273        return students_utils.renderPDFAdmissionLetter(self,
274            self.context.student, omit_fields=self.omit_fields,
275            pre_text=pre_text, post_text=post_text)
276
277
278# copied from waeup.aaue
279class CustomExportPDFCourseRegistrationSlip(
280    NigeriaExportPDFCourseRegistrationSlip):
281    """Deliver a PDF slip of the context.
282    """
283    grok.context(ICustomStudentStudyLevel)
284    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
285        'level_session', 'level_verdict',
286        'validated_by', 'validation_date', 'gpa', 'level')
287
288    omit_fields = ('password', 'suspended', 'suspended_comment',
289        'phone', 'adm_code', 'sex', 'email', 'date_of_birth',
290        'department', 'current_mode', 'current_level')
291
292    @property
293    def title(self):
294        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
295        return translate(_('Credits Registered'), 'waeup.kofa',
296            target_language=portal_language)
297
298    def _signatures(self):
299        """Signatures as inserted at document bottom.
300
301        As Kwarapoly requires a fancy 'certificate' there, the pre-
302        and post-fields of signatures are quite large and unusual
303        here.
304
305        This is also a workaround, as we cannot easily insert text
306        with signature fields in documents using reportlab platypus.
307
308        The signature boxes we return here contain context infos
309        (therefore this has to be a method) and return the following
310        layout:
311
312            +-----------------------------------------+
313            | (Empty pre text)                        |
314            +-------------+---------------------------+
315            |Date         | Students Signature        |
316            +-------------+---------------------------+
317            | (Empty post text)                       |
318            +=========================================+
319            |            Certification                |
320            +-------------+---------------------------+
321            |Date         | Director Signature, etc.  |
322            +-------------+---------------------------+
323            |NOTE: This form is the ...               |
324            +-----------------------------------------+
325
326
327        """
328        return (
329            [
330                ('', _('Student\'s Signature'), ''),
331                ],
332            [((
333                    "<br/>"
334                    + "&nbsp;" * 70 +
335                    "<u><b><font size='14'>Certification</font></b></u><br/>"
336                    "<br/><b><i>"
337                    "This is to certify that "
338                    "<font size='13'>"
339                    + self.context.student.display_fullname +
340                    "</font>"
341                    " has paid the full School Fees, duly registered and "
342                    "therefore, is cleared to sit for examination in the "
343                    "courses listed above."
344                    "</i></b><br/><br/>"
345                    ),
346              "Institute Director\'s Signature and Official Stamp",
347              (
348                    "<b><u>NOTE:</u></b> This form is the property of "
349                    "Kwara State Polytechnic, it is not transferable "
350                    "and students must properly fill it, get it duly "
351                    "endorsed and present it before they can be admitted "
352                    "into Examination Venues."
353                    )), ]
354            )
355
356    def render(self):
357        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
358        Code = translate('Code', 'waeup.kofa', target_language=portal_language)
359        Title = translate('Title', 'waeup.kofa',
360                          target_language=portal_language)
361        Cred = translate(
362            'Cred.', 'waeup.kofa', target_language=portal_language)
363        Score = translate('Score', 'waeup.kofa',
364                          target_language=portal_language)
365        Grade = translate('Grade', 'waeup.kofa',
366                          target_language=portal_language)
367        Signature = translate(_('HOD\'s Signature'), 'waeup.kwarapoly',
368            target_language=portal_language)
369        studentview = StudentBasePDFFormPage(self.context.student,
370            self.request, self.omit_fields)
371        students_utils = getUtility(IStudentsUtils)
372
373        tabledata = []
374        tableheader = []
375        for i in range(1, 7):
376            tabledata.append(sorted(
377                [value for value in self.context.values()
378                 if value.semester == i],
379                key=lambda value: str(value.semester) + value.code))
380            tableheader.append([(Code, 'code', 2.0),
381                               (Title, 'title', 7),
382                               (Cred, 'credits', 1.5),
383                               (Score, 'score', 1.4),
384                               (Grade, 'grade', 1.4),
385                               (Signature, 'dummy', 3),
386                               ])
387        topMargin = 1.5 + (self.label.count('\n') * 0.2)
388        return students_utils.renderPDF(
389            self, 'course_registration_slip.pdf',
390            self.context.student, studentview,
391            tableheader=tableheader,
392            tabledata=tabledata,
393            signatures=self._signatures(),
394            topMargin=topMargin,
395            omit_fields=self.omit_fields,
396            )
397
398
399class ExportPDFRegistrationSlip(grok.View):
400    """Deliver a PDF slip of the context.
401    """
402    grok.context(ICustomStudent)
403    grok.name('registration_slip.pdf')
404    grok.require('waeup.viewStudent')
405    prefix = 'form'
406    omit_fields = (
407        'suspended', 'phone',
408        'adm_code', 'suspended_comment', 'email',
409        'current_mode', 'matric_number', 'date_of_birth', 'current_level')
410    title = 'Clearance and Personal Data'
411    label = 'Registration Bio Data - C\nProfile Data Slip'
412
413    form_fields = grok.AutoFields(ICustomStudent).select(
414        'date_of_birth', 'lga', 'nationality',
415        'perm_address', 'corr_address',
416        'marit_stat', 'sponsor_name', 'sponsor_address',
417        )
418
419    def render(self):
420        if self.context.state in (CREATED, ADMITTED,
421                                  CLEARANCE, REQUESTED, CLEARED):
422            self.flash('Not allowed.')
423            self.redirect(self.url(self.context))
424            return
425        studentview = StudentBasePDFFormPage(self.context.student,
426            self.request, self.omit_fields)
427        students_utils = getUtility(IStudentsUtils)
428        return students_utils.renderPDF(
429            self, 'registration_slip.pdf',
430            self.context.student, studentview, signatures=None,
431            omit_fields=self.omit_fields,
432            note=post_text_registration)
433
434post_text_registration = """<br /><br />
435<strong>IMPORTANT NOTICE</strong>
436<br /><br />
437This registration slip must be supplied before attendance of lectures. Note that:
438<br /><br />
4391. All fees due must be paid in full.
440<br /><br />
4412. All information required must be available on the Registration Form.
442<br /><br />
4433. The Signature of all appropriate Polytechnic Authority must be obtained.
444<br /><br />
4454. The endorsed Registration Form should be returned to the respective
446   Institutes and Administrative Offices.
447<br /><br />
4485. No student may change his subject or course of study as shown in the
449   signed Registration Form without clearance from the Admissions Office
450   and the Director of the appropriate Institute.
451<br /><br />
4526. All candidates admitted into the HND I programmes should submit the
453   photocopies of their original certificates of ND and SSCE with scratch
454   card online for clearance latest a week after the admission is offered.
455   Failure to comply with this directive may lead to the forfeiture of
456   the admission.
457<br /><br />
458<!-- image size: 229x100 pixels; ratio (2.29:1) must be kept -->
459<img src="${signature_img_path}" valign="-30"
460     height="75" width="172" />
461<br />
462M.O. Adebayo<br />
463Admission Officer<br />
464For: Registrar
465"""
466
467post_text_freshers = """
468<strong>INSTRUCTIONS TO FRESHERS</strong>
469<br /><br />
470You are hereby offered a provisional admission for the current academic
471 session subject to the conditions that:
472<br /><br />
4731. The information given in your Application Form is correct.
474<br /><br />
4752. The original of your Credentials are presented for scrutiny.
476<br /><br />
4773. If at any time the Credentials submitted are found to be false/fake
478   or incorrect, your admission shall be withdrawn.
479<br /><br />
4804. The name by which you are admitted and registered shall remain same
481   throughout the duration of your programme.
482<br /><br />
4835. You are to fill and submit the Undertaking Form at the point of
484   registration failure which your admission will be forfeited.
485<br /><br />
4866. You will dress decently covering your nakedness at all times.
487<br /><br />
4887. Payment of school fees will be once and in full and should be by
489   Interswitch to the designated Banks. Failure to pay fees by the
490   mode mentioned above by the closing date means you have declined
491   the offer and your place will be given to another eligible
492   candidate immediately.
493<br /><br />
4948. You present a Certificate of medical fitness from the Polytechnic
495   Clinic.
496<br /><br />
4979. All indigenes of Kwara State are required to present Certificate of
498   Citizenship.
499<br /><br />
50010. The Polytechnic reserves the right to withdraw your admission at
501    any stage, if you are found to be a cultist or an expelled
502    individual from any tertiary institution. In addition, it should be
503    noted that the Polytechnic will not entertain any change of name
504    different from the one filled by the candidate on his/her Application
505    Form.
506<br /><br />
50711. You are prepared to take up accommodation provided on the campuses
508    by the authority.
509<br /><br />
51012. There will be no refund of school fees once paid.
511<br /><br />
51213. If you accept this offer with the above stated conditions, please
513    make a photocopy of this document and return it to the Admission
514    Office immediately.
515<br /><br />
51614. You possess the entry requirement for the programme you are admitted.
517<br /><br />
518
519<!-- image size: 229x100 pixels; ratio (2.29:1) must be kept -->
520<img src="${signature_img_path}" valign="-30"
521     height="75" width="172" />
522<br />
523M.O. Adebayo<br />
524Admission Officer<br />
525For: Registrar
526"""
527# XXX: a note for <img /> tag used here (from reportlab docs):
528#   The valign attribute may be set to a css like value from "baseline", "sub",
529#   "super", "top", "text-top", "middle", "bottom", "text-bottom";
530#   the value may also be a numeric percentage or an absolute value.
531#
532# Burn this remark after reading.
533
534
535class StudentGetMatricNumberView(UtilityView, grok.View):
536    """ Construct and set the matriculation number.
537    """
538    grok.context(ICustomStudent)
539    grok.name('get_matric_number')
540    grok.require('waeup.viewStudent')
541
542    def update(self):
543        students_utils = getUtility(IStudentsUtils)
544        msg, mnumber = students_utils.setMatricNumber(self.context)
545        if msg:
546            self.flash(msg, type="danger")
547        else:
548            self.flash(_('Matriculation number %s assigned.' % mnumber))
549            self.context.writeLogMessage(self, '%s assigned' % mnumber)
550        self.redirect(self.url(self.context))
551        return
552
553    def render(self):
554        return
Note: See TracBrowser for help on using the repository browser.