source: main/waeup.aaue/trunk/src/waeup/aaue/students/browser.py @ 13808

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

Add application_number to student base data.

  • Property svn:keywords set to Id
File size: 22.7 KB
Line 
1## $Id: browser.py 13795 2016-04-03 06:30:33Z 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.component import getUtility
21from zope.security import checkPermission
22from zope.formlib.textwidgets import BytesDisplayWidget
23from waeup.kofa.browser.layout import UtilityView
24from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget
25from waeup.kofa.interfaces import IKofaUtils
26from waeup.kofa.students.interfaces import IStudentsUtils, IStudent
27from waeup.kofa.students.workflow import PAID, REGISTERED
28from waeup.kofa.students.browser import (
29    StartClearancePage,
30    StudentBasePDFFormPage,
31    CourseTicketAddFormPage,
32    StudyLevelDisplayFormPage,
33    ExportPDFTranscriptSlip,
34    ExportPDFAdmissionSlip,
35    BedTicketAddPage,
36    StudentFilesUploadPage,
37    PaymentsManageFormPage,
38    CourseTicketDisplayFormPage,
39    CourseTicketManageFormPage,
40    EditScoresPage
41    )
42from kofacustom.nigeria.students.browser import (
43    NigeriaOnlinePaymentDisplayFormPage,
44    NigeriaOnlinePaymentAddFormPage,
45    NigeriaExportPDFPaymentSlip,
46    NigeriaExportPDFCourseRegistrationSlip,
47    NigeriaExportPDFClearanceSlip,
48    NigeriaStudentPersonalDisplayFormPage,
49    NigeriaStudentPersonalEditFormPage,
50    NigeriaStudentPersonalManageFormPage,
51    NigeriaStudentClearanceEditFormPage,
52    NigeriaAccommodationManageFormPage,
53    NigeriaStudentBaseDisplayFormPage,
54    NigeriaStudentBaseManageFormPage
55    )
56from waeup.aaue.students.interfaces import (
57    ICustomStudentOnlinePayment,
58    ICustomStudentStudyLevel,
59    ICustomStudent,
60    ICustomStudentPersonal,
61    ICustomStudentPersonalEdit,
62    ICustomUGStudentClearance,
63    ICustomCourseTicket,
64    ICustomStudentBase)
65from waeup.aaue.interswitch.browser import gateway_net_amt
66from waeup.aaue.interfaces import MessageFactory as _
67
68class CustomStudentBaseDisplayFormPage(NigeriaStudentBaseDisplayFormPage):
69    """ Page to display student base data
70    """
71    form_fields = grok.AutoFields(ICustomStudentBase).omit(
72        'password', 'suspended', 'suspended_comment', 'flash_notice')
73    form_fields[
74        'financial_clearance_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
75
76class CustomStudentBaseManageFormPage(NigeriaStudentBaseManageFormPage):
77    """ View to manage student base data
78    """
79    form_fields = grok.AutoFields(ICustomStudentBase).omit(
80        'student_id', 'adm_code', 'suspended',
81        'financially_cleared_by', 'financial_clearance_date')
82
83class CustomStudentPersonalDisplayFormPage(NigeriaStudentPersonalDisplayFormPage):
84    """ Page to display student personal data
85    """
86    form_fields = grok.AutoFields(ICustomStudentPersonal)
87    form_fields['perm_address'].custom_widget = BytesDisplayWidget
88    form_fields['father_address'].custom_widget = BytesDisplayWidget
89    form_fields['mother_address'].custom_widget = BytesDisplayWidget
90    form_fields['next_kin_address'].custom_widget = BytesDisplayWidget
91    form_fields[
92        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
93
94class CustomStudentPersonalEditFormPage(NigeriaStudentPersonalEditFormPage):
95    """ Page to edit personal data
96    """
97    form_fields = grok.AutoFields(ICustomStudentPersonalEdit).omit('personal_updated')
98
99class CustomStudentPersonalManageFormPage(NigeriaStudentPersonalManageFormPage):
100    """ Page to edit personal data
101    """
102    form_fields = grok.AutoFields(ICustomStudentPersonal)
103    form_fields['personal_updated'].for_display = True
104    form_fields[
105        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
106
107class CustomStudentClearanceEditFormPage(NigeriaStudentClearanceEditFormPage):
108    """ View to edit student clearance data by student
109    """
110
111    @property
112    def form_fields(self):
113        if self.context.is_postgrad:
114            form_fields = grok.AutoFields(ICustomPGStudentClearance).omit(
115            'clearance_locked', 'nysc_location', 'clr_code', 'officer_comment',
116            'physical_clearance_date')
117        else:
118            form_fields = grok.AutoFields(ICustomUGStudentClearance).omit(
119            'clearance_locked', 'clr_code', 'officer_comment',
120            'physical_clearance_date')
121        return form_fields
122
123class CustomStartClearancePage(StartClearancePage):
124    with_ac = False
125
126    @property
127    def all_required_fields_filled(self):
128        if not self.context.email:
129            return _("Email address is missing."), 'edit_base'
130        if not self.context.phone:
131            return _("Phone number is missing."), 'edit_base'
132        if not self.context.father_name:
133            return _("Personal data form is not properly filled."), 'edit_personal'
134        return
135
136class CustomOnlinePaymentDisplayFormPage(NigeriaOnlinePaymentDisplayFormPage):
137    """ Page to view an online payment ticket
138    """
139    grok.context(ICustomStudentOnlinePayment)
140    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
141        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
142    form_fields[
143        'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
144    form_fields[
145        'payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
146
147class CustomOnlinePaymentAddFormPage(NigeriaOnlinePaymentAddFormPage):
148    """ Page to add an online payment ticket
149    """
150    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).select(
151        'p_category')
152
153class CustomPaymentsManageFormPage(PaymentsManageFormPage):
154    """ Page to manage the student payments.
155
156    This manage form page is for both students and students officers.
157    """
158    @property
159    def manage_payments_allowed(self):
160        return checkPermission('waeup.manageStudent', self.context)
161
162class CustomExportPDFPaymentSlip(NigeriaExportPDFPaymentSlip):
163    """Deliver a PDF slip of the context.
164    """
165    grok.context(ICustomStudentOnlinePayment)
166    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
167        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
168    form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
169    form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
170
171    @property
172    def note(self):
173        p_session = self.context.p_session
174        try:
175            academic_session = grok.getSite()['configuration'][str(p_session)]
176        except KeyError:
177            academic_session = None
178        text =  '\n\n The Amount Authorized is inclusive of: '
179        if self.context.p_category in ('schoolfee_incl', 'schoolfee_1') \
180            and academic_session:
181            welfare_fee = gateway_net_amt(academic_session.welfare_fee)
182            union_fee = gateway_net_amt(academic_session.union_fee)
183            text += ('School Fee, '
184                     '%s Naira Student Union Dues, '
185                     '%s Naira Student Welfare Assurance Fee and '
186                     % (union_fee, welfare_fee))
187        elif self.context.p_category in (
188            'clearance_incl', 'clearance_medical_incl') and academic_session:
189            matric_gown_fee = gateway_net_amt(academic_session.matric_gown_fee)
190            lapel_fee = gateway_net_amt(academic_session.lapel_fee)
191            text += ('Acceptance Fee, '
192                     '%s Naira Matriculation Gown Fee, '
193                     '%s Naira Lapel/File Fee and '
194                     % (matric_gown_fee, lapel_fee))
195        return text + '250.0 Naira Transaction Charge.'
196
197class CustomStudyLevelDisplayFormPage(StudyLevelDisplayFormPage):
198    """ Page to display student study levels
199    """
200    grok.context(ICustomStudentStudyLevel)
201    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
202        'total_credits', 'gpa', 'level')
203    form_fields[
204        'validation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
205
206class CustomExportPDFCourseRegistrationSlip(
207    NigeriaExportPDFCourseRegistrationSlip):
208    """Deliver a PDF slip of the context.
209    """
210    grok.context(ICustomStudentStudyLevel)
211    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
212        'level_session', 'level_verdict',
213        'validated_by', 'validation_date', 'gpa', 'level')
214
215    omit_fields = ('password', 'suspended', 'suspended_comment',
216        'phone', 'adm_code', 'sex', 'email', 'date_of_birth',
217        'department', 'current_mode', 'current_level', 'flash_notice')
218
219    def update(self):
220        if self.context.student.state != REGISTERED \
221            and self.context.student.current_level == self.context.level:
222            self.flash(_('Forbidden'), type="warning")
223            self.redirect(self.url(self.context))
224
225    @property
226    def label(self):
227        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
228        lang = self.request.cookies.get('kofa.language', portal_language)
229        level_title = translate(self.context.level_title, 'waeup.kofa',
230            target_language=lang)
231        line0 = ''
232        if self.context.student.is_postgrad:
233            line0 = 'SCHOOL OF POSTGRADUATE STUDIES\n'
234        elif self.context.student.current_mode.endswith('_pt'):
235            line0 = 'DIRECTORATE OF PART-TIME DEGREE PROGRAMMES\n'
236        line1 = translate(_('Course Registration Slip'),
237            'waeup.kofa', target_language=portal_language) \
238            + ' %s' % level_title
239        line2 = translate(_('Session'),
240            'waeup.kofa', target_language=portal_language) \
241            + ' %s' % self.context.getSessionString
242        return '%s%s\n%s' % (line0, line1, line2)
243
244    @property
245    def title(self):
246        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
247        return translate(_('Units Registered'), 'waeup.kofa',
248            target_language=portal_language)
249
250    def _signatures(self):
251        if self.context.student.current_mode.endswith('_pt') \
252            or self.context.student.current_mode == 'found':
253            return (
254                [('I have selected the course on the advise of my Head of '
255                 'Department. <br>', _('Student\'s Signature'), '<br>')],
256                [('This student has satisfied the department\'s requirements. '
257                 'I recommend to approve the course registration. <br>',
258                 _('Head of Department\'s Signature'), '<br>')],
259                [('' , _('Principal Assistant Registrar\'s Signature'), '<br>')],
260                [('', _('Director\'s Signature'))]
261                )
262        if self.context.student.current_mode in (
263            'de_ft', 'ug_ft', 'dp_ft', 'transfer'):
264            return ([_('Academic Adviser\'s Signature'),
265                _('Faculty Officer\'s Signature'),
266                _('Student\'s Signature')],)
267
268        if self.context.student.current_mode in ('special_pg_ft', 'special_pg_pt'):
269            return (
270                [('I declare that all items of information supplied above are correct:' ,
271                    _('Student\'s Signature'), '<br>')],
272                [('We approved the above registration:',
273                    _('Major Supervisor (Name / Signature)'), '')],
274                [('', _('Co-Supervisor (Name / Signature)'), '')],
275                [('', _('Head of Department'), '<br>')],
276                [('The student has satisfied the conditions for renewal of '
277                  'registration for graduate school programme in this university:',
278                  _('Secretary <br /> (School of Postgraduate Studies)'), '')],
279                [('', _('Dean <br /> (School of Postgraduate Studies)'), '')],
280                )
281        return None
282
283
284    def render(self):
285        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
286        Sem = translate('Sem.', 'waeup.kofa', target_language=portal_language)
287        Code = translate('Code', 'waeup.kofa', target_language=portal_language)
288        Title = translate('Title', 'waeup.kofa', target_language=portal_language)
289        Cred = translate('Cred.', 'waeup.kofa', target_language=portal_language)
290        Score = translate('Score', 'waeup.kofa', target_language=portal_language)
291        Grade = translate('Grade', 'waeup.kofa', target_language=portal_language)
292        Signature = translate(_('Lecturer\'s Signature'), 'waeup.aaue',
293            target_language=portal_language)
294        studentview = StudentBasePDFFormPage(self.context.student,
295            self.request, self.omit_fields)
296        students_utils = getUtility(IStudentsUtils)
297
298        tabledata = []
299        tableheader = []
300        contenttitle = []
301        for i in range(1,7):
302            tabledata.append(sorted(
303                [value for value in self.context.values() if value.semester == i],
304                key=lambda value: str(value.semester) + value.code))
305            tableheader.append([(Code,'code', 2.0),
306                               (Title,'title', 7),
307                               (Cred, 'credits', 1.5),
308                               (Score, 'score', 1.4),
309                               (Grade, 'grade', 1.4),
310                               (Signature, 'dummy', 3),
311                               ])
312        if len(self.label.split('\n')) == 3:
313            topMargin = 1.9
314        elif len(self.label.split('\n')) == 2:
315            topMargin = 1.7
316        else:
317            topMargin = 1.5
318        return students_utils.renderPDF(
319            self, 'course_registration_slip.pdf',
320            self.context.student, studentview,
321            tableheader=tableheader,
322            tabledata=tabledata,
323            signatures=self._signatures(),
324            topMargin=topMargin,
325            omit_fields=self.omit_fields
326            )
327
328class CustomExportPDFTranscriptSlip(ExportPDFTranscriptSlip):
329    """Deliver a PDF slip of the context.
330    """
331
332    def _sigsInFooter(self):
333        return []
334
335    def _signatures(self):
336        return ([(
337            'Akhimien Felicia O. (MANUPA) <br /> Principal Assistant Registrar  <br /> '
338            'Exams, Records and Data Processing Division <br /> For: Registrar')],)
339
340class CustomExportPDFAdmissionSlip(ExportPDFAdmissionSlip):
341    """Deliver a PDF Admission slip.
342    """
343
344    @property
345    def label(self):
346        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
347        return translate(_('e-Admission Slip \n'),
348            'waeup.kofa', target_language=portal_language) \
349            + ' %s' % self.context.display_fullname
350
351class CustomExportPDFClearanceSlip(NigeriaExportPDFClearanceSlip):
352    """Deliver a PDF slip of the context.
353    """
354
355    @property
356    def label(self):
357        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
358        return translate(_('Clearance Slip\n'),
359            'waeup.kofa', target_language=portal_language) \
360            + ' %s' % self.context.display_fullname
361
362class StudentGetMatricNumberPage(UtilityView, grok.View):
363    """ Construct and set the matriculation number.
364    """
365    grok.context(IStudent)
366    grok.name('get_matric_number')
367    grok.require('waeup.viewStudent')
368
369    def update(self):
370        students_utils = getUtility(IStudentsUtils)
371        msg, mnumber = students_utils.setMatricNumber(self.context)
372        if msg:
373            self.flash(msg, type="danger")
374        else:
375            self.flash(_('Matriculation number %s assigned.' % mnumber))
376            self.context.writeLogMessage(self, '%s assigned' % mnumber)
377        self.redirect(self.url(self.context))
378        return
379
380    def render(self):
381        return
382
383class ExportPDFMatricNumberSlip(UtilityView, grok.View):
384    """Deliver a PDF notification slip.
385    """
386    grok.context(ICustomStudent)
387    grok.name('matric_number_slip.pdf')
388    grok.require('waeup.viewStudent')
389    prefix = 'form'
390
391    form_fields = grok.AutoFields(ICustomStudent).select(
392        'student_id', 'matric_number')
393    omit_fields = ('date_of_birth', 'current_level', 'flash_notice')
394
395    @property
396    def title(self):
397        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
398        return translate(_('Matriculation Number'), 'waeup.kofa',
399            target_language=portal_language)
400
401    @property
402    def label(self):
403        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
404        return translate(_('Matriculation Number Slip\n'),
405            'waeup.kofa', target_language=portal_language) \
406            + ' %s' % self.context.display_fullname
407
408    def render(self):
409        if self.context.state not in (PAID,) or not self.context.is_fresh \
410            or not self.context.matric_number:
411            self.flash('Not allowed.', type="danger")
412            self.redirect(self.url(self.context))
413            return
414        students_utils = getUtility(IStudentsUtils)
415        pre_text = _('Congratulations! Your acceptance fee and school fees ' +
416                     'payments have been received and your matriculation ' +
417                     'number generated with details as follows.')
418        return students_utils.renderPDFAdmissionLetter(self,
419            self.context.student, omit_fields=self.omit_fields,
420            pre_text=pre_text, post_text='')
421
422class ExportPersonalDataSlip(UtilityView, grok.View):
423    """Deliver a PDF notification slip.
424    """
425    grok.context(ICustomStudent)
426    grok.name('personal_data_slip.pdf')
427    grok.require('waeup.viewStudent')
428    prefix = 'form'
429    note = None
430
431    form_fields = grok.AutoFields(ICustomStudentPersonal).omit('personal_updated')
432    omit_fields = ('suspended', 'suspended_comment', 'adm_code',
433                   'certificate', 'flash_notice')
434
435    @property
436    def title(self):
437        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
438        return translate(_('Personal Data'), 'waeup.kofa',
439            target_language=portal_language)
440
441    @property
442    def label(self):
443        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
444        return translate(_('Personal Data Slip\n'),
445            'waeup.kofa', target_language=portal_language) \
446            + ' %s' % self.context.display_fullname
447
448    def render(self):
449        studentview = StudentBasePDFFormPage(self.context.student,
450            self.request, self.omit_fields)
451        students_utils = getUtility(IStudentsUtils)
452        return students_utils.renderPDF(self, 'personal_data_slip.pdf',
453            self.context.student, studentview, note=self.note,
454            omit_fields=self.omit_fields)
455
456class CustomAccommodationManageFormPage(NigeriaAccommodationManageFormPage):
457    """ Page to manage bed tickets.
458    This manage form page is for both students and students officers.
459    """
460    with_hostel_selection = True
461
462class CustomBedTicketAddPage(BedTicketAddPage):
463    with_ac = False
464
465class CustomStudentFilesUploadPage(StudentFilesUploadPage):
466    """ View to upload files by student. Inherit from same class in
467    base package, not from kofacustom.nigeria which
468    requires that no application slip exists.
469    """
470
471class CustomCourseTicketDisplayFormPage(CourseTicketDisplayFormPage):
472    """ Page to display course tickets
473    """
474    form_fields = grok.AutoFields(ICustomCourseTicket)
475
476class CustomCourseTicketManageFormPage(CourseTicketManageFormPage):
477    """ Page to manage course tickets
478    """
479    form_fields = grok.AutoFields(ICustomCourseTicket)
480    form_fields['title'].for_display = True
481    form_fields['fcode'].for_display = True
482    form_fields['dcode'].for_display = True
483    form_fields['semester'].for_display = True
484    form_fields['passmark'].for_display = True
485    form_fields['credits'].for_display = True
486    form_fields['mandatory'].for_display = False
487    form_fields['automatic'].for_display = True
488    form_fields['carry_over'].for_display = True
489
490class CustomEditScoresPage(EditScoresPage):
491    """Page that filters and lists students.
492    """
493    grok.template('editscorespage')
494
495    def update(self,  *args, **kw):
496        form = self.request.form
497        ob_class = self.__implemented__.__name__.replace('waeup.kofa.','')
498        self.current_academic_session = grok.getSite()[
499            'configuration'].current_academic_session
500        if self.context.__parent__.__parent__.score_editing_disabled:
501            self.flash(_('Score editing disabled.'), type="warning")
502            self.redirect(self.url(self.context))
503            return
504        if not self.current_academic_session:
505            self.flash(_('Current academic session not set.'), type="warning")
506            self.redirect(self.url(self.context))
507            return
508        self.tickets = self._searchCatalog(self.current_academic_session)
509        editable_tickets = [
510            ticket for ticket in self.tickets if ticket.editable_by_lecturer]
511        if not self.tickets:
512            self.flash(_('No student found.'), type="warning")
513            self.redirect(self.url(self.context))
514            return
515        if 'UPDATE' in form:
516            tno = 0
517            error = ''
518            if not editable_tickets:
519                return
520            scores = form['scores']
521            cas = form['cas']
522            if isinstance(scores, basestring):
523                scores = [scores]
524            if isinstance(cas, basestring):
525                cas = [cas]
526            for ticket in editable_tickets:
527                ticket_error = False
528                score = ticket.score
529                ca = ticket.ca
530                if scores[tno] == '':
531                    score = None
532                if cas[tno] == '':
533                    ca = None
534                try:
535                    if scores[tno]:
536                        score = int(scores[tno])
537                    if cas[tno]:
538                        ca = int(cas[tno])
539                except ValueError:
540                    error += '%s, ' % ticket.student.display_fullname
541                    ticket_error = True
542                if not ticket_error and ticket.score != score:
543                    ticket.score = score
544                    ticket.student.__parent__.logger.info(
545                        '%s - %s %s/%s score updated (%s)' %
546                        (ob_class, ticket.student.student_id,
547                         ticket.level, ticket.code, score))
548                if not ticket_error and ticket.ca != ca:
549                    ticket.ca = ca
550                    ticket.student.__parent__.logger.info(
551                        '%s - %s %s/%s ca updated (%s)' %
552                        (ob_class, ticket.student.student_id,
553                         ticket.level, ticket.code, ca))
554                tno += 1
555            if error:
556                self.flash(_('Error: Score(s) and CA(s) of %s have not be updated. '
557                  'Only integers are allowed.' % error.strip(', ')),
558                  type="danger")
559        return
Note: See TracBrowser for help on using the repository browser.