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

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

Hide def_adm.

  • Property svn:keywords set to Id
File size: 30.5 KB
Line 
1## $Id: browser.py 14104 2016-08-22 09:06:47Z 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.security import checkPermission
25from zope.catalog.interfaces import ICatalog
26from zope.formlib.textwidgets import BytesDisplayWidget
27from waeup.kofa.browser.layout import UtilityView
28from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget
29from waeup.kofa.interfaces import IKofaUtils, academic_sessions_vocab
30from waeup.kofa.students.interfaces import IStudentsUtils, IStudent
31from waeup.kofa.students.workflow import PAID, REGISTERED, RETURNING
32from waeup.kofa.students.studylevel import getGradeWeightFromScore
33from waeup.kofa.students.browser import (
34    StartClearancePage,
35    StudentBasePDFFormPage,
36    CourseTicketAddFormPage,
37    StudyLevelDisplayFormPage,
38    StudyLevelManageFormPage,
39    StudyLevelEditFormPage,
40    ExportPDFTranscriptSlip,
41    ExportPDFAdmissionSlip,
42    BedTicketAddPage,
43    StudentFilesUploadPage,
44    PaymentsManageFormPage,
45    CourseTicketDisplayFormPage,
46    CourseTicketManageFormPage,
47    EditScoresPage,
48    ExportPDFScoresSlip
49    )
50from kofacustom.nigeria.students.browser import (
51    NigeriaOnlinePaymentDisplayFormPage,
52    NigeriaOnlinePaymentAddFormPage,
53    NigeriaExportPDFPaymentSlip,
54    NigeriaExportPDFCourseRegistrationSlip,
55    NigeriaStudentPersonalDisplayFormPage,
56    NigeriaStudentPersonalEditFormPage,
57    NigeriaStudentPersonalManageFormPage,
58    NigeriaStudentClearanceDisplayFormPage,
59    NigeriaExportPDFClearanceSlip,
60    NigeriaStudentClearanceManageFormPage,
61    NigeriaStudentClearanceEditFormPage,
62    NigeriaAccommodationManageFormPage,
63    NigeriaStudentBaseDisplayFormPage,
64    NigeriaStudentBaseManageFormPage
65    )
66from waeup.aaue.students.interfaces import (
67    ICustomStudentOnlinePayment,
68    ICustomStudentStudyLevel,
69    ICustomStudent,
70    ICustomStudentPersonal,
71    ICustomStudentPersonalEdit,
72    ICustomUGStudentClearance,
73    ICustomPGStudentClearance,
74    ICustomCourseTicket,
75    ICustomStudentBase)
76from waeup.aaue.interswitch.browser import gateway_net_amt
77from waeup.aaue.interfaces import MessageFactory as _
78
79class CustomStudentBaseDisplayFormPage(NigeriaStudentBaseDisplayFormPage):
80    """ Page to display student base data
81    """
82    form_fields = grok.AutoFields(ICustomStudentBase).omit(
83        'password', 'suspended', 'suspended_comment', 'flash_notice')
84    form_fields[
85        'financial_clearance_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
86
87class CustomStudentBaseManageFormPage(NigeriaStudentBaseManageFormPage):
88    """ View to manage student base data
89    """
90    form_fields = grok.AutoFields(ICustomStudentBase).omit(
91        'student_id', 'adm_code', 'suspended',
92        'financially_cleared_by', 'financial_clearance_date')
93
94class CustomStudentPersonalDisplayFormPage(NigeriaStudentPersonalDisplayFormPage):
95    """ Page to display student personal data
96    """
97    form_fields = grok.AutoFields(ICustomStudentPersonal)
98    form_fields['perm_address'].custom_widget = BytesDisplayWidget
99    form_fields['father_address'].custom_widget = BytesDisplayWidget
100    form_fields['mother_address'].custom_widget = BytesDisplayWidget
101    form_fields['next_kin_address'].custom_widget = BytesDisplayWidget
102    form_fields[
103        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
104
105class CustomStudentPersonalEditFormPage(NigeriaStudentPersonalEditFormPage):
106    """ Page to edit personal data
107    """
108    form_fields = grok.AutoFields(ICustomStudentPersonalEdit).omit('personal_updated')
109
110class CustomStudentPersonalManageFormPage(NigeriaStudentPersonalManageFormPage):
111    """ Page to edit personal data
112    """
113    form_fields = grok.AutoFields(ICustomStudentPersonal)
114    form_fields['personal_updated'].for_display = True
115    form_fields[
116        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
117
118class CustomStudentClearanceDisplayFormPage(NigeriaStudentClearanceDisplayFormPage):
119    """ Page to display student clearance data
120    """
121
122    @property
123    def form_fields(self):
124        if self.context.is_postgrad:
125            form_fields = grok.AutoFields(
126                ICustomPGStudentClearance).omit('clearance_locked')
127        else:
128            form_fields = grok.AutoFields(
129                ICustomUGStudentClearance).omit('clearance_locked')
130        if not getattr(self.context, 'officer_comment'):
131            form_fields = form_fields.omit('officer_comment')
132        else:
133            form_fields['officer_comment'].custom_widget = BytesDisplayWidget
134        form_fields = form_fields.omit('def_adm')
135        return form_fields
136
137class CustomStudentClearanceManageFormPage(NigeriaStudentClearanceManageFormPage):
138    """ Page to edit student clearance data
139    """
140
141    @property
142    def form_fields(self):
143        if self.context.is_postgrad:
144            form_fields = grok.AutoFields(
145                ICustomPGStudentClearance).omit('clr_code')
146        else:
147            form_fields = grok.AutoFields(
148                ICustomUGStudentClearance).omit('clr_code')
149        form_fields = form_fields.omit('def_adm')
150        return form_fields
151
152class CustomStudentClearanceEditFormPage(NigeriaStudentClearanceEditFormPage):
153    """ View to edit student clearance data by student
154    """
155
156    @property
157    def form_fields(self):
158        if self.context.is_postgrad:
159            form_fields = grok.AutoFields(ICustomPGStudentClearance).omit(
160            'clearance_locked', 'nysc_location', 'clr_code', 'officer_comment',
161            'physical_clearance_date')
162        else:
163            form_fields = grok.AutoFields(ICustomUGStudentClearance).omit(
164            'clearance_locked', 'clr_code', 'officer_comment',
165            'physical_clearance_date', 'date_of_birth', 'nationality', 'lga')
166        form_fields = form_fields.omit('def_adm')
167        return form_fields
168
169class CustomStartClearancePage(StartClearancePage):
170    with_ac = False
171
172    @property
173    def all_required_fields_filled(self):
174        if not self.context.email:
175            return _("Email address is missing."), 'edit_base'
176        if not self.context.phone:
177            return _("Phone number is missing."), 'edit_base'
178        if not self.context.father_name:
179            return _("Personal data form is not properly filled."), 'edit_personal'
180        return
181
182class CustomOnlinePaymentDisplayFormPage(NigeriaOnlinePaymentDisplayFormPage):
183    """ Page to view an online payment ticket
184    """
185    grok.context(ICustomStudentOnlinePayment)
186    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
187        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
188    form_fields[
189        'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
190    form_fields[
191        'payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
192
193class CustomOnlinePaymentAddFormPage(NigeriaOnlinePaymentAddFormPage):
194    """ Page to add an online payment ticket
195    """
196    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).select(
197        'p_category')
198
199class CustomPaymentsManageFormPage(PaymentsManageFormPage):
200    """ Page to manage the student payments.
201
202    This manage form page is for both students and students officers.
203    """
204    @property
205    def manage_payments_allowed(self):
206        return checkPermission('waeup.manageStudent', self.context)
207
208class CustomExportPDFPaymentSlip(NigeriaExportPDFPaymentSlip):
209    """Deliver a PDF slip of the context.
210    """
211    grok.context(ICustomStudentOnlinePayment)
212    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
213        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
214    form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
215    form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
216
217    @property
218    def note(self):
219        p_session = self.context.p_session
220        try:
221            academic_session = grok.getSite()['configuration'][str(p_session)]
222        except KeyError:
223            academic_session = None
224        text =  '\n\n The Amount Authorized is inclusive of: '
225        if self.context.p_category in ('schoolfee_incl', 'schoolfee_1') \
226            and academic_session:
227            welfare_fee = gateway_net_amt(academic_session.welfare_fee)
228            union_fee = gateway_net_amt(academic_session.union_fee)
229            text += ('School Fee, '
230                     '%s Naira Student Union Dues, '
231                     '%s Naira Student Welfare Assurance Fee and '
232                     % (union_fee, welfare_fee))
233        elif self.context.p_category in (
234            'clearance_incl', 'clearance_medical_incl') and academic_session:
235            matric_gown_fee = gateway_net_amt(academic_session.matric_gown_fee)
236            lapel_fee = gateway_net_amt(academic_session.lapel_fee)
237            text += ('Acceptance Fee, '
238                     '%s Naira Matriculation Gown Fee, '
239                     '%s Naira Lapel/File Fee and '
240                     % (matric_gown_fee, lapel_fee))
241        return text + '250.0 Naira Transaction Charge.'
242
243class CustomStudyLevelDisplayFormPage(StudyLevelDisplayFormPage):
244    """ Page to display student study levels
245    """
246    grok.context(ICustomStudentStudyLevel)
247    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
248        'total_credits', 'gpa', 'level')
249    form_fields[
250        'validation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
251
252    @property
253    def show_results(self):
254        isStudent = getattr(
255            self.request.principal, 'user_type', None) == 'student'
256        if isStudent and self.context.student.state != RETURNING \
257            and self.context.student.current_level == self.context.level:
258            return False
259        return True
260
261class CustomStudyLevelManageFormPage(StudyLevelManageFormPage):
262    """ Page to edit the student study level data
263    """
264    grok.context(ICustomStudentStudyLevel)
265
266class CustomStudyLevelEditFormPage(StudyLevelEditFormPage):
267    """ Page to edit the student study level data by students
268    """
269    grok.context(ICustomStudentStudyLevel)
270
271class CustomExportPDFCourseRegistrationSlip(
272    NigeriaExportPDFCourseRegistrationSlip):
273    """Deliver a PDF slip of the context.
274    """
275    grok.context(ICustomStudentStudyLevel)
276    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
277        'level_session', 'level_verdict',
278        'validated_by', 'validation_date', 'gpa', 'level')
279
280    omit_fields = ('password', 'suspended', 'suspended_comment',
281        'phone', 'adm_code', 'sex', 'email', 'date_of_birth',
282        'department', 'current_mode', 'current_level', 'flash_notice')
283
284    @property
285    def show_results(self):
286        isStudent = getattr(
287            self.request.principal, 'user_type', None) == 'student'
288        return not isStudent \
289            or self.context.student.current_level != self.context.level \
290            or self.context.student.state == RETURNING
291
292    def update(self):
293        if self.context.student.state != REGISTERED \
294            and self.context.student.current_level == self.context.level:
295            self.flash(_('Forbidden'), type="warning")
296            self.redirect(self.url(self.context))
297            return
298
299    @property
300    def label(self):
301        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
302        lang = self.request.cookies.get('kofa.language', portal_language)
303        level_title = translate(self.context.level_title, 'waeup.kofa',
304            target_language=lang)
305        line0 = ''
306        if self.context.student.is_postgrad:
307            line0 = 'SCHOOL OF POSTGRADUATE STUDIES\n'
308        elif self.context.student.current_mode.endswith('_pt'):
309            line0 = 'DIRECTORATE OF PART-TIME DEGREE PROGRAMMES\n'
310        line1 = translate(_('Course Registration Slip'),
311            target_language=portal_language) \
312            + ' %s' % level_title
313        line2 = translate(_('Session'),
314            target_language=portal_language) \
315            + ' %s' % self.context.getSessionString
316        return '%s%s\n%s' % (line0, line1, line2)
317
318    @property
319    def title(self):
320        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
321        return translate(_('Units Registered'), target_language=portal_language)
322
323    def _signatures(self):
324        if self.context.student.current_mode.endswith('_pt') \
325            or self.context.student.current_mode == 'found':
326            return (
327                [('I have selected the course on the advise of my Head of '
328                 'Department. <br>', _('Student\'s Signature'), '<br>')],
329                [('This student has satisfied the department\'s requirements. '
330                 'I recommend to approve the course registration. <br>',
331                 _('Head of Department\'s Signature'), '<br>')],
332                [('' , _('Deputy Registrar\'s Signature'), '<br>')],
333                [('', _('Director\'s Signature'))]
334                )
335        if self.context.student.current_mode in (
336            'de_ft', 'ug_ft', 'dp_ft', 'transfer'):
337            return ([_('Academic Adviser\'s Signature'),
338                _('Faculty Officer\'s Signature'),
339                _('Student\'s Signature')],)
340
341        if self.context.student.current_mode in ('special_pg_ft', 'special_pg_pt'):
342            return (
343                [('I declare that all items of information supplied above are correct:' ,
344                    _('Student\'s Signature'), '<br>')],
345                [('We approved the above registration:',
346                    _('Major Supervisor (Name / Signature)'), '')],
347                [('', _('Co-Supervisor (Name / Signature)'), '')],
348                [('', _('Head of Department'), '<br>')],
349                [('The student has satisfied the conditions for renewal of '
350                  'registration for graduate school programme in this university:',
351                  _('Secretary <br /> (School of Postgraduate Studies)'), '')],
352                [('', _('Dean <br /> (School of Postgraduate Studies)'), '')],
353                )
354        return None
355
356
357    def render(self):
358        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
359        Sem = translate(_('Sem.'), target_language=portal_language)
360        Code = translate(_('Code'), target_language=portal_language)
361        Title = translate(_('Title'), target_language=portal_language)
362        Cred = translate(_('Cred.'), target_language=portal_language)
363        if self.show_results:
364            Score = translate(_('Score'), target_language=portal_language)
365            #CA = translate(_('CA'), target_language=portal_language)
366            Grade = translate(_('Grade'), target_language=portal_language)
367        Signature = translate(_('Lecturer\'s Signature'), 'waeup.aaue',
368            target_language=portal_language)
369        studentview = StudentBasePDFFormPage(self.context.student,
370            self.request, self.omit_fields)
371        students_utils = getUtility(IStudentsUtils)
372
373        tabledata = []
374        tableheader = []
375        contenttitle = []
376        for i in range(1,7):
377            tabledata.append(sorted(
378                [value for value in self.context.values() if value.semester == i],
379                key=lambda value: str(value.semester) + value.code))
380            if self.show_results:
381                tableheader.append([(Code,'code', 2.0),
382                                   (Title,'title', 7),
383                                   (Cred, 'credits', 1.5),
384                                   (Score, 'score', 1.4),
385                                   #(CA, 'ca', 1.4),
386                                   (Grade, 'grade', 1.4),
387                                   (Signature, 'dummy', 3),
388                                   ])
389            else:
390                tableheader.append([(Code,'code', 2.0),
391                                   (Title,'title', 7),
392                                   (Cred, 'credits', 1.5),
393                                   (Signature, 'dummy', 3),
394                                   ])
395        if len(self.label.split('\n')) == 3:
396            topMargin = 1.9
397        elif len(self.label.split('\n')) == 2:
398            topMargin = 1.7
399        else:
400            topMargin = 1.5
401        return students_utils.renderPDF(
402            self, 'course_registration_slip.pdf',
403            self.context.student, studentview,
404            tableheader=tableheader,
405            tabledata=tabledata,
406            signatures=self._signatures(),
407            topMargin=topMargin,
408            omit_fields=self.omit_fields
409            )
410
411class CustomExportPDFTranscriptSlip(ExportPDFTranscriptSlip):
412    """Deliver a PDF slip of the context.
413    """
414
415    def _sigsInFooter(self):
416        return []
417
418    def _signatures(self):
419        return ([(
420            'Akhimien Felicia O. (MANUPA) <br /> Principal Assistant Registrar  <br /> '
421            'Exams, Records and Data Processing Division <br /> For: Registrar')],)
422
423    def render(self):
424        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
425        Term = translate(_('Sem.'), target_language=portal_language)
426        Code = translate(_('Code'), target_language=portal_language)
427        Title = translate(_('Title'), target_language=portal_language)
428        Cred = translate(_('Credits'), target_language=portal_language)
429        Score = translate(_('Score'), target_language=portal_language)
430        #CA = translate(_('CA'), target_language=portal_language)
431        Grade = translate(_('Grade'), target_language=portal_language)
432        studentview = StudentBasePDFFormPage(self.context.student,
433            self.request, self.omit_fields)
434        students_utils = getUtility(IStudentsUtils)
435
436        tableheader = [(Code,'code', 2.5),
437                         (Title,'title', 7),
438                         (Term, 'semester', 1.5),
439                         (Cred, 'credits', 1.5),
440                         (Score, 'score', 1.5),
441                         #(CA, 'ca', 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(_('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        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 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        for ticket in editable_tickets:
684            ticket_error = False
685            score = ticket.score
686            ca = ticket.ca
687            sid = ticket.student.student_id
688            if formvals[sid][0] == '':
689                score = None
690            if formvals[sid][1] == '':
691                ca = None
692            try:
693                if formvals[sid][0]:
694                    score = int(formvals[sid][0])
695                if formvals[sid][1]:
696                    ca = int(formvals[sid][1])
697            except ValueError:
698                error += '%s, ' % ticket.student.display_fullname
699                ticket_error = True
700            if not ticket_error and ticket.score != score:
701                ticket.score = score
702                ticket.student.__parent__.logger.info(
703                    '%s - %s %s/%s score updated (%s)' %
704                    (ob_class, ticket.student.student_id,
705                     ticket.level, ticket.code, score))
706            if not ticket_error and ticket.ca != ca:
707                ticket.ca = ca
708                ticket.student.__parent__.logger.info(
709                    '%s - %s %s/%s ca updated (%s)' %
710                    (ob_class, ticket.student.student_id,
711                     ticket.level, ticket.code, ca))
712        if error:
713            self.flash(_('Error: Score(s) and CA(s) of %s have not be updated. '
714              'Only integers are allowed.' % error.strip(', ')),
715              type="danger")
716            return
717        self.flash(_('You successfully updated course results.'))
718        return
719
720class CustomExportPDFScoresSlip(ExportPDFScoresSlip):
721    """Deliver a PDF slip of course tickets for a lecturer.
722    """
723
724    def table_data(self, session):
725        cat = queryUtility(ICatalog, name='coursetickets_catalog')
726        coursetickets = cat.searchResults(
727            session=(session, session),
728            code=(self.context.code, self.context.code)
729            )
730        header = [[_(''),
731                   _('Matric No.'),
732                   _('Reg. No.'),
733                   _('Fullname'),
734                   _('Status'),
735                   _('Course of\nStudies'),
736                   _('Level'),
737                   _('Exam\nScore'),
738                   _(' CA  '),
739                   _('Total '),
740                   _('Grade'),
741                   ],]
742        tickets = []
743        no = 1
744        for ticket in list(coursetickets):
745            if None in (ticket.score, ticket.ca):
746                total = 'n/a'
747                grade = 'n/a'
748            else:
749                total = ticket.score + ticket.ca
750                grade = getGradeWeightFromScore(total)[0]
751            fullname = textwrap.fill(ticket.student.display_fullname, 30)
752            row = [no,
753                  ticket.student.matric_number,
754                  ticket.student.reg_number,
755                  fullname,
756                  ticket.student.translated_state,
757                  ticket.student.certcode,
758                  ticket.level,
759                  ticket.ca,
760                  ticket.score,
761                  total,
762                  grade,
763                  ]
764            tickets.append(row)
765            no += 1
766        return header + sorted(tickets,
767            key=lambda value: value[5] + value[3] + str(value[6]))
768
Note: See TracBrowser for help on using the repository browser.