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

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

Add sc_pin and sc_serial_number fields and customize all views.

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