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

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

Adjust to changes in base package.

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