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

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

Add imported_gpa and imported_cgpa fields and adjust exporters and batch processors.

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