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

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

In AAUE only editable tickets are shown.

  • Property svn:keywords set to Id
File size: 31.5 KB
Line 
1## $Id: browser.py 14149 2016-09-02 05:42:16Z 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    )
51from kofacustom.nigeria.students.browser import (
52    NigeriaOnlinePaymentDisplayFormPage,
53    NigeriaOnlinePaymentAddFormPage,
54    NigeriaExportPDFPaymentSlip,
55    NigeriaExportPDFCourseRegistrationSlip,
56    NigeriaStudentPersonalDisplayFormPage,
57    NigeriaStudentPersonalEditFormPage,
58    NigeriaStudentPersonalManageFormPage,
59    NigeriaStudentClearanceDisplayFormPage,
60    NigeriaExportPDFClearanceSlip,
61    NigeriaStudentClearanceManageFormPage,
62    NigeriaStudentClearanceEditFormPage,
63    NigeriaAccommodationManageFormPage,
64    NigeriaStudentBaseDisplayFormPage,
65    NigeriaStudentBaseManageFormPage,
66    NigeriaExportPDFFinancialClearancePage
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            text += ('Acceptance Fee, '
240                     '%s Naira Matriculation Gown Fee, '
241                     '%s Naira Lapel/File Fee and '
242                     % (matric_gown_fee, lapel_fee))
243        return text + '250.0 Naira Transaction Charge.'
244
245class CustomStudyLevelDisplayFormPage(StudyLevelDisplayFormPage):
246    """ Page to display student study levels
247    """
248    grok.context(ICustomStudentStudyLevel)
249    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
250        'total_credits', 'gpa', 'level')
251    form_fields[
252        'validation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
253
254    @property
255    def show_results(self):
256        isStudent = getattr(
257            self.request.principal, 'user_type', None) == 'student'
258        if isStudent and self.context.student.state != RETURNING \
259            and self.context.student.current_level == self.context.level:
260            return False
261        return True
262
263class CustomStudyLevelManageFormPage(StudyLevelManageFormPage):
264    """ Page to edit the student study level data
265    """
266    grok.context(ICustomStudentStudyLevel)
267
268class CustomStudyLevelEditFormPage(StudyLevelEditFormPage):
269    """ Page to edit the student study level data by students
270    """
271    grok.context(ICustomStudentStudyLevel)
272
273class CustomExportPDFCourseRegistrationSlip(
274    NigeriaExportPDFCourseRegistrationSlip):
275    """Deliver a PDF slip of the context.
276    """
277    grok.context(ICustomStudentStudyLevel)
278    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
279        'level_session', 'level_verdict',
280        'validated_by', 'validation_date', 'gpa', 'level')
281
282    omit_fields = ('password', 'suspended', 'suspended_comment',
283        'phone', 'adm_code', 'sex', 'email', 'date_of_birth',
284        'department', 'current_mode', 'current_level', 'flash_notice')
285
286    @property
287    def show_results(self):
288        isStudent = getattr(
289            self.request.principal, 'user_type', None) == 'student'
290        return not isStudent \
291            or self.context.student.current_level != self.context.level \
292            or self.context.student.state == RETURNING
293
294    def update(self):
295        if self.context.student.state != REGISTERED \
296            and self.context.student.current_level == self.context.level:
297            self.flash(_('Forbidden'), type="warning")
298            self.redirect(self.url(self.context))
299            return
300
301    @property
302    def label(self):
303        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
304        lang = self.request.cookies.get('kofa.language', portal_language)
305        level_title = translate(self.context.level_title, 'waeup.kofa',
306            target_language=lang)
307        line0 = ''
308        if self.context.student.is_postgrad:
309            line0 = 'SCHOOL OF POSTGRADUATE STUDIES\n'
310        elif self.context.student.current_mode.endswith('_pt'):
311            line0 = 'DIRECTORATE OF PART-TIME DEGREE PROGRAMMES\n'
312        line1 = translate(_('Course Registration Slip'),
313            target_language=portal_language) \
314            + ' %s' % level_title
315        line2 = translate(_('Session'),
316            target_language=portal_language) \
317            + ' %s' % self.context.getSessionString
318        return '%s%s\n%s' % (line0, line1, line2)
319
320    @property
321    def title(self):
322        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
323        return translate(_('Units Registered'), target_language=portal_language)
324
325    def _signatures(self):
326        if self.context.student.current_mode.endswith('_pt') \
327            or self.context.student.current_mode == 'found':
328            return (
329                [('I have selected the course on the advise of my Head of '
330                 'Department. <br>', _('Student\'s Signature'), '<br>')],
331                [('This student has satisfied the department\'s requirements. '
332                 'I recommend to approve the course registration. <br>',
333                 _('Head of Department\'s Signature'), '<br>')],
334                [('' , _('Deputy Registrar\'s Signature'), '<br>')],
335                [('', _('Director\'s Signature'))]
336                )
337        if self.context.student.current_mode in (
338            'de_ft', 'ug_ft', 'dp_ft', 'transfer'):
339            return ([_('Academic Adviser\'s Signature'),
340                _('Faculty Officer\'s Signature'),
341                _('Student\'s Signature')],)
342
343        if self.context.student.current_mode in ('special_pg_ft', 'special_pg_pt'):
344            return (
345                [('I declare that all items of information supplied above are correct:' ,
346                    _('Student\'s Signature'), '<br>')],
347                [('We approved the above registration:',
348                    _('Major Supervisor (Name / Signature)'), '')],
349                [('', _('Co-Supervisor (Name / Signature)'), '')],
350                [('', _('Head of Department'), '<br>')],
351                [('The student has satisfied the conditions for renewal of '
352                  'registration for graduate school programme in this university:',
353                  _('Secretary <br /> (School of Postgraduate Studies)'), '')],
354                [('', _('Dean <br /> (School of Postgraduate Studies)'), '')],
355                )
356        return None
357
358
359    def render(self):
360        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
361        Sem = translate(_('Sem.'), target_language=portal_language)
362        Code = translate(_('Code'), target_language=portal_language)
363        Title = translate(_('Title'), target_language=portal_language)
364        Cred = translate(_('Cred.'), target_language=portal_language)
365        if self.show_results:
366            Score = translate(_('Score'), target_language=portal_language)
367            #CA = translate(_('CA'), target_language=portal_language)
368            Grade = translate(_('Grade'), target_language=portal_language)
369        Signature = translate(_('Lecturer\'s Signature'), 'waeup.aaue',
370            target_language=portal_language)
371        studentview = StudentBasePDFFormPage(self.context.student,
372            self.request, self.omit_fields)
373        students_utils = getUtility(IStudentsUtils)
374
375        tabledata = []
376        tableheader = []
377        contenttitle = []
378        for i in range(1,7):
379            tabledata.append(sorted(
380                [value for value in self.context.values() if value.semester == i],
381                key=lambda value: str(value.semester) + value.code))
382            if self.show_results:
383                tableheader.append([(Code,'code', 2.0),
384                                   (Title,'title', 7),
385                                   (Cred, 'credits', 1.5),
386                                   (Score, 'score', 1.4),
387                                   #(CA, 'ca', 1.4),
388                                   (Grade, 'grade', 1.4),
389                                   (Signature, 'dummy', 3),
390                                   ])
391            else:
392                tableheader.append([(Code,'code', 2.0),
393                                   (Title,'title', 7),
394                                   (Cred, 'credits', 1.5),
395                                   (Signature, 'dummy', 3),
396                                   ])
397        if len(self.label.split('\n')) == 3:
398            topMargin = 1.9
399        elif len(self.label.split('\n')) == 2:
400            topMargin = 1.7
401        else:
402            topMargin = 1.5
403        return students_utils.renderPDF(
404            self, 'course_registration_slip.pdf',
405            self.context.student, studentview,
406            tableheader=tableheader,
407            tabledata=tabledata,
408            signatures=self._signatures(),
409            topMargin=topMargin,
410            omit_fields=self.omit_fields
411            )
412
413class CustomExportPDFTranscriptSlip(ExportPDFTranscriptSlip):
414    """Deliver a PDF slip of the context.
415    """
416
417    def _sigsInFooter(self):
418        return []
419
420    def _signatures(self):
421        return ([(
422            'Mrs. Uniamikogbo, S.O., mnim, manupa <br /> Prin. Asst Registrar  <br /> '
423            'Exams, Records and Data Processing Division <br /> For: Registrar')],)
424
425    def render(self):
426        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
427        Term = translate(_('Sem.'), target_language=portal_language)
428        Code = translate(_('Code'), target_language=portal_language)
429        Title = translate(_('Title'), target_language=portal_language)
430        Cred = translate(_('Credits'), target_language=portal_language)
431        Score = translate(_('Score'), target_language=portal_language)
432        Grade = translate(_('Grade'), target_language=portal_language)
433        studentview = StudentBasePDFFormPage(self.context.student,
434            self.request, self.omit_fields)
435        students_utils = getUtility(IStudentsUtils)
436
437        tableheader = [(Code,'code', 2.5),
438                         (Title,'title', 7),
439                         (Term, 'semester', 1.5),
440                         (Cred, 'credits', 1.5),
441                         (Score, 'total_score', 1.5),
442                         (Grade, 'grade', 1.5),
443                         ]
444
445        return students_utils.renderPDFTranscript(
446            self, 'transcript.pdf',
447            self.context.student, studentview,
448            omit_fields=self.omit_fields,
449            tableheader=tableheader,
450            signatures=self._signatures(),
451            sigs_in_footer=self._sigsInFooter(),
452            )
453
454class CustomExportPDFAdmissionSlip(ExportPDFAdmissionSlip):
455    """Deliver a PDF Admission slip.
456    """
457
458    @property
459    def label(self):
460        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
461        return translate(_('e-Admission Slip \n'),
462            target_language=portal_language) \
463            + ' %s' % self.context.display_fullname
464
465class CustomExportPDFClearanceSlip(NigeriaExportPDFClearanceSlip):
466    """Deliver a PDF slip of the context.
467    """
468
469    @property
470    def label(self):
471        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
472        return translate(_('Verification/Clearance Slip\n'),
473            target_language=portal_language) \
474            + ' %s' % self.context.display_fullname
475
476    @property
477    def form_fields(self):
478        if self.context.is_postgrad:
479            form_fields = grok.AutoFields(
480                ICustomPGStudentClearance).omit('clearance_locked')
481        else:
482            form_fields = grok.AutoFields(
483                ICustomUGStudentClearance).omit('clearance_locked')
484        if not getattr(self.context, 'officer_comment'):
485            form_fields = form_fields.omit('officer_comment')
486        form_fields = form_fields.omit('def_adm')
487        return form_fields
488
489class StudentGetMatricNumberPage(UtilityView, grok.View):
490    """ Construct and set the matriculation number.
491    """
492    grok.context(IStudent)
493    grok.name('get_matric_number')
494    grok.require('waeup.viewStudent')
495
496    def update(self):
497        students_utils = getUtility(IStudentsUtils)
498        msg, mnumber = students_utils.setMatricNumber(self.context)
499        if msg:
500            self.flash(msg, type="danger")
501        else:
502            self.flash(_('Matriculation number %s assigned.' % mnumber))
503            self.context.writeLogMessage(self, '%s assigned' % mnumber)
504        self.redirect(self.url(self.context))
505        return
506
507    def render(self):
508        return
509
510class ExportPDFMatricNumberSlip(UtilityView, grok.View):
511    """Deliver a PDF notification slip.
512    """
513    grok.context(ICustomStudent)
514    grok.name('matric_number_slip.pdf')
515    grok.require('waeup.viewStudent')
516    prefix = 'form'
517
518    form_fields = grok.AutoFields(ICustomStudent).select(
519        'student_id', 'matric_number')
520    omit_fields = ('date_of_birth', 'current_level', 'flash_notice')
521
522    @property
523    def title(self):
524        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
525        return translate(_('Matriculation Number'), 'waeup.kofa',
526            target_language=portal_language)
527
528    @property
529    def label(self):
530        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
531        return translate(_('Matriculation Number Slip\n'),
532            target_language=portal_language) \
533            + ' %s' % self.context.display_fullname
534
535    def render(self):
536        if self.context.state not in (PAID,) or not self.context.is_fresh \
537            or not self.context.matric_number:
538            self.flash('Not allowed.', type="danger")
539            self.redirect(self.url(self.context))
540            return
541        students_utils = getUtility(IStudentsUtils)
542        pre_text = _('Congratulations! Your acceptance fee and school fees ' +
543                     'payments have been received and your matriculation ' +
544                     'number generated with details as follows.')
545        return students_utils.renderPDFAdmissionLetter(self,
546            self.context.student, omit_fields=self.omit_fields,
547            pre_text=pre_text, post_text='')
548
549class ExportPersonalDataSlip(UtilityView, grok.View):
550    """Deliver a PDF notification slip.
551    """
552    grok.context(ICustomStudent)
553    grok.name('personal_data_slip.pdf')
554    grok.require('waeup.viewStudent')
555    prefix = 'form'
556    note = None
557
558    form_fields = grok.AutoFields(ICustomStudentPersonal).omit('personal_updated')
559    omit_fields = ('suspended', 'suspended_comment', 'adm_code',
560                   'certificate', 'flash_notice')
561
562    @property
563    def title(self):
564        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
565        return translate(_('Personal Data'), 'waeup.kofa',
566            target_language=portal_language)
567
568    @property
569    def label(self):
570        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
571        return translate(_('Personal Data Slip\n'),
572            target_language=portal_language) \
573            + ' %s' % self.context.display_fullname
574
575    def render(self):
576        studentview = StudentBasePDFFormPage(self.context.student,
577            self.request, self.omit_fields)
578        students_utils = getUtility(IStudentsUtils)
579        return students_utils.renderPDF(self, 'personal_data_slip.pdf',
580            self.context.student, studentview, note=self.note,
581            omit_fields=self.omit_fields)
582
583class CustomAccommodationManageFormPage(NigeriaAccommodationManageFormPage):
584    """ Page to manage bed tickets.
585    This manage form page is for both students and students officers.
586    """
587    with_hostel_selection = True
588
589class CustomBedTicketAddPage(BedTicketAddPage):
590    with_ac = False
591
592class CustomStudentFilesUploadPage(StudentFilesUploadPage):
593    """ View to upload files by student. Inherit from same class in
594    base package, not from kofacustom.nigeria which
595    requires that no application slip exists.
596    """
597
598class CustomCourseTicketDisplayFormPage(CourseTicketDisplayFormPage):
599    """ Page to display course tickets
600    """
601    form_fields = grok.AutoFields(ICustomCourseTicket)
602
603class CustomCourseTicketManageFormPage(CourseTicketManageFormPage):
604    """ Page to manage course tickets
605    """
606    form_fields = grok.AutoFields(ICustomCourseTicket)
607    form_fields['title'].for_display = True
608    form_fields['fcode'].for_display = True
609    form_fields['dcode'].for_display = True
610    form_fields['semester'].for_display = True
611    form_fields['passmark'].for_display = True
612    form_fields['credits'].for_display = True
613    form_fields['mandatory'].for_display = False
614    form_fields['automatic'].for_display = True
615    form_fields['carry_over'].for_display = True
616
617class CustomEditScoresPage(EditScoresPage):
618    """Page that filters and lists students.
619    """
620    grok.template('editscorespage')
621
622
623    def _extract_uploadfile(self, uploadfile):
624        """Get a mapping of student-ids to scores.
625
626        The mapping is constructed by reading contents from `uploadfile`.
627
628        We expect uploadfile to be a regular CSV file with columns
629        ``student_id``, ``score`` and ``ca`` (other cols are ignored).
630        """
631        result = dict()
632        data = StringIO(uploadfile.read())  # ensure we have something seekable
633        reader = csv.DictReader(data)
634        for row in reader:
635            if not ('student_id' in row and 'score' in row and 'ca' in row):
636                continue
637            result[row['student_id']] = (row['score'], row['ca'])
638        return result
639
640    def update(self,  *args, **kw):
641        form = self.request.form
642        ob_class = self.__implemented__.__name__.replace('waeup.kofa.', '')
643        self.current_academic_session = grok.getSite()[
644            'configuration'].current_academic_session
645        if self.context.__parent__.__parent__.score_editing_disabled:
646            self.flash(_('Score editing disabled.'), type="warning")
647            self.redirect(self.url(self.context))
648            return
649        if not self.current_academic_session:
650            self.flash(_('Current academic session not set.'), type="warning")
651            self.redirect(self.url(self.context))
652            return
653        self.session_title = academic_sessions_vocab.getTerm(
654            self.current_academic_session).title
655        self.tickets = self._searchCatalog(self.current_academic_session)
656        self.editable_tickets = [
657            ticket for ticket in self.tickets if ticket.editable_by_lecturer]
658        if not self.tickets:
659            self.flash(_('No student found.'), type="warning")
660            self.redirect(self.url(self.context))
661            return
662        if not 'UPDATE_TABLE' in form and not 'UPDATE_FILE' in form:
663            return
664
665        if not self.editable_tickets:
666            return
667        if 'UPDATE_FILE' in form:
668            if form['uploadfile']:
669                try:
670                    formvals = self._extract_uploadfile(form['uploadfile'])
671                except:
672                    self.flash(
673                        _('Uploaded file contains illegal data. Ignored'),
674                        type="danger")
675                    return
676            else:
677                self.flash(
678                    _('No file provided.'), type="danger")
679                return
680        else:
681            formvals = dict(zip(form['sids'], zip(form['scores'], form['cas'])))
682        error = ''
683        # In AAUE only editable tickets are shown, see also customized
684        # pagetemplate
685        for ticket in self.editable_tickets:
686            ticket_error = False
687            score = ticket.score
688            ca = ticket.ca
689            sid = ticket.student.student_id
690            if formvals[sid][0] == '':
691                score = None
692            if formvals[sid][1] == '':
693                ca = None
694            try:
695                if formvals[sid][0]:
696                    score = int(formvals[sid][0])
697                if formvals[sid][1]:
698                    ca = int(formvals[sid][1])
699            except ValueError:
700                error += '%s, ' % ticket.student.display_fullname
701                ticket_error = True
702            if not ticket_error and ticket.score != score:
703                try:
704                    ticket.score = score
705                except TooBig:
706                    error += '%s, ' % ticket.student.display_fullname
707                    ticket_error = True
708                    pass
709                ticket.student.__parent__.logger.info(
710                    '%s - %s %s/%s score updated (%s)' %
711                    (ob_class, ticket.student.student_id,
712                     ticket.level, ticket.code, score))
713            if not ticket_error and ticket.ca != ca:
714                try:
715                    ticket.ca = ca
716                except TooBig:
717                    error += '%s, ' % ticket.student.display_fullname
718                    pass
719                ticket.student.__parent__.logger.info(
720                    '%s - %s %s/%s ca updated (%s)' %
721                    (ob_class, ticket.student.student_id,
722                     ticket.level, ticket.code, ca))
723        if error:
724            self.flash(_('Error: Score(s) and CA(s) of %s have not be updated. '
725              % error.strip(', ')), type="danger")
726            return
727        self.flash(_('You successfully updated course results.'))
728        return
729
730class CustomExportPDFScoresSlip(ExportPDFScoresSlip):
731    """Deliver a PDF slip of course tickets for a lecturer.
732    """
733
734    def table_data(self, session):
735        cat = queryUtility(ICatalog, name='coursetickets_catalog')
736        coursetickets = cat.searchResults(
737            session=(session, session),
738            code=(self.context.code, self.context.code)
739            )
740        # In AAUE only editable tickets can be printed
741        editable_tickets = [
742            ticket for ticket in coursetickets if ticket.editable_by_lecturer]
743        header = [[_(''),
744                   _('Matric No.'),
745                   _('Reg. No.'),
746                   _('Fullname'),
747                   _('Status'),
748                   _('Course of\nStudies'),
749                   _('Level'),
750                   _('Exam\nScore'),
751                   _(' CA  '),
752                   _('Total '),
753                   _('Grade'),
754                   ],]
755        tickets = []
756        no = 1
757        # In AAUE only editable tickets can be printed
758        for ticket in editable_tickets:
759            if None in (ticket.score, ticket.ca):
760                total = 'n/a'
761                grade = 'n/a'
762            else:
763                total = ticket.score + ticket.ca
764                grade = getGradeWeightFromScore(total)[0]
765            fullname = textwrap.fill(ticket.student.display_fullname, 30)
766            row = [no,
767                  ticket.student.matric_number,
768                  ticket.student.reg_number,
769                  fullname,
770                  ticket.student.translated_state,
771                  ticket.student.certcode,
772                  ticket.level,
773                  ticket.ca,
774                  ticket.score,
775                  total,
776                  grade,
777                  ]
778            tickets.append(row)
779            no += 1
780        return header + sorted(tickets,
781            key=lambda value: value[5] + value[3] + str(value[6]))
782
783
784
785class CustomExportPDFFinancialClearancePage(NigeriaExportPDFFinancialClearancePage):
786    """Deliver a PDF financial clearance slip.
787    """
788
789    def _sigsInFooter(self):
790        if not checkPermission('waeup.clearStudentFinancially', self.context):
791            return ()
792        return (_('Date, Checking Officer Signature'),
793                _('Date, Approving Officer Signature'),
794                )
Note: See TracBrowser for help on using the repository browser.