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

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

Define and use ICustomUGStudentClearanceEdit.

  • Property svn:keywords set to Id
File size: 34.3 KB
Line 
1## $Id: browser.py 14298 2016-11-29 08:15:06Z 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 (
31    IKofaUtils, academic_sessions_vocab, ICSVExporter)
32from waeup.kofa.students.interfaces import IStudentsUtils, IStudent
33from waeup.kofa.students.workflow import PAID, REGISTERED, RETURNING
34from waeup.kofa.students.studylevel import getGradeWeightFromScore
35from waeup.kofa.students.browser import (
36    StartClearancePage,
37    StudentBasePDFFormPage,
38    CourseTicketAddFormPage,
39    StudyLevelDisplayFormPage,
40    StudyLevelManageFormPage,
41    StudyLevelEditFormPage,
42    ExportPDFTranscriptSlip,
43    ExportPDFAdmissionSlip,
44    BedTicketAddPage,
45    StudentFilesUploadPage,
46    PaymentsManageFormPage,
47    CourseTicketDisplayFormPage,
48    CourseTicketManageFormPage,
49    EditScoresPage,
50    ExportPDFScoresSlip,
51    StudyCourseTranscriptPage,
52    DownloadScoresView,
53    )
54from kofacustom.nigeria.students.browser import (
55    NigeriaOnlinePaymentDisplayFormPage,
56    NigeriaOnlinePaymentAddFormPage,
57    NigeriaExportPDFPaymentSlip,
58    NigeriaExportPDFCourseRegistrationSlip,
59    NigeriaStudentPersonalDisplayFormPage,
60    NigeriaStudentPersonalEditFormPage,
61    NigeriaStudentPersonalManageFormPage,
62    NigeriaStudentClearanceDisplayFormPage,
63    NigeriaExportPDFClearanceSlip,
64    NigeriaStudentClearanceManageFormPage,
65    NigeriaStudentClearanceEditFormPage,
66    NigeriaAccommodationManageFormPage,
67    NigeriaStudentBaseDisplayFormPage,
68    NigeriaStudentBaseManageFormPage
69    )
70from waeup.aaue.students.interfaces import (
71    ICustomStudentOnlinePayment,
72    ICustomStudentStudyLevel,
73    ICustomStudent,
74    ICustomStudentPersonal,
75    ICustomStudentPersonalEdit,
76    ICustomUGStudentClearance,
77    ICustomUGStudentClearanceEdit,
78    ICustomPGStudentClearance,
79    ICustomCourseTicket,
80    ICustomStudentBase)
81from waeup.aaue.interswitch.browser import gateway_net_amt
82from waeup.aaue.interfaces import MessageFactory as _
83
84class CustomStudentBaseDisplayFormPage(NigeriaStudentBaseDisplayFormPage):
85    """ Page to display student base data
86    """
87    form_fields = grok.AutoFields(ICustomStudentBase).omit(
88        'password', 'suspended', 'suspended_comment', 'flash_notice')
89    form_fields[
90        'financial_clearance_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
91
92class CustomStudentBaseManageFormPage(NigeriaStudentBaseManageFormPage):
93    """ View to manage student base data
94    """
95    form_fields = grok.AutoFields(ICustomStudentBase).omit(
96        'student_id', 'adm_code', 'suspended',
97        'financially_cleared_by', 'financial_clearance_date')
98
99class CustomStudentPersonalDisplayFormPage(NigeriaStudentPersonalDisplayFormPage):
100    """ Page to display student personal data
101    """
102    form_fields = grok.AutoFields(ICustomStudentPersonal)
103    form_fields['perm_address'].custom_widget = BytesDisplayWidget
104    form_fields['father_address'].custom_widget = BytesDisplayWidget
105    form_fields['mother_address'].custom_widget = BytesDisplayWidget
106    form_fields['next_kin_address'].custom_widget = BytesDisplayWidget
107    form_fields[
108        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
109
110class CustomStudentPersonalEditFormPage(NigeriaStudentPersonalEditFormPage):
111    """ Page to edit personal data
112    """
113    form_fields = grok.AutoFields(ICustomStudentPersonalEdit).omit('personal_updated')
114
115class CustomStudentPersonalManageFormPage(NigeriaStudentPersonalManageFormPage):
116    """ Page to edit personal data
117    """
118    form_fields = grok.AutoFields(ICustomStudentPersonal)
119    form_fields['personal_updated'].for_display = True
120    form_fields[
121        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
122
123class CustomStudentClearanceDisplayFormPage(NigeriaStudentClearanceDisplayFormPage):
124    """ Page to display student clearance data
125    """
126
127    @property
128    def form_fields(self):
129        if self.context.is_postgrad:
130            form_fields = grok.AutoFields(
131                ICustomPGStudentClearance).omit('clearance_locked')
132        else:
133            form_fields = grok.AutoFields(
134                ICustomUGStudentClearance).omit('clearance_locked')
135        if not getattr(self.context, 'officer_comment'):
136            form_fields = form_fields.omit('officer_comment')
137        else:
138            form_fields['officer_comment'].custom_widget = BytesDisplayWidget
139        form_fields = form_fields.omit('def_adm')
140        return form_fields
141
142class CustomStudentClearanceManageFormPage(NigeriaStudentClearanceManageFormPage):
143    """ Page to edit student clearance data
144    """
145
146    @property
147    def form_fields(self):
148        if self.context.is_postgrad:
149            form_fields = grok.AutoFields(
150                ICustomPGStudentClearance).omit('clr_code')
151        else:
152            form_fields = grok.AutoFields(
153                ICustomUGStudentClearance).omit('clr_code')
154        form_fields = form_fields.omit('def_adm')
155        return form_fields
156
157class CustomStudentClearanceEditFormPage(NigeriaStudentClearanceEditFormPage):
158    """ View to edit student clearance data by student
159    """
160
161    @property
162    def form_fields(self):
163        if self.context.is_postgrad:
164            form_fields = grok.AutoFields(ICustomPGStudentClearance).omit(
165            'clearance_locked', 'nysc_location', 'clr_code', 'officer_comment',
166            'physical_clearance_date')
167        else:
168            form_fields = grok.AutoFields(ICustomUGStudentClearanceEdit).omit(
169            'clearance_locked', 'clr_code', 'officer_comment',
170            'physical_clearance_date', 'date_of_birth', 'nationality', 'lga')
171        form_fields = form_fields.omit('def_adm')
172        return form_fields
173
174class CustomStartClearancePage(StartClearancePage):
175    with_ac = False
176
177    @property
178    def all_required_fields_filled(self):
179        if not self.context.email:
180            return _("Email address is missing."), 'edit_base'
181        if not self.context.phone:
182            return _("Phone number is missing."), 'edit_base'
183        if not self.context.father_name:
184            return _("Personal data form is not properly filled."), 'edit_personal'
185        return
186
187class CustomOnlinePaymentDisplayFormPage(NigeriaOnlinePaymentDisplayFormPage):
188    """ Page to view an online payment ticket
189    """
190    grok.context(ICustomStudentOnlinePayment)
191    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
192        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
193    form_fields[
194        'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
195    form_fields[
196        'payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
197
198class CustomOnlinePaymentAddFormPage(NigeriaOnlinePaymentAddFormPage):
199    """ Page to add an online payment ticket
200    """
201    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).select(
202        'p_category')
203
204    alumni_payment_cats =  {
205        'transcript_local': 'Transcript Fee Local',
206        'transcript_inter': 'Transcript Fee International',
207        }
208
209    @property
210    def selectable_categories(self):
211        if 'alumni' in self.application_url():
212            return self.alumni_payment_cats.items()
213        categories = getUtility(IKofaUtils).SELECTABLE_PAYMENT_CATEGORIES
214        return sorted(categories.items())
215
216class CustomPaymentsManageFormPage(PaymentsManageFormPage):
217    """ Page to manage the student payments.
218
219    This manage form page is for both students and students officers.
220    """
221    @property
222    def manage_payments_allowed(self):
223        return checkPermission('waeup.manageStudent', self.context)
224
225class CustomExportPDFPaymentSlip(NigeriaExportPDFPaymentSlip):
226    """Deliver a PDF slip of the context.
227    """
228    grok.context(ICustomStudentOnlinePayment)
229    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
230        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
231    form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
232    form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
233
234    @property
235    def note(self):
236        p_session = self.context.p_session
237        try:
238            academic_session = grok.getSite()['configuration'][str(p_session)]
239        except KeyError:
240            academic_session = None
241        text =  '\n\n The Amount Authorized is inclusive of: '
242        if self.context.p_category in ('schoolfee_incl', 'schoolfee_1') \
243            and academic_session:
244            welfare_fee = gateway_net_amt(academic_session.welfare_fee)
245            union_fee = gateway_net_amt(academic_session.union_fee)
246            if self.context.student.entry_session == 2016 \
247                and self.context.student.entry_mode == 'ug_ft' \
248                and self.context.p_session == 2016:
249                # Add student id card fee to first school fee payment.
250                # Attention: The payment slip does not contain any information
251                # whether the fee was added or not.
252                # We can only draw conclusions from from the student's entry
253                # session whether the fee had been included.
254                id_card_fee = gateway_net_amt(academic_session.id_card_fee)
255                text += ('School Fee, '
256                         '%s Naira Student ID Card Fee, '
257                         '%s Naira Student Union Dues, '
258                         '%s Naira Student Welfare Assurance Fee and '
259                         % (id_card_fee, union_fee, welfare_fee))
260            else:
261                text += ('School Fee, '
262                         '%s Naira Student Union Dues, '
263                         '%s Naira Student Welfare Assurance Fee and '
264                         % (union_fee, welfare_fee))
265        elif self.context.p_category in (
266            'clearance_incl', 'clearance_medical_incl') and academic_session:
267            matric_gown_fee = gateway_net_amt(academic_session.matric_gown_fee)
268            lapel_fee = gateway_net_amt(academic_session.lapel_fee)
269            text += ('Acceptance Fee, '
270                     '%s Naira Matriculation Gown Fee, '
271                     '%s Naira Lapel/File Fee and '
272                     % (matric_gown_fee, lapel_fee))
273        return text + '250.0 Naira Transaction Charge.'
274
275class CustomStudyLevelDisplayFormPage(StudyLevelDisplayFormPage):
276    """ Page to display student study levels
277    """
278    grok.context(ICustomStudentStudyLevel)
279    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
280        'total_credits', 'gpa', 'level', 'imported_gpa', 'imported_cgpa')
281    form_fields[
282        'validation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
283
284    @property
285    def show_results(self):
286        isStudent = getattr(
287            self.request.principal, 'user_type', None) == 'student'
288        if isStudent and self.context.student.state != RETURNING \
289            and self.context.student.current_level == self.context.level:
290            return False
291        return True
292
293class CustomStudyLevelManageFormPage(StudyLevelManageFormPage):
294    """ Page to edit the student study level data
295    """
296    grok.context(ICustomStudentStudyLevel)
297
298class CustomStudyLevelEditFormPage(StudyLevelEditFormPage):
299    """ Page to edit the student study level data by students
300    """
301    grok.context(ICustomStudentStudyLevel)
302
303class CustomExportPDFCourseRegistrationSlip(
304    NigeriaExportPDFCourseRegistrationSlip):
305    """Deliver a PDF slip of the context.
306    """
307    grok.context(ICustomStudentStudyLevel)
308    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
309        'level_session', 'level_verdict',
310        'validated_by', 'validation_date', 'gpa', 'level',
311        'imported_gpa', 'imported_cgpa')
312
313    omit_fields = ('password', 'suspended', 'suspended_comment',
314        'phone', 'adm_code', 'sex', 'email', 'date_of_birth',
315        'department', 'current_mode', 'current_level', 'flash_notice')
316
317    @property
318    def show_results(self):
319        isStudent = getattr(
320            self.request.principal, 'user_type', None) == 'student'
321        return not isStudent \
322            or self.context.student.current_level != self.context.level \
323            or self.context.student.state == RETURNING
324
325    def update(self):
326        if self.context.student.state != REGISTERED \
327            and self.context.student.current_level == self.context.level:
328            self.flash(_('Forbidden'), type="warning")
329            self.redirect(self.url(self.context))
330            return
331
332    @property
333    def label(self):
334        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
335        lang = self.request.cookies.get('kofa.language', portal_language)
336        level_title = translate(self.context.level_title, 'waeup.kofa',
337            target_language=lang)
338        line0 = ''
339        if self.context.student.is_postgrad:
340            line0 = 'SCHOOL OF POSTGRADUATE STUDIES\n'
341        elif self.context.student.current_mode.endswith('_pt'):
342            line0 = 'DIRECTORATE OF PART-TIME DEGREE PROGRAMMES\n'
343        line1 = translate(_('Course Registration Slip'),
344            target_language=portal_language) \
345            + ' %s' % level_title
346        line2 = translate(_('Session'),
347            target_language=portal_language) \
348            + ' %s' % self.context.getSessionString
349        return '%s%s\n%s' % (line0, line1, line2)
350
351    @property
352    def title(self):
353        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
354        return translate(_('Units Registered'), target_language=portal_language)
355
356    def _signatures(self):
357        if self.context.student.current_mode.endswith('_pt') \
358            or self.context.student.current_mode == 'found':
359            return (
360                [('I have selected the course on the advise of my Head of '
361                 'Department. <br>', _('Student\'s Signature'), '<br>')],
362                [('This student has satisfied the department\'s requirements. '
363                 'I recommend to approve the course registration. <br>',
364                 _('Head of Department\'s Signature'), '<br>')],
365                [('' , _('Deputy Registrar\'s Signature'), '<br>')],
366                [('', _('Director\'s Signature'))]
367                )
368        if self.context.student.current_mode in (
369            'de_ft', 'ug_ft', 'dp_ft', 'transfer'):
370            return ([_('Academic Adviser\'s Signature'),
371                _('Faculty Officer\'s Signature'),
372                _('Student\'s Signature')],)
373
374        if self.context.student.current_mode in ('special_pg_ft', 'special_pg_pt'):
375            return (
376                [('I declare that all items of information supplied above are correct:' ,
377                    _('Student\'s Signature'), '<br>')],
378                [('We approved the above registration:',
379                    _('Major Supervisor (Name / Signature)'), '')],
380                [('', _('Co-Supervisor (Name / Signature)'), '')],
381                [('', _('Head of Department'), '<br>')],
382                [('The student has satisfied the conditions for renewal of '
383                  'registration for graduate school programme in this university:',
384                  _('Secretary <br /> (School of Postgraduate Studies)'), '')],
385                [('', _('Dean <br /> (School of Postgraduate Studies)'), '')],
386                )
387        return None
388
389
390    def render(self):
391        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
392        Sem = translate(_('Sem.'), target_language=portal_language)
393        Code = translate(_('Code'), target_language=portal_language)
394        Title = translate(_('Title'), target_language=portal_language)
395        Cred = translate(_('Cred.'), target_language=portal_language)
396        if self.show_results:
397            Score = translate(_('Score'), target_language=portal_language)
398            #CA = translate(_('CA'), target_language=portal_language)
399            Grade = translate(_('Grade'), target_language=portal_language)
400        Signature = translate(_('Lecturer\'s Signature'), 'waeup.aaue',
401            target_language=portal_language)
402        studentview = StudentBasePDFFormPage(self.context.student,
403            self.request, self.omit_fields)
404        students_utils = getUtility(IStudentsUtils)
405
406        tabledata = []
407        tableheader = []
408        contenttitle = []
409        for i in range(1,7):
410            tabledata.append(sorted(
411                [value for value in self.context.values() if value.semester == i],
412                key=lambda value: str(value.semester) + value.code))
413            if self.show_results:
414                tableheader.append([(Code,'code', 2.0),
415                                   (Title,'title', 7),
416                                   (Cred, 'credits', 1.5),
417                                   (Score, 'score', 1.4),
418                                   #(CA, 'ca', 1.4),
419                                   (Grade, 'grade', 1.4),
420                                   (Signature, 'dummy', 3),
421                                   ])
422            else:
423                tableheader.append([(Code,'code', 2.0),
424                                   (Title,'title', 7),
425                                   (Cred, 'credits', 1.5),
426                                   (Signature, 'dummy', 3),
427                                   ])
428        if len(self.label.split('\n')) == 3:
429            topMargin = 1.9
430        elif len(self.label.split('\n')) == 2:
431            topMargin = 1.7
432        else:
433            topMargin = 1.5
434        return students_utils.renderPDF(
435            self, 'course_registration_slip.pdf',
436            self.context.student, studentview,
437            tableheader=tableheader,
438            tabledata=tabledata,
439            signatures=self._signatures(),
440            topMargin=topMargin,
441            omit_fields=self.omit_fields
442            )
443
444class CustomStudyCourseTranscriptPage(StudyCourseTranscriptPage):
445    """ Page to display the student's transcript.
446    """
447    grok.require('waeup.viewStudent')
448
449class CustomExportPDFTranscriptSlip(ExportPDFTranscriptSlip):
450    """Deliver a PDF slip of the context.
451    """
452    grok.require('waeup.viewStudent')
453
454    note = _("""
455<br /><br /><br /><br />
456<font size='10'>
457<strong>Note:</strong> This copy is subject to correction for typographical errors and ratification by the departmental board.
458</font>
459""")
460
461    def _sigsInFooter(self):
462        return []
463
464    def _signatures(self):
465        return ([(
466            'Mrs. Uniamikogbo, S.O., mnim, manupa <br /> Prin. Asst Registrar  <br /> '
467            'Exams, Records and Data Processing Division <br /> For: Registrar')],)
468
469    def render(self):
470        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
471        Term = translate(_('Sem.'), target_language=portal_language)
472        Code = translate(_('Code'), target_language=portal_language)
473        Title = translate(_('Title'), target_language=portal_language)
474        Cred = translate(_('Credits'), target_language=portal_language)
475        Score = translate(_('Score'), target_language=portal_language)
476        Grade = translate(_('Grade'), target_language=portal_language)
477        studentview = StudentBasePDFFormPage(self.context.student,
478            self.request, self.omit_fields)
479        students_utils = getUtility(IStudentsUtils)
480
481        tableheader = [(Code,'code', 2.5),
482                         (Title,'title', 7),
483                         (Term, 'semester', 1.5),
484                         (Cred, 'credits', 1.5),
485                         (Score, 'total_score', 1.5),
486                         (Grade, 'grade', 1.5),
487                         ]
488
489        return students_utils.renderPDFTranscript(
490            self, 'transcript.pdf',
491            self.context.student, studentview,
492            omit_fields=self.omit_fields,
493            tableheader=tableheader,
494            signatures=self._signatures(),
495            sigs_in_footer=self._sigsInFooter(),
496            note = self.note,
497            no_passport=True
498            )
499
500class CustomExportPDFAdmissionSlip(ExportPDFAdmissionSlip):
501    """Deliver a PDF Admission slip.
502    """
503
504    @property
505    def label(self):
506        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
507        return translate(_('e-Admission Slip \n'),
508            target_language=portal_language) \
509            + ' %s' % self.context.display_fullname
510
511class CustomExportPDFClearanceSlip(NigeriaExportPDFClearanceSlip):
512    """Deliver a PDF slip of the context.
513    """
514
515    @property
516    def label(self):
517        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
518        return translate(_('Verification/Clearance Slip\n'),
519            target_language=portal_language) \
520            + ' %s' % self.context.display_fullname
521
522    @property
523    def form_fields(self):
524        if self.context.is_postgrad:
525            form_fields = grok.AutoFields(
526                ICustomPGStudentClearance).omit('clearance_locked')
527        else:
528            form_fields = grok.AutoFields(
529                ICustomUGStudentClearance).omit('clearance_locked')
530        if not getattr(self.context, 'officer_comment'):
531            form_fields = form_fields.omit('officer_comment')
532        form_fields = form_fields.omit('def_adm')
533        return form_fields
534
535class StudentGetMatricNumberPage(UtilityView, grok.View):
536    """ Construct and set the matriculation number.
537    """
538    grok.context(IStudent)
539    grok.name('get_matric_number')
540    grok.require('waeup.viewStudent')
541
542    def update(self):
543        students_utils = getUtility(IStudentsUtils)
544        msg, mnumber = students_utils.setMatricNumber(self.context)
545        if msg:
546            self.flash(msg, type="danger")
547        else:
548            self.flash(_('Matriculation number %s assigned.' % mnumber))
549            self.context.writeLogMessage(self, '%s assigned' % mnumber)
550        self.redirect(self.url(self.context))
551        return
552
553    def render(self):
554        return
555
556class ExportPDFMatricNumberSlip(UtilityView, grok.View):
557    """Deliver a PDF notification slip.
558    """
559    grok.context(ICustomStudent)
560    grok.name('matric_number_slip.pdf')
561    grok.require('waeup.viewStudent')
562    prefix = 'form'
563
564    form_fields = grok.AutoFields(ICustomStudent).select(
565        'student_id', 'matric_number')
566    omit_fields = ('date_of_birth', 'current_level', 'flash_notice')
567
568    @property
569    def title(self):
570        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
571        return translate(_('Matriculation Number'), 'waeup.kofa',
572            target_language=portal_language)
573
574    @property
575    def label(self):
576        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
577        return translate(_('Matriculation Number Slip\n'),
578            target_language=portal_language) \
579            + ' %s' % self.context.display_fullname
580
581    def render(self):
582        if self.context.state not in (PAID,) or not self.context.is_fresh \
583            or not self.context.matric_number:
584            self.flash('Not allowed.', type="danger")
585            self.redirect(self.url(self.context))
586            return
587        students_utils = getUtility(IStudentsUtils)
588        pre_text = _('Congratulations! Your acceptance fee and school fees ' +
589                     'payments have been received and your matriculation ' +
590                     'number generated with details as follows.')
591        return students_utils.renderPDFAdmissionLetter(self,
592            self.context.student, omit_fields=self.omit_fields,
593            pre_text=pre_text, post_text='')
594
595class ExportPersonalDataSlip(UtilityView, grok.View):
596    """Deliver a PDF notification slip.
597    """
598    grok.context(ICustomStudent)
599    grok.name('personal_data_slip.pdf')
600    grok.require('waeup.viewStudent')
601    prefix = 'form'
602    note = None
603
604    form_fields = grok.AutoFields(ICustomStudentPersonal).omit('personal_updated')
605    omit_fields = ('suspended', 'suspended_comment', 'adm_code',
606                   'certificate', 'flash_notice')
607
608    @property
609    def title(self):
610        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
611        return translate(_('Personal Data'), 'waeup.kofa',
612            target_language=portal_language)
613
614    @property
615    def label(self):
616        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
617        return translate(_('Personal Data Slip\n'),
618            target_language=portal_language) \
619            + ' %s' % self.context.display_fullname
620
621    def render(self):
622        studentview = StudentBasePDFFormPage(self.context.student,
623            self.request, self.omit_fields)
624        students_utils = getUtility(IStudentsUtils)
625        return students_utils.renderPDF(self, 'personal_data_slip.pdf',
626            self.context.student, studentview, note=self.note,
627            omit_fields=self.omit_fields)
628
629class CustomAccommodationManageFormPage(NigeriaAccommodationManageFormPage):
630    """ Page to manage bed tickets.
631    This manage form page is for both students and students officers.
632    """
633    with_hostel_selection = True
634
635class CustomBedTicketAddPage(BedTicketAddPage):
636    with_ac = False
637
638class CustomStudentFilesUploadPage(StudentFilesUploadPage):
639    """ View to upload files by student. Inherit from same class in
640    base package, not from kofacustom.nigeria which
641    requires that no application slip exists.
642    """
643
644class CustomCourseTicketDisplayFormPage(CourseTicketDisplayFormPage):
645    """ Page to display course tickets
646    """
647    form_fields = grok.AutoFields(ICustomCourseTicket)
648
649class CustomCourseTicketManageFormPage(CourseTicketManageFormPage):
650    """ Page to manage course tickets
651    """
652    form_fields = grok.AutoFields(ICustomCourseTicket)
653    form_fields['title'].for_display = True
654    form_fields['fcode'].for_display = True
655    form_fields['dcode'].for_display = True
656    form_fields['semester'].for_display = True
657    form_fields['passmark'].for_display = True
658    form_fields['credits'].for_display = True
659    form_fields['mandatory'].for_display = False
660    form_fields['automatic'].for_display = True
661    form_fields['carry_over'].for_display = True
662
663class CustomEditScoresPage(EditScoresPage):
664    """Page that filters and lists students.
665    """
666    grok.template('editscorespage')
667
668
669    def _extract_uploadfile(self, uploadfile):
670        """Get a mapping of student-ids to scores.
671
672        The mapping is constructed by reading contents from `uploadfile`.
673
674        We expect uploadfile to be a regular CSV file with columns
675        ``student_id``, ``score`` and ``ca`` (other cols are ignored).
676        """
677        result = dict()
678        data = StringIO(uploadfile.read())  # ensure we have something seekable
679        reader = csv.DictReader(data)
680        for row in reader:
681            if not ('student_id' in row and 'score' in row and 'ca' in row):
682                continue
683            result[row['student_id']] = (row['score'], row['ca'])
684        return result
685
686    def _update_scores(self, form):
687        ob_class = self.__implemented__.__name__.replace('waeup.kofa.', '')
688        error = ''
689        if 'UPDATE_FILE' in form:
690            if form['uploadfile']:
691                try:
692                    formvals = self._extract_uploadfile(form['uploadfile'])
693                except:
694                    self.flash(
695                        _('Uploaded file contains illegal data. Ignored'),
696                        type="danger")
697                    return False
698            else:
699                self.flash(
700                    _('No file provided.'), type="danger")
701                return False
702        else:
703            formvals = dict(zip(form['sids'], zip(form['scores'], form['cas'])))
704        for ticket in self.editable_tickets:
705            ticket_error = False
706            score = ticket.score
707            ca = ticket.ca
708            sid = ticket.student.student_id
709            if formvals[sid][0] == '':
710                score = None
711            if formvals[sid][1] == '':
712                ca = None
713            try:
714                if formvals[sid][0]:
715                    score = int(formvals[sid][0])
716                if formvals[sid][1]:
717                    ca = int(formvals[sid][1])
718            except ValueError:
719                error += '%s, ' % ticket.student.display_fullname
720                ticket_error = True
721            if not ticket_error and ticket.score != score:
722                try:
723                    ticket.score = score
724                except TooBig:
725                    error += '%s, ' % ticket.student.display_fullname
726                    ticket_error = True
727                    pass
728                ticket.student.__parent__.logger.info(
729                    '%s - %s %s/%s score updated (%s)' %
730                    (ob_class, ticket.student.student_id,
731                     ticket.level, ticket.code, score))
732            if not ticket_error and ticket.ca != ca:
733                try:
734                    ticket.ca = ca
735                except TooBig:
736                    error += '%s, ' % ticket.student.display_fullname
737                    pass
738                ticket.student.__parent__.logger.info(
739                    '%s - %s %s/%s ca updated (%s)' %
740                    (ob_class, ticket.student.student_id,
741                     ticket.level, ticket.code, ca))
742        if error:
743            self.flash(_('Error: Score(s) and CA(s) of %s have not be updated. '
744              % error.strip(', ')), type="danger")
745        return True
746
747class EditPreviousSessionScoresPage(CustomEditScoresPage):
748
749    grok.name('edit_prev_scores')
750
751    def update(self,  *args, **kw):
752        form = self.request.form
753        self.current_academic_session = grok.getSite()[
754            'configuration'].current_academic_session
755        if self.context.__parent__.__parent__.score_editing_disabled:
756            self.flash(_('Score editing disabled.'), type="warning")
757            self.redirect(self.url(self.context))
758            return
759        if not self.current_academic_session:
760            self.flash(_('Current academic session not set.'), type="warning")
761            self.redirect(self.url(self.context))
762            return
763        previous_session = self.current_academic_session - 1
764        self.session_title = academic_sessions_vocab.getTerm(
765            previous_session).title
766        self.tickets = self._searchCatalog(previous_session)
767        if not self.tickets:
768            self.flash(_('No student found.'), type="warning")
769            self.redirect(self.url(self.context))
770            return
771        self.editable_tickets = [
772            ticket for ticket in self.tickets if ticket.editable_by_lecturer]
773        if not 'UPDATE_TABLE' in form and not 'UPDATE_FILE' in form:
774            return
775        if not self.editable_tickets:
776            return
777        success = self._update_scores(form)
778        if success:
779            self.flash(_('You successfully updated course results.'))
780        return
781
782class CustomExportPDFScoresSlip(ExportPDFScoresSlip):
783    """Deliver a PDF slip of course tickets for a lecturer.
784    """
785
786    def table_data(self, session):
787        cat = queryUtility(ICatalog, name='coursetickets_catalog')
788        coursetickets = cat.searchResults(
789            session=(session, session),
790            code=(self.context.code, self.context.code)
791            )
792        # In AAUE only editable tickets can be printed
793        editable_tickets = [
794            ticket for ticket in coursetickets if ticket.editable_by_lecturer]
795        header = [[_(''),
796                   _('Matric No.'),
797                   _('Reg. No.'),
798                   _('Fullname'),
799                   _('Status'),
800                   _('Course of\nStudies'),
801                   _('Level'),
802                   _('Exam\nScore'),
803                   _(' CA  '),
804                   _('Total '),
805                   _('Grade'),
806                   ],]
807        sorted_tickets = sorted(editable_tickets,
808            key=lambda ticket: ticket.student.certcode +
809                ticket.student.display_fullname + str(ticket.level))
810        no = 1
811        tickets = []
812        # In AAUE only editable tickets can be printed
813        for ticket in sorted_tickets:
814            if None in (ticket.score, ticket.ca):
815                total = 'n/a'
816                grade = 'n/a'
817            else:
818                total = ticket.score + ticket.ca
819                grade = getGradeWeightFromScore(total)[0]
820            fullname = textwrap.fill(ticket.student.display_fullname, 30)
821            row = [no,
822                  ticket.student.matric_number,
823                  ticket.student.reg_number,
824                  fullname,
825                  ticket.student.translated_state,
826                  ticket.student.certcode,
827                  ticket.level,
828                  ticket.ca,
829                  ticket.score,
830                  total,
831                  grade,
832                  ]
833            tickets.append(row)
834            no += 1
835        return header + tickets
836
837class DownloadPreviousSessionScoresView(DownloadScoresView):
838    """View that exports scores.
839    """
840    grok.name('download_prev_scores')
841
842    def update(self):
843        self.current_academic_session = grok.getSite()[
844            'configuration'].current_academic_session
845        if self.context.__parent__.__parent__.score_editing_disabled:
846            self.flash(_('Score editing disabled.'), type="warning")
847            self.redirect(self.url(self.context))
848            return
849        if not self.current_academic_session:
850            self.flash(_('Current academic session not set.'), type="warning")
851            self.redirect(self.url(self.context))
852            return
853        site = grok.getSite()
854        exporter = getUtility(ICSVExporter, name='lecturer')
855        self.csv = exporter.export_filtered(site, filepath=None,
856                                 catalog='coursetickets',
857                                 session=self.current_academic_session-1,
858                                 level=None,
859                                 code=self.context.code)
860        return
Note: See TracBrowser for help on using the repository browser.