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

Last change on this file since 17487 was 17443, checked in by Henrik Bettermann, 17 months ago

Reduce payment slip.

  • Property svn:keywords set to Id
File size: 52.0 KB
Line 
1## $Id: browser.py 17443 2023-06-20 09:12:19Z 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
21import pytz
22from cStringIO import StringIO
23from datetime import datetime
24from zope.i18n import translate
25from zope.component import getUtility, queryUtility
26from zope.schema.interfaces import TooBig, TooSmall
27from zope.security import checkPermission
28from zope.catalog.interfaces import ICatalog
29from zope.formlib.textwidgets import BytesDisplayWidget
30from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
31from waeup.kofa.browser.layout import UtilityView, KofaEditFormPage, jsaction
32from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget
33from waeup.kofa.interfaces import (
34    IKofaUtils, academic_sessions_vocab, ICSVExporter, IKofaObject)
35from waeup.kofa.students.interfaces import (
36    IStudentsUtils, IStudent, IStudentRequestPW, IStudentStudyLevel)
37from waeup.kofa.students.workflow import PAID, REGISTERED, RETURNING, GRADUATED
38from waeup.kofa.students.browser import (
39    StartClearancePage,
40    StudentBasePDFFormPage,
41    CourseTicketAddFormPage,
42    StudyLevelDisplayFormPage,
43    StudyLevelManageFormPage,
44    StudyLevelEditFormPage,
45    ExportPDFTranscriptSlip,
46    ExportPDFAdmissionSlip,
47    StudentFilesUploadPage,
48    PaymentsManageFormPage,
49    CourseTicketDisplayFormPage,
50    CourseTicketManageFormPage,
51    EditScoresPage,
52    ExportPDFScoresSlip,
53    StudyCourseTranscriptPage,
54    DownloadScoresView,
55    StudentRequestPasswordPage,
56    StudyCourseManageFormPage,
57    UnregisterCoursesView,
58    addCourseTicket,
59    AddStudyLevelFormPage,
60    emit_lock_message
61    )
62from kofacustom.nigeria.students.browser import (
63    NigeriaOnlinePaymentDisplayFormPage,
64    NigeriaOnlinePaymentAddFormPage,
65    NigeriaExportPDFPaymentSlip,
66    NigeriaExportPDFCourseRegistrationSlip,
67    NigeriaStudentPersonalDisplayFormPage,
68    NigeriaStudentPersonalEditFormPage,
69    NigeriaStudentPersonalManageFormPage,
70    NigeriaStudentClearanceDisplayFormPage,
71    NigeriaExportPDFClearanceSlip,
72    NigeriaStudentClearanceManageFormPage,
73    NigeriaStudentClearanceEditFormPage,
74    NigeriaAccommodationManageFormPage,
75    NigeriaAccommodationDisplayFormPage,
76    NigeriaStudentBaseDisplayFormPage,
77    NigeriaStudentBaseManageFormPage,
78    NigeriaBedTicketAddPage
79    )
80from waeup.aaue.students.interfaces import (
81    ICustomStudentOnlinePayment,
82    ICustomStudentStudyLevel,
83    ICustomStudent,
84    ICustomStudentPersonal,
85    ICustomStudentPersonalEdit,
86    ICustomUGStudentClearance,
87    ICustomUGStudentClearanceEdit,
88    ICustomPGStudentClearance,
89    ICustomCourseTicket,
90    ICustomStudentBase,
91    ICustomStudentStudyCourse)
92from waeup.aaue.interfaces import MessageFactory as _
93
94grok.context(IKofaObject)  # Make IKofaObject the default context
95
96def translated_values(view):
97    """Translate course ticket attribute values to be displayed on
98    studylevel pages.
99    """
100    lang = view.request.cookies.get('kofa.language')
101    for value in view.context.values():
102        value._p_activate()
103        value_dict = dict([i for i in value.__dict__.items()])
104        value_dict['url'] = view.url(value)
105        value_dict['removable_by_student'] = value.removable_by_student
106        value_dict['mandatory'] = translate(str(value.mandatory), 'zope',
107            target_language=lang)
108        value_dict['carry_over'] = translate(str(value.carry_over), 'zope',
109            target_language=lang)
110        value_dict['outstanding'] = translate(str(value.outstanding), 'zope',
111            target_language=lang)
112        value_dict['automatic'] = translate(str(value.automatic), 'zope',
113            target_language=lang)
114        value_dict['grade'] = value.grade
115        value_dict['weight'] = value.weight
116        value_dict['course_category'] = value.course_category
117        value_dict['total_score'] = value.total_score
118        semester_dict = getUtility(IKofaUtils).SEMESTER_DICT
119        value_dict['semester'] = semester_dict[
120            value.semester].replace('mester', 'm.')
121        # AAUE specific
122        value_dict['formatted_total_score'] = value.total_score
123        if getattr(value, 'imported_ts', None):
124            value_dict['formatted_total_score'] = "<strong>%s</strong>" % value.imported_ts
125        yield value_dict
126
127class CustomStudentBaseDisplayFormPage(NigeriaStudentBaseDisplayFormPage):
128    """ Page to display student base data
129    """
130    form_fields = grok.AutoFields(ICustomStudentBase).omit(
131        'password', 'suspended', 'suspended_comment', 'flash_notice')
132    form_fields[
133        'financial_clearance_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
134
135class CustomStudentBaseManageFormPage(NigeriaStudentBaseManageFormPage):
136    """ View to manage student base data
137    """
138    form_fields = grok.AutoFields(ICustomStudentBase).omit(
139        'student_id', 'adm_code', 'suspended',
140        'financially_cleared_by', 'financial_clearance_date')
141
142class CustomStudentPersonalDisplayFormPage(NigeriaStudentPersonalDisplayFormPage):
143    """ Page to display student personal data
144    """
145    form_fields = grok.AutoFields(ICustomStudentPersonal)
146    form_fields['perm_address'].custom_widget = BytesDisplayWidget
147    form_fields['father_address'].custom_widget = BytesDisplayWidget
148    form_fields['mother_address'].custom_widget = BytesDisplayWidget
149    form_fields['next_kin_address'].custom_widget = BytesDisplayWidget
150    form_fields[
151        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
152
153class CustomStudentPersonalEditFormPage(NigeriaStudentPersonalEditFormPage):
154    """ Page to edit personal data
155    """
156    form_fields = grok.AutoFields(ICustomStudentPersonalEdit).omit('personal_updated')
157
158class CustomStudentPersonalManageFormPage(NigeriaStudentPersonalManageFormPage):
159    """ Page to edit personal data
160    """
161    form_fields = grok.AutoFields(ICustomStudentPersonal)
162    form_fields['personal_updated'].for_display = True
163    form_fields[
164        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
165
166
167class ExportExaminationScheduleSlip(UtilityView, grok.View):
168    """Deliver a examination schedule slip.
169
170    This form page is available only in Uniben and AAUE.
171    """
172    grok.context(ICustomStudent)
173    grok.name('examination_schedule_slip.pdf')
174    grok.require('waeup.viewStudent')
175    prefix = 'form'
176
177    label = u'Examination Schedule Slip'
178
179    omit_fields = (
180        'suspended', 'phone', 'email',
181        'adm_code', 'suspended_comment',
182        'date_of_birth', 'current_level',
183        'current_mode',
184        'entry_session',
185        'flash_notice')
186
187    form_fields = []
188
189    @property
190    def note(self):
191        return """
192 <br /><br />
193 <strong>Instructions on CBT Venue Allocation Slip (VAS)</strong>
194 <br /><br />
195 You should login with your student id from Kofa and surname as password.
196 Download and print two copies of this slip and bring them to the
197 allocated CBT examination center.
198 The copies <strong>MUST</strong> be shown to the invigilators
199 before being admitted into the examination hall.
200 <br /><br />
201 How to start examination:<br /><br />
202  * Username:  "student id" from Kofa e.g E1000000<br />
203  * Password: "surname" as shown on this slip in capital letters<br />
204  * Click the course and click "start exam".
205 <br /><br />
206 <strong>WARNING:</strong> Electronic devices (phones, tablets, laptops etc.)
207 are not allowed in the examination hall. Any electronics seized will not
208 be returned. Any student caught charging his/her mobile phone at the CBT
209 centers will be penalized and the exam of such a student will be cancelled.
210 Bags and any foreign materials are not allowed at the venue of
211 the CBT exams. Any omission/other complaints should be reported to the CBT
212 committee through the HoD before the date of examination.
213 <br /><br />
214 Your examination date, time and venue is scheduled as follows:
215 <br /><br />
216 <strong>%s</strong>
217""" % self.context.flash_notice
218        return
219
220
221    def update(self):
222        if not self.context.flash_notice \
223            or not 'exam' in self.context.flash_notice.lower():
224            self.flash(_('Forbidden'), type="warning")
225            self.redirect(self.url(self.context))
226
227    def render(self):
228        studentview = StudentBasePDFFormPage(self.context.student,
229            self.request, self.omit_fields)
230        students_utils = getUtility(IStudentsUtils)
231        return students_utils.renderPDF(
232            self, 'examination_schedule_slip',
233            self.context.student, studentview,
234            omit_fields=self.omit_fields,
235            note=self.note)
236
237class CustomStudentClearanceDisplayFormPage(NigeriaStudentClearanceDisplayFormPage):
238    """ Page to display student clearance data
239    """
240
241    @property
242    def form_fields(self):
243        if self.context.is_postgrad:
244            form_fields = grok.AutoFields(
245                ICustomPGStudentClearance).omit('clearance_locked')
246        else:
247            form_fields = grok.AutoFields(
248                ICustomUGStudentClearance).omit('clearance_locked')
249        if not getattr(self.context, 'officer_comment'):
250            form_fields = form_fields.omit('officer_comment')
251        else:
252            form_fields['officer_comment'].custom_widget = BytesDisplayWidget
253        form_fields = form_fields.omit('def_adm')
254        return form_fields
255
256class CustomStudentClearanceManageFormPage(NigeriaStudentClearanceManageFormPage):
257    """ Page to edit student clearance data
258    """
259
260    @property
261    def form_fields(self):
262        if self.context.is_postgrad:
263            form_fields = grok.AutoFields(
264                ICustomPGStudentClearance).omit('clr_code')
265        else:
266            form_fields = grok.AutoFields(
267                ICustomUGStudentClearance).omit('clr_code')
268        form_fields = form_fields.omit('def_adm')
269        return form_fields
270
271class CustomStudentClearanceEditFormPage(NigeriaStudentClearanceEditFormPage):
272    """ View to edit student clearance data by student
273    """
274
275    @property
276    def form_fields(self):
277        if self.context.is_postgrad:
278            form_fields = grok.AutoFields(ICustomPGStudentClearance).omit(
279            'clearance_locked', 'nysc_location', 'clr_code', 'officer_comment',
280            'physical_clearance_date')
281        else:
282            form_fields = grok.AutoFields(ICustomUGStudentClearanceEdit).omit(
283            'clearance_locked', 'clr_code', 'officer_comment',
284            'physical_clearance_date', 'date_of_birth', 'nationality', 'lga')
285        form_fields = form_fields.omit('def_adm')
286        return form_fields
287
288class CustomStartClearancePage(StartClearancePage):
289    with_ac = False
290
291    @property
292    def all_required_fields_filled(self):
293        if not self.context.email:
294            return _("Email address is missing."), 'edit_base'
295        if not self.context.phone:
296            return _("Phone number is missing."), 'edit_base'
297        if not self.context.father_name:
298            return _("Personal data form is not properly filled."), 'edit_personal'
299        return
300
301class CustomOnlinePaymentDisplayFormPage(NigeriaOnlinePaymentDisplayFormPage):
302    """ Page to view an online payment ticket
303    """
304    grok.context(ICustomStudentOnlinePayment)
305    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
306        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
307    form_fields[
308        'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
309    form_fields[
310        'payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
311
312class CustomPaymentsManageFormPage(PaymentsManageFormPage):
313    """ Page to manage the student payments.
314
315    This manage form page is for both students and students officers.
316    """
317    @property
318    def manage_payments_allowed(self):
319        return checkPermission('waeup.manageStudent', self.context)
320
321class CustomExportPDFPaymentSlip(NigeriaExportPDFPaymentSlip):
322    """Deliver a PDF slip of the context.
323    """
324    grok.context(ICustomStudentOnlinePayment)
325    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
326        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item',
327        'p_split_data', 'p_combi', 'r_card_num', 'r_payment_link',
328        'p_option')
329    form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
330    form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
331
332    @property
333    def note(self):
334        p_session = self.context.p_session
335        try:
336            academic_session = grok.getSite()['configuration'][str(p_session)]
337        except KeyError:
338            academic_session = None
339        text =  '\nThe Amount Authorized is inclusive of: %s Interswitch surcharge (NGN %d).' % (
340            getattr(self.context, 'p_split_data'), self.context.gateway_amt)
341
342        return text
343
344class CustomStudyCourseManageFormPage(StudyCourseManageFormPage):
345    """ Page to edit the student study course data
346    """
347    grok.context(ICustomStudentStudyCourse)
348
349    @property
350    def form_fields(self):
351        if self.context.is_postgrad:
352            form_fields = grok.AutoFields(ICustomStudentStudyCourse).omit(
353                'previous_verdict')
354        else:
355            form_fields = grok.AutoFields(ICustomStudentStudyCourse)
356        form_fields['imported_cgpa'].for_display = True
357        return form_fields
358
359class CustomUnregisterCoursesView(UnregisterCoursesView):
360    """Unregister course list by student
361    """
362    grok.context(ICustomStudentStudyLevel)
363
364    def update(self):
365        if not self.context.__parent__.is_current:
366            emit_lock_message(self)
367            return
368        #try:
369        #    academic_session = grok.getSite()['configuration'][
370        #        str(self.context.level_session)]
371        #    if self.context.student.is_postgrad:
372        #        deadline = academic_session.coursereg_deadline_pg
373        #    elif self.context.student.current_mode.startswith('dp'):
374        #        deadline = academic_session.coursereg_deadline_dp
375        #    elif self.context.student.current_mode in (
376        #        'ug_pt', 'de_pt', 'de_dsh', 'ug_dsh'):
377        #        deadline = academic_session.coursereg_deadline_pt
378        #    elif self.context.student.current_mode == 'found':
379        #        deadline = academic_session.coursereg_deadline_found
380        #    elif self.context.student.current_mode == 'bridge':
381        #        deadline = academic_session.coursereg_deadline_bridge
382        #    else:
383        #        deadline = academic_session.coursereg_deadline
384        #except (TypeError, KeyError):
385        #    deadline = None
386        #if deadline and deadline < datetime.now(pytz.utc):
387        #    self.flash(_(
388        #        "Course registration has ended. "
389        #        "Unregistration is disabled."), type="danger")
390        if str(self.context.__parent__.current_level) != self.context.__name__:
391            self.flash(_('This is not your current level.'), type="danger")
392        elif self.context.student.state == REGISTERED:
393            IWorkflowInfo(self.context.student).fireTransition('reset7')
394            message = _('Course list has been unregistered.')
395            self.flash(message)
396        else:
397            self.flash(_('You are in the wrong state.'), type="warning")
398        self.redirect(self.url(self.context))
399        return
400
401class CustomStudyLevelDisplayFormPage(StudyLevelDisplayFormPage):
402    """ Page to display student study levels
403    """
404    grok.context(ICustomStudentStudyLevel)
405    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
406        'total_credits', 'gpa', 'level', 'imported_gpa', 'imported_cgpa')
407    form_fields[
408        'validation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
409
410    @property
411    def translated_values(self):
412        return translated_values(self)
413
414    @property
415    def show_results(self):
416        isStudent = getattr(
417            self.request.principal, 'user_type', None) == 'student'
418        try:
419            show_results = grok.getSite()[
420                'configuration'][str(self.context.level_session)].show_results
421        except KeyError:
422            return False
423        if isStudent and self.context.student.current_mode not in show_results:
424            return False
425        #if isStudent and self.context.student.state != RETURNING \
426        #    and self.context.student.current_level == self.context.level:
427        #    return False
428        return True
429
430class CustomStudyLevelManageFormPage(StudyLevelManageFormPage):
431    """ Page to edit the student study level data
432    """
433    grok.context(ICustomStudentStudyLevel)
434
435    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
436        'validation_date', 'validated_by', 'total_credits', 'gpa', 'level',
437        'total_credits_s1', 'total_credits_s2')
438
439    form_fields['imported_gpa'].for_display = True
440    form_fields['imported_cgpa'].for_display = True
441
442class CustomStudyLevelEditFormPage(StudyLevelEditFormPage):
443    """ Page to edit the student study level data by students
444    """
445    grok.context(ICustomStudentStudyLevel)
446
447class StudyLevelRepairFormPage(KofaEditFormPage):
448    """ Page to repair the student study level data by students
449    """
450    grok.context(IStudentStudyLevel)
451    grok.name('repair')
452    grok.require('waeup.editStudyLevel')
453    grok.template('studylevelrepairpage')
454    pnav = 4
455    placeholder = _('Enter valid course code')
456
457    def update(self, ADD=None, course=None):
458        if not self.context.__parent__.is_current \
459            or self.context.student.studycourse_locked:
460            emit_lock_message(self)
461            return
462        try:
463            studylevel_repair_enabled = grok.getSite()['configuration'][
464                str(self.context.level_session)].studylevel_repair_enabled
465        except KeyError:
466            emit_lock_message(self)
467            return
468        if not studylevel_repair_enabled:
469            emit_lock_message(self)
470            return
471        super(StudyLevelRepairFormPage, self).update()
472        if ADD is not None:
473            if not course:
474                self.flash(_('No valid course code entered.'), type="warning")
475                return
476            cat = queryUtility(ICatalog, name='courses_catalog')
477            result = cat.searchResults(code=(course, course))
478            if len(result) != 1:
479                self.flash(_('Course not found.'), type="warning")
480                return
481            course = list(result)[0]
482            addCourseTicket(self, course)
483        return
484
485    @property
486    def label(self):
487        # Here we know that the cookie has been set
488        lang = self.request.cookies.get('kofa.language')
489        level_title = translate(self.context.level_title, 'waeup.kofa',
490            target_language=lang)
491        return _('Repair course list of ${a}',
492            mapping = {'a':level_title})
493
494    @property
495    def translated_values(self):
496        return translated_values(self)
497
498    def _delCourseTicket(self, **data):
499        form = self.request.form
500        if 'val_id' in form:
501            child_id = form['val_id']
502        else:
503            self.flash(_('No ticket selected.'), type="warning")
504            self.redirect(self.url(self.context, '@@edit'))
505            return
506        if not isinstance(child_id, list):
507            child_id = [child_id]
508        deleted = []
509        for id in child_id:
510            # Students are not allowed to remove core tickets
511            if id in self.context and \
512                self.context[id].removable_by_student:
513                del self.context[id]
514                deleted.append(id)
515        if len(deleted):
516            self.flash(_('Successfully removed: ${a}',
517                mapping = {'a':', '.join(deleted)}))
518            self.context.writeLogMessage(
519                self,'removed: %s at %s' %
520                (', '.join(deleted), self.context.level))
521        self.redirect(self.url(self.context, u'@@repair'))
522        return
523
524    @jsaction(_('Remove selected tickets'))
525    def delCourseTicket(self, **data):
526        self._delCourseTicket(**data)
527        return
528
529class CustomExportPDFCourseRegistrationSlip(
530    NigeriaExportPDFCourseRegistrationSlip):
531    """Deliver a PDF slip of the context.
532    """
533    grok.context(ICustomStudentStudyLevel)
534    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
535        'level_session', 'level_verdict',
536        'validated_by', 'validation_date', 'gpa', 'level',
537        'imported_gpa', 'imported_cgpa', 'transcript_remark')
538
539    omit_fields = ('password', 'suspended', 'suspended_comment',
540        'phone', 'adm_code', 'sex', 'email', 'date_of_birth',
541        'department', 'current_mode', 'current_level', 'flash_notice',
542        )
543
544    @property
545    def show_results(self):
546        isStudent = getattr(
547            self.request.principal, 'user_type', None) == 'student'
548        try:
549            show_results = grok.getSite()[
550                'configuration'][str(self.context.level_session)].show_results
551        except KeyError:
552            return False
553        if isStudent and self.context.student.current_mode not in show_results:
554            return False
555        if isStudent and self.context.student.state != RETURNING \
556            and self.context.student.current_level == self.context.level:
557            return False
558        return True
559
560    def update(self):
561        if self.context.student.state != REGISTERED \
562            and self.context.student.current_level == self.context.level:
563            self.flash(_('Forbidden'), type="warning")
564            self.redirect(self.url(self.context))
565            return
566
567    @property
568    def label(self):
569        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
570        lang = self.request.cookies.get('kofa.language', portal_language)
571        level_title = translate(self.context.level_title, 'waeup.kofa',
572            target_language=lang)
573        line0 = ''
574        if self.context.student.is_postgrad:
575            line0 = 'SCHOOL OF POSTGRADUATE STUDIES\n'
576        elif self.context.student.current_mode.endswith('_pt'):
577            line0 = 'DIRECTORATE OF PART-TIME DEGREE PROGRAMMES\n'
578        line1 = translate(_('Course Registration Slip'),
579            target_language=portal_language) \
580            + ' %s' % level_title
581        line2 = translate(_('Session'),
582            target_language=portal_language) \
583            + ' %s' % self.context.getSessionString
584        return '%s%s\n%s' % (line0, line1, line2)
585
586    @property
587    def title(self):
588        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
589        return translate(_('Units Registered'), target_language=portal_language)
590
591    def _signatures(self):
592        if self.context.student.current_mode in (
593            'ug_pt', 'de_pt', 'ug_dsh', 'de_dsh', 'found'):
594            return (
595                [('I have selected the course on the advise of my Head of '
596                 'Department. <br>', _('Student\'s Signature'), '<br>')],
597                [('This student has satisfied the department\'s requirements. '
598                 'I recommend to approve the course registration. <br>',
599                 _('Head of Department\'s Signature'), '<br>')],
600                [('' , _('Deputy Registrar\'s Signature'), '<br>')],
601                [('', _('Director\'s Signature'))]
602                )
603        if self.context.student.current_mode in (
604            'de_ft', 'ug_ft', 'dp_ft', 'transfer', 'bridge'):
605            return ([_('Academic Adviser\'s Signature'),
606                _('Faculty Officer\'s Signature'),
607                _('Student\'s Signature')],)
608
609        if self.context.student.current_mode in ('special_pg_ft', 'special_pg_pt'):
610            return (
611                [('I declare that all items of information supplied above are correct:' ,
612                    _('Student\'s Signature'), '<br>')],
613                [('We approved the above registration:',
614                    _('Major Supervisor (Name / Signature)'), '')],
615                [('', _('Co-Supervisor (Name / Signature)'), '')],
616                [('', _('Head of Department'), '<br>')],
617                [('The student has satisfied the conditions for renewal of '
618                  'registration for graduate school programme in this university:',
619                  _('Secretary <br /> (School of Postgraduate Studies)'), '')],
620                [('', _('Dean <br /> (School of Postgraduate Studies)'), '')],
621                )
622        return None
623
624
625    def render(self):
626        if not self.context.student.current_mode:
627            self.flash('No certificate assigned.', type="danger")
628            self.redirect(self.url(self.context))
629            return
630        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
631        Sem = translate(_('Term'), target_language=portal_language)
632        Code = translate(_('Code'), target_language=portal_language)
633        Title = translate(_('Title'), target_language=portal_language)
634        Cred = translate(_('Cred.'), target_language=portal_language)
635        CC = translate(_('Cat.'), target_language=portal_language)
636        if self.show_results:
637            TotalScore = translate(_('Total Score'), target_language=portal_language)
638            #CA = translate(_('CA'), target_language=portal_language)
639            Grade = translate(_('Grade'), target_language=portal_language)
640        Signature = translate(_('Lecturer\'s Signature'), 'waeup.aaue',
641            target_language=portal_language)
642        studentview = StudentBasePDFFormPage(self.context.student,
643            self.request, self.omit_fields)
644        students_utils = getUtility(IStudentsUtils)
645
646        tabledata = []
647        tableheader = []
648        contenttitle = []
649        for i in range(1,7):
650            tabledata.append(sorted(
651                [value for value in self.context.values()
652                 if value.semester == i and not value.outstanding],
653                 key=lambda value: str(value.semester) + value.code))
654            if self.show_results:
655                tableheader.append([(Code,'code', 2.0),
656                                   (Title,'title', 7),
657                                   (Cred, 'credits', 1.4),
658                                   (CC, 'course_category', 1.2),
659                                   (TotalScore, 'total_score', 1.4),
660                                   #(CA, 'ca', 1.4),
661                                   (Grade, 'grade', 1.4),
662                                   (Signature, 'dummy', 3),
663                                   ])
664            else:
665                tableheader.append([(Code,'code', 2.0),
666                                   (Title,'title', 7),
667                                   (Cred, 'credits', 1.5),
668                                   (CC, 'course_category', 1.2),
669                                   (Signature, 'dummy', 3),
670                                   ])
671        if len(self.label.split('\n')) == 3:
672            topMargin = 1.9
673        elif len(self.label.split('\n')) == 2:
674            topMargin = 1.7
675        else:
676            topMargin = 1.5
677        return students_utils.renderPDF(
678            self, 'course_registration_slip.pdf',
679            self.context.student, studentview,
680            tableheader=tableheader,
681            tabledata=tabledata,
682            signatures=self._signatures(),
683            topMargin=topMargin,
684            omit_fields=self.omit_fields
685            )
686
687class CustomStudyCourseTranscriptPage(StudyCourseTranscriptPage):
688    """ Page to display the student's transcript.
689    """
690    grok.require('waeup.viewStudent')
691
692class CustomExportPDFTranscriptSlip(ExportPDFTranscriptSlip):
693    """Deliver a PDF slip of the context.
694    """
695
696    note = _("""
697<br /><br /><br /><br />
698<font size='10'>
699<strong>Note:</strong> This copy is subject to correction for typographical errors and ratification by the departmental board.
700</font>
701""")
702
703    def _sigsInFooter(self):
704        return []
705
706    def _signatures(self):
707
708        #if self.context.student.state == GRADUATED:
709        #    return ([(
710        #        'O.O OHIKHENA (Manupa)<br />'
711        #        'Principal Asst. Registrar<br /> '
712        #        'Exams, Records and Data Processing Division <br /> '
713        #        'For: Registrar')],)
714        #elif self.context.certificate is not None:
715        #    dep = self.context.certificate.__parent__.__parent__
716        #    if dep.officer_1:
717        #        return ([(dep.officer_1)],)
718        #return
719
720        return ([(
721            'F.O. Omoigui, MANUPA<br />'
722            'Principal Assistant Registrar<br /> '
723            'Exams, Records and Data Processing Division <br /> '
724            'For: Registrar')],)
725
726    def render(self):
727        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
728        Term = translate(_('Term'), target_language=portal_language)
729        Code = translate(_('Code'), target_language=portal_language)
730        Title = translate(_('Title'), target_language=portal_language)
731        Cred = translate(_('Credits'), target_language=portal_language)
732        Score = translate(_('Score'), target_language=portal_language)
733        Grade = translate(_('Grade'), target_language=portal_language)
734        studentview = StudentBasePDFFormPage(self.context.student,
735            self.request, self.omit_fields)
736        students_utils = getUtility(IStudentsUtils)
737
738        tableheader = [(Code,'code', 2.5),
739                         (Title,'title', 7),
740                         (Term, 'semester', 1.5),
741                         (Cred, 'credits', 1.5),
742                         (Score, 'total_score', 1.5),
743                         (Grade, 'grade', 1.5),
744                         ]
745
746        pdfstream = students_utils.renderPDFTranscript(
747            self, 'transcript.pdf',
748            self.context.student, studentview,
749            omit_fields=self.omit_fields,
750            tableheader=tableheader,
751            signatures=self._signatures(),
752            sigs_in_footer=self._sigsInFooter(),
753            digital_sigs=self._digital_sigs(),
754            save_file=self._save_file(),
755            )
756        if not pdfstream:
757            self.redirect(self.url(self.context.student))
758            return
759        return pdfstream
760
761class ExportPDFTranscriptStudViewSlip(CustomExportPDFTranscriptSlip):
762    """Deliver a PDF slip of the context.
763    """
764
765    grok.name('transcript_studview.pdf')
766
767    def render(self):
768        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
769        Term = translate(_('Term'), target_language=portal_language)
770        Code = translate(_('Code'), target_language=portal_language)
771        Title = translate(_('Title'), target_language=portal_language)
772        Cred = translate(_('Credits'), target_language=portal_language)
773        #Score = translate(_('Score'), target_language=portal_language)
774        Grade = translate(_('Grade'), target_language=portal_language)
775        studentview = StudentBasePDFFormPage(self.context.student,
776            self.request, self.omit_fields)
777        students_utils = getUtility(IStudentsUtils)
778
779        tableheader = [(Code,'code', 2.5),
780                         (Title,'title', 7),
781                         (Term, 'semester', 1.5),
782                         (Cred, 'credits', 1.5),
783                         #(Score, 'total_score', 1.5),
784                         (Grade, 'grade', 1.5),
785                         ]
786
787        pdfstream = students_utils.renderPDFTranscript(
788            self, 'transcript.pdf',
789            self.context.student, studentview,
790            omit_fields=self.omit_fields,
791            tableheader=tableheader,
792            signatures=self._signatures(),
793            sigs_in_footer=self._sigsInFooter(),
794            digital_sigs=self._digital_sigs(),
795            save_file=self._save_file(),
796            )
797        if not pdfstream:
798            self.redirect(self.url(self.context.student))
799            return
800        return pdfstream
801
802class CustomExportPDFAdmissionSlip(ExportPDFAdmissionSlip):
803    """Deliver a PDF Admission slip.
804    """
805
806    @property
807    def label(self):
808        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
809        return translate(_('e-Admission Slip \n'),
810            target_language=portal_language) \
811            + ' %s' % self.context.display_fullname
812
813class CustomExportPDFClearanceSlip(NigeriaExportPDFClearanceSlip):
814    """Deliver a PDF slip of the context.
815    """
816
817    @property
818    def label(self):
819        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
820        return translate(_('Verification/Clearance Slip\n'),
821            target_language=portal_language) \
822            + ' %s' % self.context.display_fullname
823
824    @property
825    def form_fields(self):
826        if self.context.is_postgrad:
827            form_fields = grok.AutoFields(
828                ICustomPGStudentClearance).omit('clearance_locked')
829        else:
830            form_fields = grok.AutoFields(
831                ICustomUGStudentClearance).omit('clearance_locked')
832        if not getattr(self.context, 'officer_comment'):
833            form_fields = form_fields.omit('officer_comment')
834        form_fields = form_fields.omit('def_adm')
835        return form_fields
836
837class StudentGetMatricNumberPage(UtilityView, grok.View):
838    """ Construct and set the matriculation number.
839    """
840    grok.context(IStudent)
841    grok.name('get_matric_number')
842    grok.require('waeup.viewStudent')
843
844    def update(self):
845        students_utils = getUtility(IStudentsUtils)
846        msg, mnumber = students_utils.setMatricNumber(self.context)
847        if msg:
848            self.flash(msg, type="danger")
849        else:
850            self.flash(_('Matriculation number %s assigned.' % mnumber))
851            self.context.writeLogMessage(self, '%s assigned' % mnumber)
852        self.redirect(self.url(self.context))
853        return
854
855    def render(self):
856        return
857
858class ExportPDFMatricNumberSlip(UtilityView, grok.View):
859    """Deliver a PDF notification slip.
860    """
861    grok.context(ICustomStudent)
862    grok.name('matric_number_slip.pdf')
863    grok.require('waeup.viewStudent')
864    prefix = 'form'
865
866    form_fields = grok.AutoFields(ICustomStudent).select(
867        'student_id', 'matric_number')
868    omit_fields = ('date_of_birth', 'current_level', 'flash_notice')
869
870    @property
871    def title(self):
872        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
873        return translate(_('Matriculation Number'), 'waeup.kofa',
874            target_language=portal_language)
875
876    @property
877    def label(self):
878        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
879        return translate(_('Matriculation Number Slip\n'),
880            target_language=portal_language) \
881            + ' %s' % self.context.display_fullname
882
883    def render(self):
884        if self.context.state not in (PAID,) or not self.context.is_fresh \
885            or not self.context.matric_number:
886            self.flash('Not allowed.', type="danger")
887            self.redirect(self.url(self.context))
888            return
889        students_utils = getUtility(IStudentsUtils)
890        pre_text = _('Congratulations! Your acceptance fee and school fees ' +
891                     'payments have been received and your matriculation ' +
892                     'number generated with details as follows.')
893        return students_utils.renderPDFAdmissionLetter(self,
894            self.context.student, omit_fields=self.omit_fields,
895            pre_text=pre_text, post_text='')
896
897class ExportPersonalDataSlip(UtilityView, grok.View):
898    """Deliver a PDF notification slip.
899    """
900    grok.context(ICustomStudent)
901    grok.name('personal_data_slip.pdf')
902    grok.require('waeup.viewStudent')
903    prefix = 'form'
904    note = None
905
906    form_fields = grok.AutoFields(ICustomStudentPersonal).omit('personal_updated')
907    omit_fields = ('suspended', 'suspended_comment', 'adm_code',
908                   'certificate', 'flash_notice')
909
910    @property
911    def title(self):
912        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
913        return translate(_('Personal Data'), 'waeup.kofa',
914            target_language=portal_language)
915
916    @property
917    def label(self):
918        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
919        return translate(_('Personal Data Slip\n'),
920            target_language=portal_language) \
921            + ' %s' % self.context.display_fullname
922
923    def render(self):
924        studentview = StudentBasePDFFormPage(self.context.student,
925            self.request, self.omit_fields)
926        students_utils = getUtility(IStudentsUtils)
927        return students_utils.renderPDF(self, 'personal_data_slip.pdf',
928            self.context.student, studentview, note=self.note,
929            omit_fields=self.omit_fields)
930
931class CustomAccommodationDisplayFormPage(NigeriaAccommodationDisplayFormPage):
932    """ Page to view bed tickets.
933    """
934    with_hostel_selection = True
935
936class CustomAccommodationManageFormPage(NigeriaAccommodationManageFormPage):
937    """ Page to manage bed tickets.
938    This manage form page is for both students and students officers.
939    """
940    with_hostel_selection = True
941
942class CustomBedTicketAddPage(NigeriaBedTicketAddPage):
943    with_ac = False
944
945class CustomStudentFilesUploadPage(StudentFilesUploadPage):
946    """ View to upload files by student. Inherit from same class in
947    base package, not from kofacustom.nigeria which
948    requires that no application slip exists.
949    """
950
951class CustomCourseTicketDisplayFormPage(CourseTicketDisplayFormPage):
952    """ Page to display course tickets
953    """
954
955    @property
956    def show_results(self):
957        isStudent = getattr(
958            self.request.principal, 'user_type', None) == 'student'
959        if isStudent:
960            return False
961        return True
962
963    @property
964    def form_fields(self):
965        if self.show_results:
966            return grok.AutoFields(ICustomCourseTicket)
967        else:
968            return grok.AutoFields(ICustomCourseTicket).omit('score').omit('ca')
969
970class CustomCourseTicketManageFormPage(CourseTicketManageFormPage):
971    """ Page to manage course tickets
972    """
973    form_fields = grok.AutoFields(ICustomCourseTicket)
974    form_fields['title'].for_display = True
975    form_fields['fcode'].for_display = True
976    form_fields['dcode'].for_display = True
977    form_fields['semester'].for_display = True
978    form_fields['passmark'].for_display = True
979    form_fields['credits'].for_display = True
980    form_fields['mandatory'].for_display = False
981    form_fields['automatic'].for_display = True
982    form_fields['carry_over'].for_display = True
983    form_fields['ticket_session'].for_display = True
984    form_fields['imported_ts'].for_display = True
985
986class CustomEditScoresPage(EditScoresPage):
987    """Page that filters and lists students.
988    """
989    grok.template('editscorespage')
990
991    def _searchCatalog(self, session):
992        cat = queryUtility(ICatalog, name='coursetickets_catalog')
993        coursetickets = cat.searchResults(
994            session=(session, session),
995            code=(self.context.code, self.context.code)
996            )
997        try:
998            score_editing_enabled = grok.getSite()[
999                'configuration'][str(session)].score_editing_enabled
1000        except KeyError:
1001            return []
1002        coursetickets_list = [courseticket for courseticket in coursetickets
1003            if courseticket.student.current_mode in score_editing_enabled]
1004        return coursetickets_list
1005
1006    def _extract_uploadfile(self, uploadfile):
1007        """Get a mapping of student-ids to scores.
1008
1009        The mapping is constructed by reading contents from `uploadfile`.
1010
1011        We expect uploadfile to be a regular CSV file with columns
1012        ``student_id``, ``score``, ``imported_ts``
1013        and ``ca`` (other cols are ignored).
1014        """
1015        result = dict()
1016        data = StringIO(uploadfile.read())  # ensure we have something seekable
1017        reader = csv.DictReader(data)
1018        for row in reader:
1019            if not ('student_id' in row and 'score' in row and 'ca' in row and
1020                'imported_ts' in row):
1021                continue
1022            result[row['student_id']] = (
1023                row['score'], row['ca'], row['imported_ts'])
1024        return result
1025
1026    def _update_scores(self, form):
1027        ob_class = self.__implemented__.__name__.replace('waeup.kofa.', '')
1028        error = ''
1029        if 'UPDATE_FILE' in form:
1030            if form['uploadfile']:
1031                try:
1032                    formvals = self._extract_uploadfile(form['uploadfile'])
1033                except:
1034                    self.flash(
1035                        _('Uploaded file contains illegal data. Ignored'),
1036                        type="danger")
1037                    return False
1038            else:
1039                self.flash(
1040                    _('No file provided.'), type="danger")
1041                return False
1042        else:
1043            formvals = dict(zip(form['sids'], zip(
1044                form['scores'], form['cas'], form['imported_ts'])))
1045        for ticket in self.editable_tickets:
1046            ticket_error = False
1047            score = ticket.score
1048            ca = ticket.ca
1049            imported_ts = ticket.imported_ts
1050            sid = ticket.student.student_id
1051            if sid not in formvals:
1052                continue
1053            if formvals[sid][0] == '':
1054                score = None
1055            if formvals[sid][1] == '':
1056                ca = None
1057            if formvals[sid][2] == '':
1058                imported_ts = None
1059            try:
1060                if formvals[sid][0]:
1061                    score = int(formvals[sid][0])
1062                if formvals[sid][1]:
1063                    ca = int(formvals[sid][1])
1064                if formvals[sid][2]:
1065                    imported_ts = int(formvals[sid][2])
1066            except ValueError:
1067                error += '%s, ' % ticket.student.display_fullname
1068                ticket_error = True
1069            if score and ca and not ticket_error and score + ca > 100:
1070                error += '%s, ' % ticket.student.display_fullname
1071                ticket_error = True
1072            if not ticket_error and ticket.score != score:
1073                try:
1074                    ticket.score = score
1075                except TooBig:
1076                    error += '%s, ' % ticket.student.display_fullname
1077                    ticket_error = True
1078                    pass
1079                ticket.student.__parent__.logger.info(
1080                    '%s - %s %s/%s score updated (%s)' %
1081                    (ob_class, ticket.student.student_id,
1082                     ticket.level, ticket.code, score))
1083            if not ticket_error and ticket.ca != ca:
1084                try:
1085                    ticket.ca = ca
1086                except TooBig:
1087                    error += '%s, ' % ticket.student.display_fullname
1088                    pass
1089                ticket.student.__parent__.logger.info(
1090                    '%s - %s %s/%s ca updated (%s)' %
1091                    (ob_class, ticket.student.student_id,
1092                     ticket.level, ticket.code, ca))
1093            if not ticket_error and ticket.imported_ts != imported_ts:
1094                try:
1095                    ticket.imported_ts = imported_ts
1096                except TooBig:
1097                    error += '%s, ' % ticket.student.display_fullname
1098                    pass
1099                ticket.student.__parent__.logger.info(
1100                    '%s - %s %s/%s imported_ts updated (%s)' %
1101                    (ob_class, ticket.student.student_id,
1102                     ticket.level, ticket.code, imported_ts))
1103        if error:
1104            self.flash(_('Error: Score(s), CA(s) and Imported TS(s) of %s have not be updated. '
1105              % error.strip(', ')), type="danger")
1106        return True
1107
1108class EditPreviousSessionScoresPage(CustomEditScoresPage):
1109
1110    grok.name('edit_prev_scores')
1111
1112    def update(self,  *args, **kw):
1113        form = self.request.form
1114        self.current_academic_session = grok.getSite()[
1115            'configuration'].current_academic_session
1116        if self.context.__parent__.__parent__.score_editing_disabled:
1117            self.flash(_('Score editing disabled.'), type="warning")
1118            self.redirect(self.url(self.context))
1119            return
1120        if not self.current_academic_session:
1121            self.flash(_('Current academic session not set.'), type="warning")
1122            self.redirect(self.url(self.context))
1123            return
1124        previous_session = self.current_academic_session - 1
1125        self.session_title = academic_sessions_vocab.getTerm(
1126            previous_session).title
1127        self.tickets = self._searchCatalog(previous_session)
1128        if not self.tickets:
1129            self.flash(_('No student found.'), type="warning")
1130            self.redirect(self.url(self.context))
1131            return
1132        self.editable_tickets = [
1133            ticket for ticket in self.tickets if ticket.editable_by_lecturer]
1134        if not 'UPDATE_TABLE' in form and not 'UPDATE_FILE' in form:
1135            return
1136        if not self.editable_tickets:
1137            return
1138        success = self._update_scores(form)
1139        if success:
1140            self.flash(_('You successfully updated course results.'))
1141        return
1142
1143class CustomExportPDFScoresSlip(ExportPDFScoresSlip):
1144    """Deliver a PDF slip of course tickets for a lecturer.
1145    """
1146
1147    note = u'\nUpgraded scores are with asterisks.'
1148
1149    def data(self, session):
1150        #site = grok.getSite()
1151        cat = queryUtility(ICatalog, name='coursetickets_catalog')
1152        coursetickets = cat.searchResults(
1153            session=(session, session),
1154            code=(self.context.code, self.context.code)
1155            )
1156        # Apply filter
1157        try:
1158            score_editing_enabled = grok.getSite()[
1159                'configuration'][str(session)].score_editing_enabled
1160            if checkPermission('waeup.manageAcademics', self.context):
1161                score_editing_enabled = True
1162            coursetickets_filtered = [courseticket
1163                for courseticket in coursetickets
1164                if (checkPermission('waeup.manageAcademics', self.context)
1165                    or (courseticket.student.current_mode in
1166                        score_editing_enabled))
1167                and courseticket.total_score is not None
1168                and courseticket.__parent__.__parent__.is_current]
1169        except KeyError:
1170            coursetickets_filtered = coursetickets
1171        # In AAUE only editable tickets can be printed (deactivated on 02/10/19)
1172        #editable_tickets = [
1173        #    ticket for ticket in coursetickets_filtered
1174        #    if ticket.editable_by_lecturer]
1175        header = [[_(''),
1176                   _('Student Id'),
1177                   _('Matric No.'),
1178                   #_('Reg. No.'),
1179                   #_('Fullname'),
1180                   #_('Status'),
1181                   #_('Course of\nStudies'),
1182                   _('Department'),
1183                   _('Level'),
1184                   _(' CA  '),
1185                   _('Exam\nScore'),
1186                   _('Total '),
1187                   _('Grade'),
1188                   ],]
1189        sorted_tickets = sorted(coursetickets_filtered, # editable_tickets,
1190            key=lambda ticket: str(ticket.student.depcode)
1191                               + str(ticket.student.faccode)
1192                               + str(ticket.student.matric_number)
1193                               )
1194        no = 1
1195        tickets = []
1196        passed = 0
1197        failed = 0
1198        with_ca = False
1199        grade_stats = {'A':0, 'B':0, 'C':0, 'D':0, 'E':0, 'F':0, }
1200        for ticket in sorted_tickets:
1201            if ticket.ca > 0:
1202                with_ca = True
1203            total = ticket.total_score
1204            if getattr(ticket, 'imported_ts', None):
1205                total = "**%s**" % ticket.imported_ts
1206            grade = ticket._getGradeWeightFromScore[0]
1207            if grade in grade_stats.keys():
1208                grade_stats[grade] += 1
1209            if grade in ('F', '-'):
1210                failed += 1
1211            else:
1212                passed += 1
1213            fullname = textwrap.fill(ticket.student.display_fullname, 30)
1214            #deptitle = site['faculties'][ticket.student.faccode][
1215            #    ticket.student.depcode].longtitle
1216            row = [str(no),
1217                  ticket.student.student_id,
1218                  ticket.student.matric_number,
1219                  #ticket.student.reg_number,
1220                  #fullname,
1221                  #ticket.student.translated_state,
1222                  #ticket.student.certcode,
1223                  str(ticket.student.faccode) + ' / ' + str(ticket.student.depcode),
1224                  ticket.level,
1225                  ticket.ca,
1226                  ticket.score,
1227                  total,
1228                  grade,
1229                  ]
1230            tickets.append(row)
1231            no += 1
1232        total = passed + failed
1233        passed_perc = 0
1234        failed_perc = 0
1235        if total:
1236            passed_perc = round(100.0 * passed / total)
1237            failed_perc = round(100.0 * failed / total)
1238        dep = self.context.__parent__.__parent__.longtitle
1239        fac = self.context.__parent__.__parent__.__parent__.longtitle
1240        # remove CA column if not necessary
1241        if not with_ca:
1242            header = [[_(''),
1243                       _('Student Id'),
1244                       _('Matric No.'),
1245                       #_('Reg. No.'),
1246                       #_('Fullname'),
1247                       #_('Status'),
1248                       #_('Course of\nStudies'),
1249                       _('Department'),
1250                       _('Level'),
1251                       #_(' CA  '),
1252                       _('Exam\nScore'),
1253                       _('Total '),
1254                       _('Grade'),
1255                       ],]
1256            for ticket in tickets:
1257                del(ticket[5])
1258        return header + tickets, [
1259            dep, fac, total, passed, passed_perc, failed, failed_perc, grade_stats]
1260
1261    def render(self):
1262        session = grok.getSite()['configuration'].current_academic_session
1263        lecturers = [i['user_title'] for i in self.getUsersWithLocalRoles()
1264                     if i['local_role'] == 'waeup.local.Lecturer']
1265        lecturers = sorted(lecturers)
1266        lecturers =  ', '.join(lecturers)
1267        students_utils = getUtility(IStudentsUtils)
1268        # only orientation is different
1269        return students_utils.renderPDFCourseticketsOverview(
1270            self, 'coursetickets',
1271            session, self.data(session), lecturers, '', 45, self.note)
1272
1273class DownloadPreviousSessionScoresView(DownloadScoresView):
1274    """View that exports scores.
1275    """
1276    grok.name('download_prev_scores')
1277
1278    def update(self):
1279        self.current_academic_session = grok.getSite()[
1280            'configuration'].current_academic_session
1281        if self.context.__parent__.__parent__.score_editing_disabled:
1282            self.flash(_('Score editing disabled.'), type="warning")
1283            self.redirect(self.url(self.context))
1284            return
1285        if not self.current_academic_session:
1286            self.flash(_('Current academic session not set.'), type="warning")
1287            self.redirect(self.url(self.context))
1288            return
1289        site = grok.getSite()
1290        exporter = getUtility(ICSVExporter, name='lecturer')
1291        self.csv = exporter.export_filtered(site, filepath=None,
1292                                 catalog='coursetickets',
1293                                 session=self.current_academic_session-1,
1294                                 level=None,
1295                                 code=self.context.code)
1296        return
1297
1298class AlumniRequestPasswordPage(StudentRequestPasswordPage):
1299    """Captcha'd request password page for students.
1300    """
1301    grok.name('alumni_requestpw')
1302    grok.require('waeup.Anonymous')
1303    grok.template('alumni_requestpw')
1304    form_fields = grok.AutoFields(IStudentRequestPW).select(
1305        'lastname','number','email')
1306    label = _('Search student record and send password for first-time login')
1307
1308    def _redirect_no_student(self):
1309        self.flash(_('No student record found.'), type="warning")
1310        self.redirect(self.application_url() + '/applicants/trans2017/register')
1311        return
1312
1313class CustomAddStudyLevelFormPage(AddStudyLevelFormPage):
1314    """ This page is temporarily locked.
1315    """
1316    def update(self):
1317        if self.context.student.current_mode == 'ug_ft' \
1318            and self.context.student.current_session == 2021 \
1319            and not self.context.student.entry_session == 2021:
1320            emit_lock_message(self)
1321            return
1322        super(CustomAddStudyLevelFormPage, self).update()
1323        return
Note: See TracBrowser for help on using the repository browser.