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

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

Customize ICourseTicket. Add field 'ca'.

Customize EditScoresPage?, CourseTicketManageFormPage?, CourseTicketDisplayFormPage? and editscorespage.pt accordingly.

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