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

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

Remove CustomStudyCourseTranscriptPage?.

Students are allowed to view transcripts. Buttons not yet visible, waiting for confirmation.

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