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

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

Hide the score and CA or let them be fetching empty fields.

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