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

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

Apply getMultiAdapter to collect all file registered viewlets and make upload of these files compulsory.

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