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

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

Temporarily disable course list creation by student.

Attention: 2 tests are failing and 7 tests have errors.

  • Property svn:keywords set to Id
File size: 51.3 KB
Line 
1## $Id: browser.py 15864 2019-12-02 23:20:29Z 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
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    NigeriaStudentBaseDisplayFormPage,
76    NigeriaStudentBaseManageFormPage,
77    NigeriaBedTicketAddPage
78    )
79from waeup.aaue.students.interfaces import (
80    ICustomStudentOnlinePayment,
81    ICustomStudentStudyLevel,
82    ICustomStudent,
83    ICustomStudentPersonal,
84    ICustomStudentPersonalEdit,
85    ICustomUGStudentClearance,
86    ICustomUGStudentClearanceEdit,
87    ICustomPGStudentClearance,
88    ICustomCourseTicket,
89    ICustomStudentBase,
90    ICustomStudentStudyCourse)
91from waeup.aaue.interswitch.browser import gateway_net_amt
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')
328    form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
329    form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
330
331    @property
332    def note(self):
333        p_session = self.context.p_session
334        try:
335            academic_session = grok.getSite()['configuration'][str(p_session)]
336        except KeyError:
337            academic_session = None
338        text =  '\n\n The Amount Authorized is inclusive of: '
339        if self.context.p_category in ('schoolfee_incl', 'schoolfee_1') \
340            and academic_session:
341            #welfare_fee = gateway_net_amt(academic_session.welfare_fee)
342            #union_fee = gateway_net_amt(academic_session.union_fee)
343            if self.context.student.entry_session == 2016 \
344                and self.context.student.entry_mode == 'ug_ft' \
345                and self.context.p_session == 2016:
346                # Add student id card fee to first school fee payment.
347
348                ## Attention: The payment slip does not contain any information
349                ## whether the fee was added or not.
350                ## We can only draw conclusions from from the student's entry
351                ## session whether the fee had been included.
352                #id_card_fee = gateway_net_amt(academic_session.id_card_fee)
353                #text += ('School Fee, '
354                #         '%s Naira Student ID Card Fee, '
355                #         '%s Naira Student Union Dues, '
356                #         '%s Naira Student Welfare Assurance Fee and '
357                #         % (id_card_fee, union_fee, welfare_fee))
358
359                text += ('School Fee, '
360                         'Student ID Card Fee, '
361                         'Student Union Dues, '
362                         'Sports Fee, '
363                         'Library Levy, '
364                         'Student Welfare Assurance Fee and ')
365            else:
366
367                #text += ('School Fee, '
368                #         '%s Naira Student Union Dues, '
369                #         '%s Naira Student Welfare Assurance Fee and '
370                #         % (union_fee, welfare_fee))
371
372                text += ('School Fee, '
373                         'Student Union Dues, '
374                         'Sports Development Fee, '
375                         'Library Development Levy, '
376                         'Student Welfare Assurance Fee and ')
377        elif self.context.p_category in (
378            'clearance_incl', 'clearance_medical_incl') and academic_session:
379
380            #matric_gown_fee = gateway_net_amt(academic_session.matric_gown_fee)
381            #lapel_fee = gateway_net_amt(academic_session.lapel_fee)
382            #text += ('Acceptance Fee, '
383            #         '%s Naira Matriculation Gown Fee, '
384            #         '%s Naira Lapel/File Fee and '
385            #         % (matric_gown_fee, lapel_fee))
386
387            text += ('Acceptance Fee, '
388                     'Matriculation Gown Fee, '
389                     'Lapel/File Fee and ')
390
391        #return text + '250.0 Naira Transaction Charge.'
392
393        return text + 'Transaction Charge.'
394
395class CustomStudyCourseManageFormPage(StudyCourseManageFormPage):
396    """ Page to edit the student study course data
397    """
398    grok.context(ICustomStudentStudyCourse)
399
400    @property
401    def form_fields(self):
402        if self.context.is_postgrad:
403            form_fields = grok.AutoFields(ICustomStudentStudyCourse).omit(
404                'previous_verdict')
405        else:
406            form_fields = grok.AutoFields(ICustomStudentStudyCourse)
407        form_fields['imported_cgpa'].for_display = True
408        return form_fields
409
410class CustomUnregisterCoursesView(UnregisterCoursesView):
411    """Unregister course list by student
412    """
413    grok.context(ICustomStudentStudyLevel)
414
415    def update(self):
416        if not self.context.__parent__.is_current:
417            emit_lock_message(self)
418            return
419        #try:
420        #    academic_session = grok.getSite()['configuration'][
421        #        str(self.context.level_session)]
422        #    if self.context.student.is_postgrad:
423        #        deadline = academic_session.coursereg_deadline_pg
424        #    elif self.context.student.current_mode.startswith('dp'):
425        #        deadline = academic_session.coursereg_deadline_dp
426        #    elif self.context.student.current_mode in (
427        #        'ug_pt', 'de_pt', 'de_dsh', 'ug_dsh'):
428        #        deadline = academic_session.coursereg_deadline_pt
429        #    elif self.context.student.current_mode == 'found':
430        #        deadline = academic_session.coursereg_deadline_found
431        #    elif self.context.student.current_mode == 'bridge':
432        #        deadline = academic_session.coursereg_deadline_bridge
433        #    else:
434        #        deadline = academic_session.coursereg_deadline
435        #except (TypeError, KeyError):
436        #    deadline = None
437        #if deadline and deadline < datetime.now(pytz.utc):
438        #    self.flash(_(
439        #        "Course registration has ended. "
440        #        "Unregistration is disabled."), type="danger")
441        if str(self.context.__parent__.current_level) != self.context.__name__:
442            self.flash(_('This is not your current level.'), type="danger")
443        elif self.context.student.state == REGISTERED:
444            IWorkflowInfo(self.context.student).fireTransition('reset7')
445            message = _('Course list has been unregistered.')
446            self.flash(message)
447        else:
448            self.flash(_('You are in the wrong state.'), type="warning")
449        self.redirect(self.url(self.context))
450        return
451
452class CustomStudyLevelDisplayFormPage(StudyLevelDisplayFormPage):
453    """ Page to display student study levels
454    """
455    grok.context(ICustomStudentStudyLevel)
456    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
457        'total_credits', 'gpa', 'level', 'imported_gpa', 'imported_cgpa')
458    form_fields[
459        'validation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
460
461    @property
462    def translated_values(self):
463        return translated_values(self)
464
465    @property
466    def show_results(self):
467        isStudent = getattr(
468            self.request.principal, 'user_type', None) == 'student'
469        try:
470            show_results = grok.getSite()[
471                'configuration'][str(self.context.level_session)].show_results
472        except KeyError:
473            return False
474        if isStudent and self.context.student.current_mode not in show_results:
475            return False
476        #if isStudent and self.context.student.state != RETURNING \
477        #    and self.context.student.current_level == self.context.level:
478        #    return False
479        return True
480
481class CustomStudyLevelManageFormPage(StudyLevelManageFormPage):
482    """ Page to edit the student study level data
483    """
484    grok.context(ICustomStudentStudyLevel)
485
486    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
487        'validation_date', 'validated_by', 'total_credits', 'gpa', 'level',
488        'total_credits_s1', 'total_credits_s2')
489
490    form_fields['imported_gpa'].for_display = True
491    form_fields['imported_cgpa'].for_display = True
492
493class CustomStudyLevelEditFormPage(StudyLevelEditFormPage):
494    """ Page to edit the student study level data by students
495    """
496    grok.context(ICustomStudentStudyLevel)
497
498class StudyLevelRepairFormPage(KofaEditFormPage):
499    """ Page to repair the student study level data by students
500    """
501    grok.context(IStudentStudyLevel)
502    grok.name('repair')
503    grok.require('waeup.editStudyLevel')
504    grok.template('studylevelrepairpage')
505    pnav = 4
506    placeholder = _('Enter valid course code')
507
508    def update(self, ADD=None, course=None):
509        if not self.context.__parent__.is_current \
510            or self.context.student.studycourse_locked:
511            emit_lock_message(self)
512            return
513        try:
514            studylevel_repair_enabled = grok.getSite()['configuration'][
515                str(self.context.level_session)].studylevel_repair_enabled
516        except KeyError:
517            emit_lock_message(self)
518            return
519        if not studylevel_repair_enabled:
520            emit_lock_message(self)
521            return
522        super(StudyLevelRepairFormPage, self).update()
523        if ADD is not None:
524            if not course:
525                self.flash(_('No valid course code entered.'), type="warning")
526                return
527            cat = queryUtility(ICatalog, name='courses_catalog')
528            result = cat.searchResults(code=(course, course))
529            if len(result) != 1:
530                self.flash(_('Course not found.'), type="warning")
531                return
532            course = list(result)[0]
533            addCourseTicket(self, course)
534        return
535
536    @property
537    def label(self):
538        # Here we know that the cookie has been set
539        lang = self.request.cookies.get('kofa.language')
540        level_title = translate(self.context.level_title, 'waeup.kofa',
541            target_language=lang)
542        return _('Repair course list of ${a}',
543            mapping = {'a':level_title})
544
545    @property
546    def translated_values(self):
547        return translated_values(self)
548
549    def _delCourseTicket(self, **data):
550        form = self.request.form
551        if 'val_id' in form:
552            child_id = form['val_id']
553        else:
554            self.flash(_('No ticket selected.'), type="warning")
555            self.redirect(self.url(self.context, '@@edit'))
556            return
557        if not isinstance(child_id, list):
558            child_id = [child_id]
559        deleted = []
560        for id in child_id:
561            # Students are not allowed to remove core tickets
562            if id in self.context and \
563                self.context[id].removable_by_student:
564                del self.context[id]
565                deleted.append(id)
566        if len(deleted):
567            self.flash(_('Successfully removed: ${a}',
568                mapping = {'a':', '.join(deleted)}))
569            self.context.writeLogMessage(
570                self,'removed: %s at %s' %
571                (', '.join(deleted), self.context.level))
572        self.redirect(self.url(self.context, u'@@repair'))
573        return
574
575    @jsaction(_('Remove selected tickets'))
576    def delCourseTicket(self, **data):
577        self._delCourseTicket(**data)
578        return
579
580class CustomExportPDFCourseRegistrationSlip(
581    NigeriaExportPDFCourseRegistrationSlip):
582    """Deliver a PDF slip of the context.
583    """
584    grok.context(ICustomStudentStudyLevel)
585    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
586        'level_session', 'level_verdict',
587        'validated_by', 'validation_date', 'gpa', 'level',
588        'imported_gpa', 'imported_cgpa')
589
590    omit_fields = ('password', 'suspended', 'suspended_comment',
591        'phone', 'adm_code', 'sex', 'email', 'date_of_birth',
592        'department', 'current_mode', 'current_level', 'flash_notice',
593        'transcript_remark')
594
595    @property
596    def show_results(self):
597        isStudent = getattr(
598            self.request.principal, 'user_type', None) == 'student'
599        try:
600            show_results = grok.getSite()[
601                'configuration'][str(self.context.level_session)].show_results
602        except KeyError:
603            return False
604        if isStudent and self.context.student.current_mode not in show_results:
605            return False
606        if isStudent and self.context.student.state != RETURNING \
607            and self.context.student.current_level == self.context.level:
608            return False
609        return True
610
611    def update(self):
612        if self.context.student.state != REGISTERED \
613            and self.context.student.current_level == self.context.level:
614            self.flash(_('Forbidden'), type="warning")
615            self.redirect(self.url(self.context))
616            return
617
618    @property
619    def label(self):
620        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
621        lang = self.request.cookies.get('kofa.language', portal_language)
622        level_title = translate(self.context.level_title, 'waeup.kofa',
623            target_language=lang)
624        line0 = ''
625        if self.context.student.is_postgrad:
626            line0 = 'SCHOOL OF POSTGRADUATE STUDIES\n'
627        elif self.context.student.current_mode.endswith('_pt'):
628            line0 = 'DIRECTORATE OF PART-TIME DEGREE PROGRAMMES\n'
629        line1 = translate(_('Course Registration Slip'),
630            target_language=portal_language) \
631            + ' %s' % level_title
632        line2 = translate(_('Session'),
633            target_language=portal_language) \
634            + ' %s' % self.context.getSessionString
635        return '%s%s\n%s' % (line0, line1, line2)
636
637    @property
638    def title(self):
639        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
640        return translate(_('Units Registered'), target_language=portal_language)
641
642    def _signatures(self):
643        if self.context.student.current_mode in (
644            'ug_pt', 'de_pt', 'ug_dsh', 'de_dsh', 'found'):
645            return (
646                [('I have selected the course on the advise of my Head of '
647                 'Department. <br>', _('Student\'s Signature'), '<br>')],
648                [('This student has satisfied the department\'s requirements. '
649                 'I recommend to approve the course registration. <br>',
650                 _('Head of Department\'s Signature'), '<br>')],
651                [('' , _('Deputy Registrar\'s Signature'), '<br>')],
652                [('', _('Director\'s Signature'))]
653                )
654        if self.context.student.current_mode in (
655            'de_ft', 'ug_ft', 'dp_ft', 'transfer', 'bridge'):
656            return ([_('Academic Adviser\'s Signature'),
657                _('Faculty Officer\'s Signature'),
658                _('Student\'s Signature')],)
659
660        if self.context.student.current_mode in ('special_pg_ft', 'special_pg_pt'):
661            return (
662                [('I declare that all items of information supplied above are correct:' ,
663                    _('Student\'s Signature'), '<br>')],
664                [('We approved the above registration:',
665                    _('Major Supervisor (Name / Signature)'), '')],
666                [('', _('Co-Supervisor (Name / Signature)'), '')],
667                [('', _('Head of Department'), '<br>')],
668                [('The student has satisfied the conditions for renewal of '
669                  'registration for graduate school programme in this university:',
670                  _('Secretary <br /> (School of Postgraduate Studies)'), '')],
671                [('', _('Dean <br /> (School of Postgraduate Studies)'), '')],
672                )
673        return None
674
675
676    def render(self):
677        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
678        Sem = translate(_('Sem.'), target_language=portal_language)
679        Code = translate(_('Code'), target_language=portal_language)
680        Title = translate(_('Title'), target_language=portal_language)
681        Cred = translate(_('Cred.'), target_language=portal_language)
682        CC = translate(_('Cat.'), target_language=portal_language)
683        if self.show_results:
684            TotalScore = translate(_('Total Score'), target_language=portal_language)
685            #CA = translate(_('CA'), target_language=portal_language)
686            Grade = translate(_('Grade'), target_language=portal_language)
687        Signature = translate(_('Lecturer\'s Signature'), 'waeup.aaue',
688            target_language=portal_language)
689        studentview = StudentBasePDFFormPage(self.context.student,
690            self.request, self.omit_fields)
691        students_utils = getUtility(IStudentsUtils)
692
693        tabledata = []
694        tableheader = []
695        contenttitle = []
696        for i in range(1,7):
697            tabledata.append(sorted(
698                [value for value in self.context.values() if value.semester == i],
699                key=lambda value: str(value.semester) + value.code))
700            if self.show_results:
701                tableheader.append([(Code,'code', 2.0),
702                                   (Title,'title', 7),
703                                   (Cred, 'credits', 1.4),
704                                   (CC, 'course_category', 1.2),
705                                   (TotalScore, 'total_score', 1.4),
706                                   #(CA, 'ca', 1.4),
707                                   (Grade, 'grade', 1.4),
708                                   (Signature, 'dummy', 3),
709                                   ])
710            else:
711                tableheader.append([(Code,'code', 2.0),
712                                   (Title,'title', 7),
713                                   (Cred, 'credits', 1.5),
714                                   (CC, 'course_category', 1.2),
715                                   (Signature, 'dummy', 3),
716                                   ])
717        if len(self.label.split('\n')) == 3:
718            topMargin = 1.9
719        elif len(self.label.split('\n')) == 2:
720            topMargin = 1.7
721        else:
722            topMargin = 1.5
723        return students_utils.renderPDF(
724            self, 'course_registration_slip.pdf',
725            self.context.student, studentview,
726            tableheader=tableheader,
727            tabledata=tabledata,
728            signatures=self._signatures(),
729            topMargin=topMargin,
730            omit_fields=self.omit_fields
731            )
732
733class CustomStudyCourseTranscriptPage(StudyCourseTranscriptPage):
734    """ Page to display the student's transcript.
735    """
736    grok.require('waeup.viewStudent')
737
738class CustomExportPDFTranscriptSlip(ExportPDFTranscriptSlip):
739    """Deliver a PDF slip of the context.
740    """
741#    grok.require('waeup.viewStudent')
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        return ([(
755            'O.O OHIKHENA (Manupa)<br />Principal Asst. Registrar<br /> '
756            'Exams, Records and Data Processing Division <br /> For: Registrar')],)
757
758    def render(self):
759        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
760        Term = translate(_('Sem.'), target_language=portal_language)
761        Code = translate(_('Code'), target_language=portal_language)
762        Title = translate(_('Title'), target_language=portal_language)
763        Cred = translate(_('Credits'), target_language=portal_language)
764        Score = translate(_('Score'), target_language=portal_language)
765        Grade = translate(_('Grade'), target_language=portal_language)
766        studentview = StudentBasePDFFormPage(self.context.student,
767            self.request, self.omit_fields)
768        students_utils = getUtility(IStudentsUtils)
769
770        tableheader = [(Code,'code', 2.5),
771                         (Title,'title', 7),
772                         (Term, 'semester', 1.5),
773                         (Cred, 'credits', 1.5),
774                         (Score, 'total_score', 1.5),
775                         (Grade, 'grade', 1.5),
776                         ]
777
778        pdfstream = students_utils.renderPDFTranscript(
779            self, 'transcript.pdf',
780            self.context.student, studentview,
781            omit_fields=self.omit_fields,
782            tableheader=tableheader,
783            signatures=self._signatures(),
784            sigs_in_footer=self._sigsInFooter(),
785            digital_sigs=self._digital_sigs(),
786            save_file=self._save_file(),
787            )
788        if not pdfstream:
789            self.redirect(self.url(self.context.student))
790            return
791        return pdfstream
792
793class CustomExportPDFAdmissionSlip(ExportPDFAdmissionSlip):
794    """Deliver a PDF Admission slip.
795    """
796
797    @property
798    def label(self):
799        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
800        return translate(_('e-Admission Slip \n'),
801            target_language=portal_language) \
802            + ' %s' % self.context.display_fullname
803
804class CustomExportPDFClearanceSlip(NigeriaExportPDFClearanceSlip):
805    """Deliver a PDF slip of the context.
806    """
807
808    @property
809    def label(self):
810        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
811        return translate(_('Verification/Clearance Slip\n'),
812            target_language=portal_language) \
813            + ' %s' % self.context.display_fullname
814
815    @property
816    def form_fields(self):
817        if self.context.is_postgrad:
818            form_fields = grok.AutoFields(
819                ICustomPGStudentClearance).omit('clearance_locked')
820        else:
821            form_fields = grok.AutoFields(
822                ICustomUGStudentClearance).omit('clearance_locked')
823        if not getattr(self.context, 'officer_comment'):
824            form_fields = form_fields.omit('officer_comment')
825        form_fields = form_fields.omit('def_adm')
826        return form_fields
827
828class StudentGetMatricNumberPage(UtilityView, grok.View):
829    """ Construct and set the matriculation number.
830    """
831    grok.context(IStudent)
832    grok.name('get_matric_number')
833    grok.require('waeup.viewStudent')
834
835    def update(self):
836        students_utils = getUtility(IStudentsUtils)
837        msg, mnumber = students_utils.setMatricNumber(self.context)
838        if msg:
839            self.flash(msg, type="danger")
840        else:
841            self.flash(_('Matriculation number %s assigned.' % mnumber))
842            self.context.writeLogMessage(self, '%s assigned' % mnumber)
843        self.redirect(self.url(self.context))
844        return
845
846    def render(self):
847        return
848
849class ExportPDFMatricNumberSlip(UtilityView, grok.View):
850    """Deliver a PDF notification slip.
851    """
852    grok.context(ICustomStudent)
853    grok.name('matric_number_slip.pdf')
854    grok.require('waeup.viewStudent')
855    prefix = 'form'
856
857    form_fields = grok.AutoFields(ICustomStudent).select(
858        'student_id', 'matric_number')
859    omit_fields = ('date_of_birth', 'current_level', 'flash_notice')
860
861    @property
862    def title(self):
863        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
864        return translate(_('Matriculation Number'), 'waeup.kofa',
865            target_language=portal_language)
866
867    @property
868    def label(self):
869        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
870        return translate(_('Matriculation Number Slip\n'),
871            target_language=portal_language) \
872            + ' %s' % self.context.display_fullname
873
874    def render(self):
875        if self.context.state not in (PAID,) or not self.context.is_fresh \
876            or not self.context.matric_number:
877            self.flash('Not allowed.', type="danger")
878            self.redirect(self.url(self.context))
879            return
880        students_utils = getUtility(IStudentsUtils)
881        pre_text = _('Congratulations! Your acceptance fee and school fees ' +
882                     'payments have been received and your matriculation ' +
883                     'number generated with details as follows.')
884        return students_utils.renderPDFAdmissionLetter(self,
885            self.context.student, omit_fields=self.omit_fields,
886            pre_text=pre_text, post_text='')
887
888class ExportPersonalDataSlip(UtilityView, grok.View):
889    """Deliver a PDF notification slip.
890    """
891    grok.context(ICustomStudent)
892    grok.name('personal_data_slip.pdf')
893    grok.require('waeup.viewStudent')
894    prefix = 'form'
895    note = None
896
897    form_fields = grok.AutoFields(ICustomStudentPersonal).omit('personal_updated')
898    omit_fields = ('suspended', 'suspended_comment', 'adm_code',
899                   'certificate', 'flash_notice')
900
901    @property
902    def title(self):
903        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
904        return translate(_('Personal Data'), 'waeup.kofa',
905            target_language=portal_language)
906
907    @property
908    def label(self):
909        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
910        return translate(_('Personal Data Slip\n'),
911            target_language=portal_language) \
912            + ' %s' % self.context.display_fullname
913
914    def render(self):
915        studentview = StudentBasePDFFormPage(self.context.student,
916            self.request, self.omit_fields)
917        students_utils = getUtility(IStudentsUtils)
918        return students_utils.renderPDF(self, 'personal_data_slip.pdf',
919            self.context.student, studentview, note=self.note,
920            omit_fields=self.omit_fields)
921
922class CustomAccommodationManageFormPage(NigeriaAccommodationManageFormPage):
923    """ Page to manage bed tickets.
924    This manage form page is for both students and students officers.
925    """
926    with_hostel_selection = True
927
928class CustomBedTicketAddPage(NigeriaBedTicketAddPage):
929    with_ac = False
930
931class CustomStudentFilesUploadPage(StudentFilesUploadPage):
932    """ View to upload files by student. Inherit from same class in
933    base package, not from kofacustom.nigeria which
934    requires that no application slip exists.
935    """
936
937class CustomCourseTicketDisplayFormPage(CourseTicketDisplayFormPage):
938    """ Page to display course tickets
939    """
940
941    @property
942    def show_results(self):
943        isStudent = getattr(
944            self.request.principal, 'user_type', None) == 'student'
945        if isStudent:
946            return False
947        return True
948
949    @property
950    def form_fields(self):
951        if self.show_results:
952            return grok.AutoFields(ICustomCourseTicket)
953        else:
954            return grok.AutoFields(ICustomCourseTicket).omit('score').omit('ca')
955
956class CustomCourseTicketManageFormPage(CourseTicketManageFormPage):
957    """ Page to manage course tickets
958    """
959    form_fields = grok.AutoFields(ICustomCourseTicket)
960    form_fields['title'].for_display = True
961    form_fields['fcode'].for_display = True
962    form_fields['dcode'].for_display = True
963    form_fields['semester'].for_display = True
964    form_fields['passmark'].for_display = True
965    form_fields['credits'].for_display = True
966    form_fields['mandatory'].for_display = False
967    form_fields['automatic'].for_display = True
968    form_fields['carry_over'].for_display = True
969    form_fields['ticket_session'].for_display = True
970    form_fields['imported_ts'].for_display = True
971
972class CustomEditScoresPage(EditScoresPage):
973    """Page that filters and lists students.
974    """
975    grok.template('editscorespage')
976
977    def _searchCatalog(self, session):
978        cat = queryUtility(ICatalog, name='coursetickets_catalog')
979        coursetickets = cat.searchResults(
980            session=(session, session),
981            code=(self.context.code, self.context.code)
982            )
983        try:
984            score_editing_enabled = grok.getSite()[
985                'configuration'][str(session)].score_editing_enabled
986        except KeyError:
987            return []
988        coursetickets_list = [courseticket for courseticket in coursetickets
989            if courseticket.student.current_mode in score_editing_enabled]
990        return coursetickets_list
991
992    def _extract_uploadfile(self, uploadfile):
993        """Get a mapping of student-ids to scores.
994
995        The mapping is constructed by reading contents from `uploadfile`.
996
997        We expect uploadfile to be a regular CSV file with columns
998        ``student_id``, ``score``, ``imported_ts``
999        and ``ca`` (other cols are ignored).
1000        """
1001        result = dict()
1002        data = StringIO(uploadfile.read())  # ensure we have something seekable
1003        reader = csv.DictReader(data)
1004        for row in reader:
1005            if not ('student_id' in row and 'score' in row and 'ca' in row and
1006                'imported_ts' in row):
1007                continue
1008            result[row['student_id']] = (
1009                row['score'], row['ca'], row['imported_ts'])
1010        return result
1011
1012    def _update_scores(self, form):
1013        ob_class = self.__implemented__.__name__.replace('waeup.kofa.', '')
1014        error = ''
1015        if 'UPDATE_FILE' in form:
1016            if form['uploadfile']:
1017                try:
1018                    formvals = self._extract_uploadfile(form['uploadfile'])
1019                except:
1020                    self.flash(
1021                        _('Uploaded file contains illegal data. Ignored'),
1022                        type="danger")
1023                    return False
1024            else:
1025                self.flash(
1026                    _('No file provided.'), type="danger")
1027                return False
1028        else:
1029            formvals = dict(zip(form['sids'], zip(
1030                form['scores'], form['cas'], form['imported_ts'])))
1031        for ticket in self.editable_tickets:
1032            ticket_error = False
1033            score = ticket.score
1034            ca = ticket.ca
1035            imported_ts = ticket.imported_ts
1036            sid = ticket.student.student_id
1037            if formvals[sid][0] == '':
1038                score = None
1039            if formvals[sid][1] == '':
1040                ca = None
1041            if formvals[sid][2] == '':
1042                imported_ts = None
1043            try:
1044                if formvals[sid][0]:
1045                    score = int(formvals[sid][0])
1046                if formvals[sid][1]:
1047                    ca = int(formvals[sid][1])
1048                if formvals[sid][2]:
1049                    imported_ts = int(formvals[sid][2])
1050            except ValueError:
1051                error += '%s, ' % ticket.student.display_fullname
1052                ticket_error = True
1053            if not ticket_error and ticket.score != score:
1054                try:
1055                    ticket.score = score
1056                except TooBig:
1057                    error += '%s, ' % ticket.student.display_fullname
1058                    ticket_error = True
1059                    pass
1060                ticket.student.__parent__.logger.info(
1061                    '%s - %s %s/%s score updated (%s)' %
1062                    (ob_class, ticket.student.student_id,
1063                     ticket.level, ticket.code, score))
1064            if not ticket_error and ticket.ca != ca:
1065                try:
1066                    ticket.ca = ca
1067                except TooBig:
1068                    error += '%s, ' % ticket.student.display_fullname
1069                    pass
1070                ticket.student.__parent__.logger.info(
1071                    '%s - %s %s/%s ca updated (%s)' %
1072                    (ob_class, ticket.student.student_id,
1073                     ticket.level, ticket.code, ca))
1074            if not ticket_error and ticket.imported_ts != imported_ts:
1075                try:
1076                    ticket.imported_ts = imported_ts
1077                except TooBig:
1078                    error += '%s, ' % ticket.student.display_fullname
1079                    pass
1080                ticket.student.__parent__.logger.info(
1081                    '%s - %s %s/%s imported_ts updated (%s)' %
1082                    (ob_class, ticket.student.student_id,
1083                     ticket.level, ticket.code, imported_ts))
1084        if error:
1085            self.flash(_('Error: Score(s), CA(s) and Imported TS(s) of %s have not be updated. '
1086              % error.strip(', ')), type="danger")
1087        return True
1088
1089class EditPreviousSessionScoresPage(CustomEditScoresPage):
1090
1091    grok.name('edit_prev_scores')
1092
1093    def update(self,  *args, **kw):
1094        form = self.request.form
1095        self.current_academic_session = grok.getSite()[
1096            'configuration'].current_academic_session
1097        if self.context.__parent__.__parent__.score_editing_disabled:
1098            self.flash(_('Score editing disabled.'), type="warning")
1099            self.redirect(self.url(self.context))
1100            return
1101        if not self.current_academic_session:
1102            self.flash(_('Current academic session not set.'), type="warning")
1103            self.redirect(self.url(self.context))
1104            return
1105        previous_session = self.current_academic_session - 1
1106        self.session_title = academic_sessions_vocab.getTerm(
1107            previous_session).title
1108        self.tickets = self._searchCatalog(previous_session)
1109        if not self.tickets:
1110            self.flash(_('No student found.'), type="warning")
1111            self.redirect(self.url(self.context))
1112            return
1113        self.editable_tickets = [
1114            ticket for ticket in self.tickets if ticket.editable_by_lecturer]
1115        if not 'UPDATE_TABLE' in form and not 'UPDATE_FILE' in form:
1116            return
1117        if not self.editable_tickets:
1118            return
1119        success = self._update_scores(form)
1120        if success:
1121            self.flash(_('You successfully updated course results.'))
1122        return
1123
1124class CustomExportPDFScoresSlip(ExportPDFScoresSlip):
1125    """Deliver a PDF slip of course tickets for a lecturer.
1126    """
1127
1128    note = u'\nUpgraded scores are with asterisks.'
1129
1130    def data(self, session):
1131        #site = grok.getSite()
1132        cat = queryUtility(ICatalog, name='coursetickets_catalog')
1133        coursetickets = cat.searchResults(
1134            session=(session, session),
1135            code=(self.context.code, self.context.code)
1136            )
1137        # Apply filter
1138        try:
1139            score_editing_enabled = grok.getSite()[
1140                'configuration'][str(session)].score_editing_enabled
1141            if checkPermission('waeup.manageAcademics', self.context):
1142                score_editing_enabled = True
1143            coursetickets_filtered = [courseticket
1144                for courseticket in coursetickets
1145                if (checkPermission('waeup.manageAcademics', self.context)
1146                    or (courseticket.student.current_mode in
1147                        score_editing_enabled))
1148                and courseticket.total_score is not None
1149                and courseticket.__parent__.__parent__.is_current]
1150        except KeyError:
1151            coursetickets_filtered = coursetickets
1152        # In AAUE only editable tickets can be printed (deactivated on 02/10/19)
1153        #editable_tickets = [
1154        #    ticket for ticket in coursetickets_filtered
1155        #    if ticket.editable_by_lecturer]
1156        header = [[_(''),
1157                   _('Student Id'),
1158                   _('Matric No.'),
1159                   #_('Reg. No.'),
1160                   #_('Fullname'),
1161                   #_('Status'),
1162                   #_('Course of\nStudies'),
1163                   _('Department'),
1164                   _('Level'),
1165                   _(' CA  '),
1166                   _('Exam\nScore'),
1167                   _('Total '),
1168                   _('Grade'),
1169                   ],]
1170        sorted_tickets = sorted(coursetickets_filtered, # editable_tickets,
1171            key=lambda ticket: ticket.student.depcode
1172                               + ticket.student.faccode
1173                               + ticket.student.matric_number)
1174        no = 1
1175        tickets = []
1176        passed = 0
1177        failed = 0
1178        with_ca = False
1179        grade_stats = {'A':0, 'B':0, 'C':0, 'D':0, 'E':0, 'F':0, }
1180        for ticket in sorted_tickets:
1181            if ticket.ca > 0:
1182                with_ca = True
1183            total = ticket.total_score
1184            if getattr(ticket, 'imported_ts', None):
1185                total = "**%s**" % ticket.imported_ts
1186            grade = ticket._getGradeWeightFromScore[0]
1187            if grade in grade_stats.keys():
1188                grade_stats[grade] += 1
1189            if grade in ('F', '-'):
1190                failed += 1
1191            else:
1192                passed += 1
1193            fullname = textwrap.fill(ticket.student.display_fullname, 30)
1194            #deptitle = site['faculties'][ticket.student.faccode][
1195            #    ticket.student.depcode].longtitle
1196            row = [str(no),
1197                  ticket.student.student_id,
1198                  ticket.student.matric_number,
1199                  #ticket.student.reg_number,
1200                  #fullname,
1201                  #ticket.student.translated_state,
1202                  #ticket.student.certcode,
1203                  ticket.student.faccode + ' / ' + ticket.student.depcode,
1204                  ticket.level,
1205                  ticket.ca,
1206                  ticket.score,
1207                  total,
1208                  grade,
1209                  ]
1210            tickets.append(row)
1211            no += 1
1212        total = passed + failed
1213        passed_perc = 0
1214        failed_perc = 0
1215        if total:
1216            passed_perc = round(100.0 * passed / total)
1217            failed_perc = round(100.0 * failed / total)
1218        dep = self.context.__parent__.__parent__.longtitle
1219        fac = self.context.__parent__.__parent__.__parent__.longtitle
1220        # remove CA column if not necessary
1221        if not with_ca:
1222            header = [[_(''),
1223                       _('Student Id'),
1224                       _('Matric No.'),
1225                       #_('Reg. No.'),
1226                       #_('Fullname'),
1227                       #_('Status'),
1228                       #_('Course of\nStudies'),
1229                       _('Department'),
1230                       _('Level'),
1231                       #_(' CA  '),
1232                       _('Exam\nScore'),
1233                       _('Total '),
1234                       _('Grade'),
1235                       ],]
1236            for ticket in tickets:
1237                del(ticket[5])
1238        return header + tickets, [
1239            dep, fac, total, passed, passed_perc, failed, failed_perc, grade_stats]
1240
1241    def render(self):
1242        session = grok.getSite()['configuration'].current_academic_session
1243        lecturers = [i['user_title'] for i in self.getUsersWithLocalRoles()
1244                     if i['local_role'] == 'waeup.local.Lecturer']
1245        lecturers =  ', '.join(lecturers)
1246        students_utils = getUtility(IStudentsUtils)
1247        # only orientation is different
1248        return students_utils.renderPDFCourseticketsOverview(
1249            self, 'coursetickets',
1250            session, self.data(session), lecturers, '', 45, self.note)
1251
1252class DownloadPreviousSessionScoresView(DownloadScoresView):
1253    """View that exports scores.
1254    """
1255    grok.name('download_prev_scores')
1256
1257    def update(self):
1258        self.current_academic_session = grok.getSite()[
1259            'configuration'].current_academic_session
1260        if self.context.__parent__.__parent__.score_editing_disabled:
1261            self.flash(_('Score editing disabled.'), type="warning")
1262            self.redirect(self.url(self.context))
1263            return
1264        if not self.current_academic_session:
1265            self.flash(_('Current academic session not set.'), type="warning")
1266            self.redirect(self.url(self.context))
1267            return
1268        site = grok.getSite()
1269        exporter = getUtility(ICSVExporter, name='lecturer')
1270        self.csv = exporter.export_filtered(site, filepath=None,
1271                                 catalog='coursetickets',
1272                                 session=self.current_academic_session-1,
1273                                 level=None,
1274                                 code=self.context.code)
1275        return
1276
1277class AlumniRequestPasswordPage(StudentRequestPasswordPage):
1278    """Captcha'd request password page for students.
1279    """
1280    grok.name('alumni_requestpw')
1281    grok.require('waeup.Anonymous')
1282    grok.template('alumni_requestpw')
1283    form_fields = grok.AutoFields(IStudentRequestPW).select(
1284        'lastname','number','email')
1285    label = _('Search student record and send password for first-time login')
1286
1287    def _redirect_no_student(self):
1288        self.flash(_('No student record found.'), type="warning")
1289        self.redirect(self.application_url() + '/applicants/trans2017/register')
1290        return
1291
1292class CustomAddStudyLevelFormPage(AddStudyLevelFormPage):
1293    """ This page is temporarily locked.
1294    """
1295
1296    def update(self):
1297        emit_lock_message(self)
1298        return
Note: See TracBrowser for help on using the repository browser.