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

Last change on this file since 15976 was 15976, checked in by Henrik Bettermann, 5 years ago

Change sports and library fee payment setting.

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