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

Last change on this file since 14238 was 14236, checked in by Henrik Bettermann, 8 years ago

Add student id card fee to acceptance fee plus.

  • Property svn:keywords set to Id
File size: 31.5 KB
Line 
1## $Id: browser.py 14236 2016-10-27 15:34:12Z 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
19import csv
20import textwrap
21from cStringIO import StringIO
22from zope.i18n import translate
23from zope.component import getUtility, queryUtility
24from zope.schema.interfaces import TooBig, TooSmall
25from zope.security import checkPermission
26from zope.catalog.interfaces import ICatalog
27from zope.formlib.textwidgets import BytesDisplayWidget
28from waeup.kofa.browser.layout import UtilityView
29from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget
30from waeup.kofa.interfaces import IKofaUtils, academic_sessions_vocab
31from waeup.kofa.students.interfaces import IStudentsUtils, IStudent
32from waeup.kofa.students.workflow import PAID, REGISTERED, RETURNING
33from waeup.kofa.students.studylevel import getGradeWeightFromScore
34from waeup.kofa.students.browser import (
35    StartClearancePage,
36    StudentBasePDFFormPage,
37    CourseTicketAddFormPage,
38    StudyLevelDisplayFormPage,
39    StudyLevelManageFormPage,
40    StudyLevelEditFormPage,
41    ExportPDFTranscriptSlip,
42    ExportPDFAdmissionSlip,
43    BedTicketAddPage,
44    StudentFilesUploadPage,
45    PaymentsManageFormPage,
46    CourseTicketDisplayFormPage,
47    CourseTicketManageFormPage,
48    EditScoresPage,
49    ExportPDFScoresSlip,
50    StudyCourseTranscriptPage,
51    )
52from kofacustom.nigeria.students.browser import (
53    NigeriaOnlinePaymentDisplayFormPage,
54    NigeriaOnlinePaymentAddFormPage,
55    NigeriaExportPDFPaymentSlip,
56    NigeriaExportPDFCourseRegistrationSlip,
57    NigeriaStudentPersonalDisplayFormPage,
58    NigeriaStudentPersonalEditFormPage,
59    NigeriaStudentPersonalManageFormPage,
60    NigeriaStudentClearanceDisplayFormPage,
61    NigeriaExportPDFClearanceSlip,
62    NigeriaStudentClearanceManageFormPage,
63    NigeriaStudentClearanceEditFormPage,
64    NigeriaAccommodationManageFormPage,
65    NigeriaStudentBaseDisplayFormPage,
66    NigeriaStudentBaseManageFormPage
67    )
68from waeup.aaue.students.interfaces import (
69    ICustomStudentOnlinePayment,
70    ICustomStudentStudyLevel,
71    ICustomStudent,
72    ICustomStudentPersonal,
73    ICustomStudentPersonalEdit,
74    ICustomUGStudentClearance,
75    ICustomPGStudentClearance,
76    ICustomCourseTicket,
77    ICustomStudentBase)
78from waeup.aaue.interswitch.browser import gateway_net_amt
79from waeup.aaue.interfaces import MessageFactory as _
80
81class CustomStudentBaseDisplayFormPage(NigeriaStudentBaseDisplayFormPage):
82    """ Page to display student base data
83    """
84    form_fields = grok.AutoFields(ICustomStudentBase).omit(
85        'password', 'suspended', 'suspended_comment', 'flash_notice')
86    form_fields[
87        'financial_clearance_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
88
89class CustomStudentBaseManageFormPage(NigeriaStudentBaseManageFormPage):
90    """ View to manage student base data
91    """
92    form_fields = grok.AutoFields(ICustomStudentBase).omit(
93        'student_id', 'adm_code', 'suspended',
94        'financially_cleared_by', 'financial_clearance_date')
95
96class CustomStudentPersonalDisplayFormPage(NigeriaStudentPersonalDisplayFormPage):
97    """ Page to display student personal data
98    """
99    form_fields = grok.AutoFields(ICustomStudentPersonal)
100    form_fields['perm_address'].custom_widget = BytesDisplayWidget
101    form_fields['father_address'].custom_widget = BytesDisplayWidget
102    form_fields['mother_address'].custom_widget = BytesDisplayWidget
103    form_fields['next_kin_address'].custom_widget = BytesDisplayWidget
104    form_fields[
105        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
106
107class CustomStudentPersonalEditFormPage(NigeriaStudentPersonalEditFormPage):
108    """ Page to edit personal data
109    """
110    form_fields = grok.AutoFields(ICustomStudentPersonalEdit).omit('personal_updated')
111
112class CustomStudentPersonalManageFormPage(NigeriaStudentPersonalManageFormPage):
113    """ Page to edit personal data
114    """
115    form_fields = grok.AutoFields(ICustomStudentPersonal)
116    form_fields['personal_updated'].for_display = True
117    form_fields[
118        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
119
120class CustomStudentClearanceDisplayFormPage(NigeriaStudentClearanceDisplayFormPage):
121    """ Page to display student clearance data
122    """
123
124    @property
125    def form_fields(self):
126        if self.context.is_postgrad:
127            form_fields = grok.AutoFields(
128                ICustomPGStudentClearance).omit('clearance_locked')
129        else:
130            form_fields = grok.AutoFields(
131                ICustomUGStudentClearance).omit('clearance_locked')
132        if not getattr(self.context, 'officer_comment'):
133            form_fields = form_fields.omit('officer_comment')
134        else:
135            form_fields['officer_comment'].custom_widget = BytesDisplayWidget
136        form_fields = form_fields.omit('def_adm')
137        return form_fields
138
139class CustomStudentClearanceManageFormPage(NigeriaStudentClearanceManageFormPage):
140    """ Page to edit student clearance data
141    """
142
143    @property
144    def form_fields(self):
145        if self.context.is_postgrad:
146            form_fields = grok.AutoFields(
147                ICustomPGStudentClearance).omit('clr_code')
148        else:
149            form_fields = grok.AutoFields(
150                ICustomUGStudentClearance).omit('clr_code')
151        form_fields = form_fields.omit('def_adm')
152        return form_fields
153
154class CustomStudentClearanceEditFormPage(NigeriaStudentClearanceEditFormPage):
155    """ View to edit student clearance data by student
156    """
157
158    @property
159    def form_fields(self):
160        if self.context.is_postgrad:
161            form_fields = grok.AutoFields(ICustomPGStudentClearance).omit(
162            'clearance_locked', 'nysc_location', 'clr_code', 'officer_comment',
163            'physical_clearance_date')
164        else:
165            form_fields = grok.AutoFields(ICustomUGStudentClearance).omit(
166            'clearance_locked', 'clr_code', 'officer_comment',
167            'physical_clearance_date', 'date_of_birth', 'nationality', 'lga')
168        form_fields = form_fields.omit('def_adm')
169        return form_fields
170
171class CustomStartClearancePage(StartClearancePage):
172    with_ac = False
173
174    @property
175    def all_required_fields_filled(self):
176        if not self.context.email:
177            return _("Email address is missing."), 'edit_base'
178        if not self.context.phone:
179            return _("Phone number is missing."), 'edit_base'
180        if not self.context.father_name:
181            return _("Personal data form is not properly filled."), 'edit_personal'
182        return
183
184class CustomOnlinePaymentDisplayFormPage(NigeriaOnlinePaymentDisplayFormPage):
185    """ Page to view an online payment ticket
186    """
187    grok.context(ICustomStudentOnlinePayment)
188    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
189        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
190    form_fields[
191        'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
192    form_fields[
193        'payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
194
195class CustomOnlinePaymentAddFormPage(NigeriaOnlinePaymentAddFormPage):
196    """ Page to add an online payment ticket
197    """
198    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).select(
199        'p_category')
200
201class CustomPaymentsManageFormPage(PaymentsManageFormPage):
202    """ Page to manage the student payments.
203
204    This manage form page is for both students and students officers.
205    """
206    @property
207    def manage_payments_allowed(self):
208        return checkPermission('waeup.manageStudent', self.context)
209
210class CustomExportPDFPaymentSlip(NigeriaExportPDFPaymentSlip):
211    """Deliver a PDF slip of the context.
212    """
213    grok.context(ICustomStudentOnlinePayment)
214    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
215        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
216    form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
217    form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
218
219    @property
220    def note(self):
221        p_session = self.context.p_session
222        try:
223            academic_session = grok.getSite()['configuration'][str(p_session)]
224        except KeyError:
225            academic_session = None
226        text =  '\n\n The Amount Authorized is inclusive of: '
227        if self.context.p_category in ('schoolfee_incl', 'schoolfee_1') \
228            and academic_session:
229            welfare_fee = gateway_net_amt(academic_session.welfare_fee)
230            union_fee = gateway_net_amt(academic_session.union_fee)
231            text += ('School Fee, '
232                     '%s Naira Student Union Dues, '
233                     '%s Naira Student Welfare Assurance Fee and '
234                     % (union_fee, welfare_fee))
235        elif self.context.p_category in (
236            'clearance_incl', 'clearance_medical_incl') and academic_session:
237            matric_gown_fee = gateway_net_amt(academic_session.matric_gown_fee)
238            lapel_fee = gateway_net_amt(academic_session.lapel_fee)
239            id_card_fee = gateway_net_amt(academic_session.id_card_fee)
240            text += ('Acceptance Fee, '
241                     '%s Naira Matriculation Gown Fee, '
242                     '%s Naira Student ID Card Fee, '
243                     '%s Naira Lapel/File Fee and '
244                     % (matric_gown_fee, id_card_fee, lapel_fee))
245        return text + '250.0 Naira Transaction Charge.'
246
247class CustomStudyLevelDisplayFormPage(StudyLevelDisplayFormPage):
248    """ Page to display student study levels
249    """
250    grok.context(ICustomStudentStudyLevel)
251    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
252        'total_credits', 'gpa', 'level', 'imported_gpa', 'imported_cgpa')
253    form_fields[
254        'validation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
255
256    @property
257    def show_results(self):
258        isStudent = getattr(
259            self.request.principal, 'user_type', None) == 'student'
260        if isStudent and self.context.student.state != RETURNING \
261            and self.context.student.current_level == self.context.level:
262            return False
263        return True
264
265class CustomStudyLevelManageFormPage(StudyLevelManageFormPage):
266    """ Page to edit the student study level data
267    """
268    grok.context(ICustomStudentStudyLevel)
269
270class CustomStudyLevelEditFormPage(StudyLevelEditFormPage):
271    """ Page to edit the student study level data by students
272    """
273    grok.context(ICustomStudentStudyLevel)
274
275class CustomExportPDFCourseRegistrationSlip(
276    NigeriaExportPDFCourseRegistrationSlip):
277    """Deliver a PDF slip of the context.
278    """
279    grok.context(ICustomStudentStudyLevel)
280    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
281        'level_session', 'level_verdict',
282        'validated_by', 'validation_date', 'gpa', 'level',
283        'imported_gpa', 'imported_cgpa')
284
285    omit_fields = ('password', 'suspended', 'suspended_comment',
286        'phone', 'adm_code', 'sex', 'email', 'date_of_birth',
287        'department', 'current_mode', 'current_level', 'flash_notice')
288
289    @property
290    def show_results(self):
291        isStudent = getattr(
292            self.request.principal, 'user_type', None) == 'student'
293        return not isStudent \
294            or self.context.student.current_level != self.context.level \
295            or self.context.student.state == RETURNING
296
297    def update(self):
298        if self.context.student.state != REGISTERED \
299            and self.context.student.current_level == self.context.level:
300            self.flash(_('Forbidden'), type="warning")
301            self.redirect(self.url(self.context))
302            return
303
304    @property
305    def label(self):
306        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
307        lang = self.request.cookies.get('kofa.language', portal_language)
308        level_title = translate(self.context.level_title, 'waeup.kofa',
309            target_language=lang)
310        line0 = ''
311        if self.context.student.is_postgrad:
312            line0 = 'SCHOOL OF POSTGRADUATE STUDIES\n'
313        elif self.context.student.current_mode.endswith('_pt'):
314            line0 = 'DIRECTORATE OF PART-TIME DEGREE PROGRAMMES\n'
315        line1 = translate(_('Course Registration Slip'),
316            target_language=portal_language) \
317            + ' %s' % level_title
318        line2 = translate(_('Session'),
319            target_language=portal_language) \
320            + ' %s' % self.context.getSessionString
321        return '%s%s\n%s' % (line0, line1, line2)
322
323    @property
324    def title(self):
325        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
326        return translate(_('Units Registered'), target_language=portal_language)
327
328    def _signatures(self):
329        if self.context.student.current_mode.endswith('_pt') \
330            or self.context.student.current_mode == 'found':
331            return (
332                [('I have selected the course on the advise of my Head of '
333                 'Department. <br>', _('Student\'s Signature'), '<br>')],
334                [('This student has satisfied the department\'s requirements. '
335                 'I recommend to approve the course registration. <br>',
336                 _('Head of Department\'s Signature'), '<br>')],
337                [('' , _('Deputy Registrar\'s Signature'), '<br>')],
338                [('', _('Director\'s Signature'))]
339                )
340        if self.context.student.current_mode in (
341            'de_ft', 'ug_ft', 'dp_ft', 'transfer'):
342            return ([_('Academic Adviser\'s Signature'),
343                _('Faculty Officer\'s Signature'),
344                _('Student\'s Signature')],)
345
346        if self.context.student.current_mode in ('special_pg_ft', 'special_pg_pt'):
347            return (
348                [('I declare that all items of information supplied above are correct:' ,
349                    _('Student\'s Signature'), '<br>')],
350                [('We approved the above registration:',
351                    _('Major Supervisor (Name / Signature)'), '')],
352                [('', _('Co-Supervisor (Name / Signature)'), '')],
353                [('', _('Head of Department'), '<br>')],
354                [('The student has satisfied the conditions for renewal of '
355                  'registration for graduate school programme in this university:',
356                  _('Secretary <br /> (School of Postgraduate Studies)'), '')],
357                [('', _('Dean <br /> (School of Postgraduate Studies)'), '')],
358                )
359        return None
360
361
362    def render(self):
363        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
364        Sem = translate(_('Sem.'), target_language=portal_language)
365        Code = translate(_('Code'), target_language=portal_language)
366        Title = translate(_('Title'), target_language=portal_language)
367        Cred = translate(_('Cred.'), target_language=portal_language)
368        if self.show_results:
369            Score = translate(_('Score'), target_language=portal_language)
370            #CA = translate(_('CA'), target_language=portal_language)
371            Grade = translate(_('Grade'), target_language=portal_language)
372        Signature = translate(_('Lecturer\'s Signature'), 'waeup.aaue',
373            target_language=portal_language)
374        studentview = StudentBasePDFFormPage(self.context.student,
375            self.request, self.omit_fields)
376        students_utils = getUtility(IStudentsUtils)
377
378        tabledata = []
379        tableheader = []
380        contenttitle = []
381        for i in range(1,7):
382            tabledata.append(sorted(
383                [value for value in self.context.values() if value.semester == i],
384                key=lambda value: str(value.semester) + value.code))
385            if self.show_results:
386                tableheader.append([(Code,'code', 2.0),
387                                   (Title,'title', 7),
388                                   (Cred, 'credits', 1.5),
389                                   (Score, 'score', 1.4),
390                                   #(CA, 'ca', 1.4),
391                                   (Grade, 'grade', 1.4),
392                                   (Signature, 'dummy', 3),
393                                   ])
394            else:
395                tableheader.append([(Code,'code', 2.0),
396                                   (Title,'title', 7),
397                                   (Cred, 'credits', 1.5),
398                                   (Signature, 'dummy', 3),
399                                   ])
400        if len(self.label.split('\n')) == 3:
401            topMargin = 1.9
402        elif len(self.label.split('\n')) == 2:
403            topMargin = 1.7
404        else:
405            topMargin = 1.5
406        return students_utils.renderPDF(
407            self, 'course_registration_slip.pdf',
408            self.context.student, studentview,
409            tableheader=tableheader,
410            tabledata=tabledata,
411            signatures=self._signatures(),
412            topMargin=topMargin,
413            omit_fields=self.omit_fields
414            )
415
416class CustomStudyCourseTranscriptPage(StudyCourseTranscriptPage):
417    """ Page to display the student's transcript.
418    """
419    grok.require('waeup.viewStudent')
420
421class CustomExportPDFTranscriptSlip(ExportPDFTranscriptSlip):
422    """Deliver a PDF slip of the context.
423    """
424    grok.require('waeup.viewStudent')
425
426    def _sigsInFooter(self):
427        return []
428
429    def _signatures(self):
430        return ([(
431            'Mrs. Uniamikogbo, S.O., mnim, manupa <br /> Prin. Asst Registrar  <br /> '
432            'Exams, Records and Data Processing Division <br /> For: Registrar')],)
433
434    def render(self):
435        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
436        Term = translate(_('Sem.'), target_language=portal_language)
437        Code = translate(_('Code'), target_language=portal_language)
438        Title = translate(_('Title'), target_language=portal_language)
439        Cred = translate(_('Credits'), target_language=portal_language)
440        Score = translate(_('Score'), target_language=portal_language)
441        Grade = translate(_('Grade'), target_language=portal_language)
442        studentview = StudentBasePDFFormPage(self.context.student,
443            self.request, self.omit_fields)
444        students_utils = getUtility(IStudentsUtils)
445
446        tableheader = [(Code,'code', 2.5),
447                         (Title,'title', 7),
448                         (Term, 'semester', 1.5),
449                         (Cred, 'credits', 1.5),
450                         (Score, 'total_score', 1.5),
451                         (Grade, 'grade', 1.5),
452                         ]
453
454        return students_utils.renderPDFTranscript(
455            self, 'transcript.pdf',
456            self.context.student, studentview,
457            omit_fields=self.omit_fields,
458            tableheader=tableheader,
459            signatures=self._signatures(),
460            sigs_in_footer=self._sigsInFooter(),
461            )
462
463class CustomExportPDFAdmissionSlip(ExportPDFAdmissionSlip):
464    """Deliver a PDF Admission slip.
465    """
466
467    @property
468    def label(self):
469        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
470        return translate(_('e-Admission Slip \n'),
471            target_language=portal_language) \
472            + ' %s' % self.context.display_fullname
473
474class CustomExportPDFClearanceSlip(NigeriaExportPDFClearanceSlip):
475    """Deliver a PDF slip of the context.
476    """
477
478    @property
479    def label(self):
480        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
481        return translate(_('Verification/Clearance Slip\n'),
482            target_language=portal_language) \
483            + ' %s' % self.context.display_fullname
484
485    @property
486    def form_fields(self):
487        if self.context.is_postgrad:
488            form_fields = grok.AutoFields(
489                ICustomPGStudentClearance).omit('clearance_locked')
490        else:
491            form_fields = grok.AutoFields(
492                ICustomUGStudentClearance).omit('clearance_locked')
493        if not getattr(self.context, 'officer_comment'):
494            form_fields = form_fields.omit('officer_comment')
495        form_fields = form_fields.omit('def_adm')
496        return form_fields
497
498class StudentGetMatricNumberPage(UtilityView, grok.View):
499    """ Construct and set the matriculation number.
500    """
501    grok.context(IStudent)
502    grok.name('get_matric_number')
503    grok.require('waeup.viewStudent')
504
505    def update(self):
506        students_utils = getUtility(IStudentsUtils)
507        msg, mnumber = students_utils.setMatricNumber(self.context)
508        if msg:
509            self.flash(msg, type="danger")
510        else:
511            self.flash(_('Matriculation number %s assigned.' % mnumber))
512            self.context.writeLogMessage(self, '%s assigned' % mnumber)
513        self.redirect(self.url(self.context))
514        return
515
516    def render(self):
517        return
518
519class ExportPDFMatricNumberSlip(UtilityView, grok.View):
520    """Deliver a PDF notification slip.
521    """
522    grok.context(ICustomStudent)
523    grok.name('matric_number_slip.pdf')
524    grok.require('waeup.viewStudent')
525    prefix = 'form'
526
527    form_fields = grok.AutoFields(ICustomStudent).select(
528        'student_id', 'matric_number')
529    omit_fields = ('date_of_birth', 'current_level', 'flash_notice')
530
531    @property
532    def title(self):
533        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
534        return translate(_('Matriculation Number'), 'waeup.kofa',
535            target_language=portal_language)
536
537    @property
538    def label(self):
539        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
540        return translate(_('Matriculation Number Slip\n'),
541            target_language=portal_language) \
542            + ' %s' % self.context.display_fullname
543
544    def render(self):
545        if self.context.state not in (PAID,) or not self.context.is_fresh \
546            or not self.context.matric_number:
547            self.flash('Not allowed.', type="danger")
548            self.redirect(self.url(self.context))
549            return
550        students_utils = getUtility(IStudentsUtils)
551        pre_text = _('Congratulations! Your acceptance fee and school fees ' +
552                     'payments have been received and your matriculation ' +
553                     'number generated with details as follows.')
554        return students_utils.renderPDFAdmissionLetter(self,
555            self.context.student, omit_fields=self.omit_fields,
556            pre_text=pre_text, post_text='')
557
558class ExportPersonalDataSlip(UtilityView, grok.View):
559    """Deliver a PDF notification slip.
560    """
561    grok.context(ICustomStudent)
562    grok.name('personal_data_slip.pdf')
563    grok.require('waeup.viewStudent')
564    prefix = 'form'
565    note = None
566
567    form_fields = grok.AutoFields(ICustomStudentPersonal).omit('personal_updated')
568    omit_fields = ('suspended', 'suspended_comment', 'adm_code',
569                   'certificate', 'flash_notice')
570
571    @property
572    def title(self):
573        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
574        return translate(_('Personal Data'), 'waeup.kofa',
575            target_language=portal_language)
576
577    @property
578    def label(self):
579        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
580        return translate(_('Personal Data Slip\n'),
581            target_language=portal_language) \
582            + ' %s' % self.context.display_fullname
583
584    def render(self):
585        studentview = StudentBasePDFFormPage(self.context.student,
586            self.request, self.omit_fields)
587        students_utils = getUtility(IStudentsUtils)
588        return students_utils.renderPDF(self, 'personal_data_slip.pdf',
589            self.context.student, studentview, note=self.note,
590            omit_fields=self.omit_fields)
591
592class CustomAccommodationManageFormPage(NigeriaAccommodationManageFormPage):
593    """ Page to manage bed tickets.
594    This manage form page is for both students and students officers.
595    """
596    with_hostel_selection = True
597
598class CustomBedTicketAddPage(BedTicketAddPage):
599    with_ac = False
600
601class CustomStudentFilesUploadPage(StudentFilesUploadPage):
602    """ View to upload files by student. Inherit from same class in
603    base package, not from kofacustom.nigeria which
604    requires that no application slip exists.
605    """
606
607class CustomCourseTicketDisplayFormPage(CourseTicketDisplayFormPage):
608    """ Page to display course tickets
609    """
610    form_fields = grok.AutoFields(ICustomCourseTicket)
611
612class CustomCourseTicketManageFormPage(CourseTicketManageFormPage):
613    """ Page to manage course tickets
614    """
615    form_fields = grok.AutoFields(ICustomCourseTicket)
616    form_fields['title'].for_display = True
617    form_fields['fcode'].for_display = True
618    form_fields['dcode'].for_display = True
619    form_fields['semester'].for_display = True
620    form_fields['passmark'].for_display = True
621    form_fields['credits'].for_display = True
622    form_fields['mandatory'].for_display = False
623    form_fields['automatic'].for_display = True
624    form_fields['carry_over'].for_display = True
625
626class CustomEditScoresPage(EditScoresPage):
627    """Page that filters and lists students.
628    """
629    grok.template('editscorespage')
630
631
632    def _extract_uploadfile(self, uploadfile):
633        """Get a mapping of student-ids to scores.
634
635        The mapping is constructed by reading contents from `uploadfile`.
636
637        We expect uploadfile to be a regular CSV file with columns
638        ``student_id``, ``score`` and ``ca`` (other cols are ignored).
639        """
640        result = dict()
641        data = StringIO(uploadfile.read())  # ensure we have something seekable
642        reader = csv.DictReader(data)
643        for row in reader:
644            if not ('student_id' in row and 'score' in row and 'ca' in row):
645                continue
646            result[row['student_id']] = (row['score'], row['ca'])
647        return result
648
649    def update(self,  *args, **kw):
650        form = self.request.form
651        ob_class = self.__implemented__.__name__.replace('waeup.kofa.', '')
652        self.current_academic_session = grok.getSite()[
653            'configuration'].current_academic_session
654        if self.context.__parent__.__parent__.score_editing_disabled:
655            self.flash(_('Score editing disabled.'), type="warning")
656            self.redirect(self.url(self.context))
657            return
658        if not self.current_academic_session:
659            self.flash(_('Current academic session not set.'), type="warning")
660            self.redirect(self.url(self.context))
661            return
662        self.session_title = academic_sessions_vocab.getTerm(
663            self.current_academic_session).title
664        self.tickets = self._searchCatalog(self.current_academic_session)
665        self.editable_tickets = [
666            ticket for ticket in self.tickets if ticket.editable_by_lecturer]
667        if not self.tickets:
668            self.flash(_('No student found.'), type="warning")
669            self.redirect(self.url(self.context))
670            return
671        if not 'UPDATE_TABLE' in form and not 'UPDATE_FILE' in form:
672            return
673
674        if not self.editable_tickets:
675            return
676        if 'UPDATE_FILE' in form:
677            if form['uploadfile']:
678                try:
679                    formvals = self._extract_uploadfile(form['uploadfile'])
680                except:
681                    self.flash(
682                        _('Uploaded file contains illegal data. Ignored'),
683                        type="danger")
684                    return
685            else:
686                self.flash(
687                    _('No file provided.'), type="danger")
688                return
689        else:
690            formvals = dict(zip(form['sids'], zip(form['scores'], form['cas'])))
691        error = ''
692        # In AAUE only editable tickets are shown, see also customized
693        # pagetemplate
694        for ticket in self.editable_tickets:
695            ticket_error = False
696            score = ticket.score
697            ca = ticket.ca
698            sid = ticket.student.student_id
699            if formvals[sid][0] == '':
700                score = None
701            if formvals[sid][1] == '':
702                ca = None
703            try:
704                if formvals[sid][0]:
705                    score = int(formvals[sid][0])
706                if formvals[sid][1]:
707                    ca = int(formvals[sid][1])
708            except ValueError:
709                error += '%s, ' % ticket.student.display_fullname
710                ticket_error = True
711            if not ticket_error and ticket.score != score:
712                try:
713                    ticket.score = score
714                except TooBig:
715                    error += '%s, ' % ticket.student.display_fullname
716                    ticket_error = True
717                    pass
718                ticket.student.__parent__.logger.info(
719                    '%s - %s %s/%s score updated (%s)' %
720                    (ob_class, ticket.student.student_id,
721                     ticket.level, ticket.code, score))
722            if not ticket_error and ticket.ca != ca:
723                try:
724                    ticket.ca = ca
725                except TooBig:
726                    error += '%s, ' % ticket.student.display_fullname
727                    pass
728                ticket.student.__parent__.logger.info(
729                    '%s - %s %s/%s ca updated (%s)' %
730                    (ob_class, ticket.student.student_id,
731                     ticket.level, ticket.code, ca))
732        if error:
733            self.flash(_('Error: Score(s) and CA(s) of %s have not be updated. '
734              % error.strip(', ')), type="danger")
735            return
736        self.flash(_('You successfully updated course results.'))
737        return
738
739class CustomExportPDFScoresSlip(ExportPDFScoresSlip):
740    """Deliver a PDF slip of course tickets for a lecturer.
741    """
742
743    def table_data(self, session):
744        cat = queryUtility(ICatalog, name='coursetickets_catalog')
745        coursetickets = cat.searchResults(
746            session=(session, session),
747            code=(self.context.code, self.context.code)
748            )
749        # In AAUE only editable tickets can be printed
750        editable_tickets = [
751            ticket for ticket in coursetickets if ticket.editable_by_lecturer]
752        header = [[_(''),
753                   _('Matric No.'),
754                   _('Reg. No.'),
755                   _('Fullname'),
756                   _('Status'),
757                   _('Course of\nStudies'),
758                   _('Level'),
759                   _('Exam\nScore'),
760                   _(' CA  '),
761                   _('Total '),
762                   _('Grade'),
763                   ],]
764        tickets = []
765        no = 1
766        # In AAUE only editable tickets can be printed
767        for ticket in editable_tickets:
768            if None in (ticket.score, ticket.ca):
769                total = 'n/a'
770                grade = 'n/a'
771            else:
772                total = ticket.score + ticket.ca
773                grade = getGradeWeightFromScore(total)[0]
774            fullname = textwrap.fill(ticket.student.display_fullname, 30)
775            row = [no,
776                  ticket.student.matric_number,
777                  ticket.student.reg_number,
778                  fullname,
779                  ticket.student.translated_state,
780                  ticket.student.certcode,
781                  ticket.level,
782                  ticket.ca,
783                  ticket.score,
784                  total,
785                  grade,
786                  ]
787            tickets.append(row)
788            no += 1
789        return header + sorted(tickets,
790            key=lambda value: value[5] + value[3] + str(value[6]))
Note: See TracBrowser for help on using the repository browser.