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

Last change on this file since 13963 was 13963, checked in by Henrik Bettermann, 9 years ago

Resolve ticket #228:

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