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

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

verification/clearance slip

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