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

Last change on this file since 15241 was 15240, checked in by Henrik Bettermann, 7 years ago

Only strings are allowed in base package.

  • Property svn:keywords set to Id
File size: 47.3 KB
RevLine 
[8911]1## $Id: browser.py 15240 2018-11-13 12:18:06Z henrik $
2##
3## Copyright (C) 2012 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18import grok
[13937]19import csv
[13964]20import textwrap
[14981]21import pytz
[13937]22from cStringIO import StringIO
[14981]23from datetime import datetime
[8911]24from zope.i18n import translate
[13900]25from zope.component import getUtility, queryUtility
[14113]26from zope.schema.interfaces import TooBig, TooSmall
[13523]27from zope.security import checkPermission
[13900]28from zope.catalog.interfaces import ICatalog
[13351]29from zope.formlib.textwidgets import BytesDisplayWidget
[14981]30from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
[15232]31from reportlab.platypus import Paragraph
32from reportlab.lib.styles import getSampleStyleSheet
[11597]33from waeup.kofa.browser.layout import UtilityView
[8911]34from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget
[14288]35from waeup.kofa.interfaces import (
[14306]36    IKofaUtils, academic_sessions_vocab, ICSVExporter, IKofaObject)
37from waeup.kofa.students.interfaces import (
38    IStudentsUtils, IStudent, IStudentRequestPW)
[14000]39from waeup.kofa.students.workflow import PAID, REGISTERED, RETURNING
[9914]40from waeup.kofa.students.browser import (
[11846]41    StartClearancePage,
[9914]42    StudentBasePDFFormPage,
43    CourseTicketAddFormPage,
44    StudyLevelDisplayFormPage,
[13834]45    StudyLevelManageFormPage,
46    StudyLevelEditFormPage,
[13059]47    ExportPDFTranscriptSlip,
48    ExportPDFAdmissionSlip,
[13353]49    BedTicketAddPage,
[13380]50    StudentFilesUploadPage,
[13523]51    PaymentsManageFormPage,
[13770]52    CourseTicketDisplayFormPage,
53    CourseTicketManageFormPage,
[13900]54    EditScoresPage,
[14165]55    ExportPDFScoresSlip,
56    StudyCourseTranscriptPage,
[14288]57    DownloadScoresView,
[14534]58    StudentRequestPasswordPage,
[14981]59    StudyCourseManageFormPage,
60    UnregisterCoursesView
[10269]61    )
[8911]62from kofacustom.nigeria.students.browser import (
63    NigeriaOnlinePaymentDisplayFormPage,
64    NigeriaOnlinePaymentAddFormPage,
[13059]65    NigeriaExportPDFPaymentSlip,
66    NigeriaExportPDFCourseRegistrationSlip,
[13351]67    NigeriaStudentPersonalDisplayFormPage,
68    NigeriaStudentPersonalEditFormPage,
69    NigeriaStudentPersonalManageFormPage,
[14084]70    NigeriaStudentClearanceDisplayFormPage,
71    NigeriaExportPDFClearanceSlip,
72    NigeriaStudentClearanceManageFormPage,
[13362]73    NigeriaStudentClearanceEditFormPage,
[13462]74    NigeriaAccommodationManageFormPage,
[13795]75    NigeriaStudentBaseDisplayFormPage,
[14165]76    NigeriaStudentBaseManageFormPage
[10269]77    )
[9496]78from waeup.aaue.students.interfaces import (
[9914]79    ICustomStudentOnlinePayment,
[11607]80    ICustomStudentStudyLevel,
[13351]81    ICustomStudent,
82    ICustomStudentPersonal,
[13362]83    ICustomStudentPersonalEdit,
[13770]84    ICustomUGStudentClearance,
[14298]85    ICustomUGStudentClearanceEdit,
[14084]86    ICustomPGStudentClearance,
[13795]87    ICustomCourseTicket,
[14534]88    ICustomStudentBase,
89    ICustomStudentStudyCourse)
[13414]90from waeup.aaue.interswitch.browser import gateway_net_amt
[9914]91from waeup.aaue.interfaces import MessageFactory as _
[8911]92
[15232]93STYLE = getSampleStyleSheet()
94
[14306]95grok.context(IKofaObject)  # Make IKofaObject the default context
96
[15228]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
[13795]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
[13351]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
[14661]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 """
[14674]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.
[14677]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.
[14674]201 <br /><br />
[14677]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.)
[14674]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
[14677]210 centers will be penalized and the exam of such a student will be cancelled.
[14674]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>
[14661]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
[14084]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
[14104]254        form_fields = form_fields.omit('def_adm')
[14084]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')
[14104]269        form_fields = form_fields.omit('def_adm')
[14084]270        return form_fields
271
[13362]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:
[14298]283            form_fields = grok.AutoFields(ICustomUGStudentClearanceEdit).omit(
[13362]284            'clearance_locked', 'clr_code', 'officer_comment',
[14104]285            'physical_clearance_date', 'date_of_birth', 'nationality', 'lga')
286        form_fields = form_fields.omit('def_adm')
[13362]287        return form_fields
288
[11846]289class CustomStartClearancePage(StartClearancePage):
[13360]290    with_ac = False
[11846]291
[13351]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
[8911]302class CustomOnlinePaymentDisplayFormPage(NigeriaOnlinePaymentDisplayFormPage):
303    """ Page to view an online payment ticket
304    """
305    grok.context(ICustomStudentOnlinePayment)
[9853]306    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
[9990]307        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
[8911]308    form_fields[
309        'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
310    form_fields[
311        'payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
312
313class CustomOnlinePaymentAddFormPage(NigeriaOnlinePaymentAddFormPage):
314    """ Page to add an online payment ticket
315    """
316    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).select(
317        'p_category')
318
[14527]319    ALUMNI_PAYMENT_CATS =  {
[14296]320        'transcript_local': 'Transcript Fee Local',
321        'transcript_inter': 'Transcript Fee International',
322        }
323
[14533]324    REDUCED_PAYMENT_CATS =  {
[14527]325        'clearance': 'Acceptance Fee',
326        'schoolfee': 'School Fee',
327        }
328
[14986]329    IJMBE_PAYMENT_CATS =  {
330        'clearance': 'Acceptance Fee',
331        'schoolfee': 'School Fee',
[14987]332        'schoolfee_1': 'School Fee (1st instalment)',
[14986]333        'schoolfee_2': 'School Fee (2nd instalment)',
334        }
335
[14296]336    @property
337    def selectable_categories(self):
338        if 'alumni' in self.application_url():
[14527]339            return self.ALUMNI_PAYMENT_CATS.items()
[14573]340        if self.context.student.current_mode in (
[15182]341            'special_pg_ft', 'special_pg_pt', 'found'):
[14533]342            return self.REDUCED_PAYMENT_CATS.items()
[14986]343        if self.context.student.current_mode == 'ijmbe':
344            return sorted(self.IJMBE_PAYMENT_CATS.items())
[14296]345        categories = getUtility(IKofaUtils).SELECTABLE_PAYMENT_CATEGORIES
346        return sorted(categories.items())
347
[13523]348class CustomPaymentsManageFormPage(PaymentsManageFormPage):
349    """ Page to manage the student payments.
350
351    This manage form page is for both students and students officers.
352    """
353    @property
354    def manage_payments_allowed(self):
355        return checkPermission('waeup.manageStudent', self.context)
356
[13059]357class CustomExportPDFPaymentSlip(NigeriaExportPDFPaymentSlip):
[8911]358    """Deliver a PDF slip of the context.
359    """
360    grok.context(ICustomStudentOnlinePayment)
[9853]361    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
[9990]362        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
[8911]363    form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
[9496]364    form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
[9914]365
[11625]366    @property
367    def note(self):
[13408]368        p_session = self.context.p_session
[13405]369        try:
[13408]370            academic_session = grok.getSite()['configuration'][str(p_session)]
[13405]371        except KeyError:
372            academic_session = None
[13425]373        text =  '\n\n The Amount Authorized is inclusive of: '
[13512]374        if self.context.p_category in ('schoolfee_incl', 'schoolfee_1') \
375            and academic_session:
[14385]376            #welfare_fee = gateway_net_amt(academic_session.welfare_fee)
377            #union_fee = gateway_net_amt(academic_session.union_fee)
[14244]378            if self.context.student.entry_session == 2016 \
379                and self.context.student.entry_mode == 'ug_ft' \
380                and self.context.p_session == 2016:
381                # Add student id card fee to first school fee payment.
[14385]382
383                ## Attention: The payment slip does not contain any information
384                ## whether the fee was added or not.
385                ## We can only draw conclusions from from the student's entry
386                ## session whether the fee had been included.
387                #id_card_fee = gateway_net_amt(academic_session.id_card_fee)
388                #text += ('School Fee, '
389                #         '%s Naira Student ID Card Fee, '
390                #         '%s Naira Student Union Dues, '
391                #         '%s Naira Student Welfare Assurance Fee and '
392                #         % (id_card_fee, union_fee, welfare_fee))
393
[14244]394                text += ('School Fee, '
[14385]395                         'Student ID Card Fee, '
396                         'Student Union Dues, '
397                         'Student Welfare Assurance Fee and ')
[14244]398            else:
[14385]399
400                #text += ('School Fee, '
401                #         '%s Naira Student Union Dues, '
402                #         '%s Naira Student Welfare Assurance Fee and '
403                #         % (union_fee, welfare_fee))
404
[14244]405                text += ('School Fee, '
[14385]406                         'Student Union Dues, '
407                         'Student Welfare Assurance Fee and ')
[13410]408        elif self.context.p_category in (
409            'clearance_incl', 'clearance_medical_incl') and academic_session:
[14385]410
411            #matric_gown_fee = gateway_net_amt(academic_session.matric_gown_fee)
412            #lapel_fee = gateway_net_amt(academic_session.lapel_fee)
413            #text += ('Acceptance Fee, '
414            #         '%s Naira Matriculation Gown Fee, '
415            #         '%s Naira Lapel/File Fee and '
416            #         % (matric_gown_fee, lapel_fee))
417
[13437]418            text += ('Acceptance Fee, '
[14385]419                     'Matriculation Gown Fee, '
420                     'Lapel/File Fee and ')
[11625]421
[14385]422        #return text + '250.0 Naira Transaction Charge.'
423
424        return text + 'Transaction Charge.'
425
[14534]426class CustomStudyCourseManageFormPage(StudyCourseManageFormPage):
427    """ Page to edit the student study course data
428    """
429    grok.context(ICustomStudentStudyCourse)
430
431    @property
432    def form_fields(self):
433        if self.context.is_postgrad:
434            form_fields = grok.AutoFields(ICustomStudentStudyCourse).omit(
435                'previous_verdict')
436        else:
437            form_fields = grok.AutoFields(ICustomStudentStudyCourse)
438        form_fields['imported_cgpa'].for_display = True
439        return form_fields
440
[14981]441class CustomUnregisterCoursesView(UnregisterCoursesView):
442    """Unregister course list by student
443    """
444    grok.context(ICustomStudentStudyLevel)
445
446    def update(self):
447        if not self.context.__parent__.is_current:
448            emit_lock_message(self)
449            return
450        try:
451            deadline = grok.getSite()['configuration'][
452                str(self.context.level_session)].coursereg_deadline
453        except (TypeError, KeyError):
454            deadline = None
455        # In AAUE fresh students are allowed to "unregister their course"
456        # aside the deadline
457        if deadline and not self.context.student.is_fresh \
458            and deadline < datetime.now(pytz.utc):
459            self.flash(_(
460                "Course registration has ended. "
461                "Unregistration is disabled."), type="warning")
462        elif str(self.context.__parent__.current_level) != self.context.__name__:
463            self.flash(_('This is not your current level.'), type="danger")
464        elif self.context.student.state == REGISTERED:
465            IWorkflowInfo(self.context.student).fireTransition('reset7')
466            message = _('Course list has been unregistered.')
467            self.flash(message)
468        else:
469            self.flash(_('You are in the wrong state.'), type="warning")
470        self.redirect(self.url(self.context))
471        return
472
[9914]473class CustomStudyLevelDisplayFormPage(StudyLevelDisplayFormPage):
474    """ Page to display student study levels
475    """
476    grok.context(ICustomStudentStudyLevel)
[10480]477    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
[14206]478        'total_credits', 'gpa', 'level', 'imported_gpa', 'imported_cgpa')
[9914]479    form_fields[
480        'validation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
481
[14000]482    @property
[15228]483    def translated_values(self):
484        return translated_values(self)
485
486    @property
[14000]487    def show_results(self):
488        isStudent = getattr(
489            self.request.principal, 'user_type', None) == 'student'
[14944]490        try:
491            show_results = grok.getSite()[
492                'configuration'][str(self.context.level_session)].show_results
493        except KeyError:
[14000]494            return False
[14944]495        if isStudent and self.context.student.current_mode not in show_results:
496            return False
497        if isStudent and self.context.student.state != RETURNING \
498            and self.context.student.current_level == self.context.level:
499            return False
[14000]500        return True
501
[13834]502class CustomStudyLevelManageFormPage(StudyLevelManageFormPage):
503    """ Page to edit the student study level data
504    """
505    grok.context(ICustomStudentStudyLevel)
506
[14534]507    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
508        'validation_date', 'validated_by', 'total_credits', 'gpa', 'level',
509        'total_credits_s1', 'total_credits_s2')
510
511    form_fields['imported_gpa'].for_display = True
512    form_fields['imported_cgpa'].for_display = True
513
[13834]514class CustomStudyLevelEditFormPage(StudyLevelEditFormPage):
515    """ Page to edit the student study level data by students
516    """
517    grok.context(ICustomStudentStudyLevel)
518
[13059]519class CustomExportPDFCourseRegistrationSlip(
520    NigeriaExportPDFCourseRegistrationSlip):
[9914]521    """Deliver a PDF slip of the context.
522    """
523    grok.context(ICustomStudentStudyLevel)
524    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
[10102]525        'level_session', 'level_verdict',
[14206]526        'validated_by', 'validation_date', 'gpa', 'level',
527        'imported_gpa', 'imported_cgpa')
[9914]528
[10269]529    omit_fields = ('password', 'suspended', 'suspended_comment',
[10689]530        'phone', 'adm_code', 'sex', 'email', 'date_of_birth',
[13713]531        'department', 'current_mode', 'current_level', 'flash_notice')
[10269]532
[14000]533    @property
534    def show_results(self):
535        isStudent = getattr(
536            self.request.principal, 'user_type', None) == 'student'
[14944]537        try:
538            show_results = grok.getSite()[
539                'configuration'][str(self.context.level_session)].show_results
540        except KeyError:
[14302]541            return False
[14944]542        if isStudent and self.context.student.current_mode not in show_results:
543            return False
544        if isStudent and self.context.student.state != RETURNING \
545            and self.context.student.current_level == self.context.level:
546            return False
[14302]547        return True
[14000]548
[13038]549    def update(self):
550        if self.context.student.state != REGISTERED \
[13051]551            and self.context.student.current_level == self.context.level:
[13038]552            self.flash(_('Forbidden'), type="warning")
553            self.redirect(self.url(self.context))
[14000]554            return
[13038]555
[9914]556    @property
557    def label(self):
558        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
559        lang = self.request.cookies.get('kofa.language', portal_language)
560        level_title = translate(self.context.level_title, 'waeup.kofa',
561            target_language=lang)
562        line0 = ''
[13788]563        if self.context.student.is_postgrad:
564            line0 = 'SCHOOL OF POSTGRADUATE STUDIES\n'
565        elif self.context.student.current_mode.endswith('_pt'):
[9914]566            line0 = 'DIRECTORATE OF PART-TIME DEGREE PROGRAMMES\n'
[13866]567        line1 = translate(_('Course Registration Slip'),
568            target_language=portal_language) \
[9914]569            + ' %s' % level_title
[13866]570        line2 = translate(_('Session'),
571            target_language=portal_language) \
[9914]572            + ' %s' % self.context.getSessionString
573        return '%s%s\n%s' % (line0, line1, line2)
574
575    @property
576    def title(self):
577        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]578        return translate(_('Units Registered'), target_language=portal_language)
[9914]579
580    def _signatures(self):
[13647]581        if self.context.student.current_mode.endswith('_pt') \
582            or self.context.student.current_mode == 'found':
583            return (
584                [('I have selected the course on the advise of my Head of '
585                 'Department. <br>', _('Student\'s Signature'), '<br>')],
586                [('This student has satisfied the department\'s requirements. '
587                 'I recommend to approve the course registration. <br>',
588                 _('Head of Department\'s Signature'), '<br>')],
[13946]589                [('' , _('Deputy Registrar\'s Signature'), '<br>')],
[13647]590                [('', _('Director\'s Signature'))]
591                )
592        if self.context.student.current_mode in (
593            'de_ft', 'ug_ft', 'dp_ft', 'transfer'):
[13649]594            return ([_('Academic Adviser\'s Signature'),
595                _('Faculty Officer\'s Signature'),
596                _('Student\'s Signature')],)
597
[13647]598        if self.context.student.current_mode in ('special_pg_ft', 'special_pg_pt'):
599            return (
[13676]600                [('I declare that all items of information supplied above are correct:' ,
[13680]601                    _('Student\'s Signature'), '<br>')],
[13676]602                [('We approved the above registration:',
[13680]603                    _('Major Supervisor (Name / Signature)'), '')],
604                [('', _('Co-Supervisor (Name / Signature)'), '')],
[13676]605                [('', _('Head of Department'), '<br>')],
606                [('The student has satisfied the conditions for renewal of '
607                  'registration for graduate school programme in this university:',
[13680]608                  _('Secretary <br /> (School of Postgraduate Studies)'), '')],
609                [('', _('Dean <br /> (School of Postgraduate Studies)'), '')],
[13647]610                )
611        return None
[9914]612
[13647]613
[9914]614    def render(self):
615        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]616        Sem = translate(_('Sem.'), target_language=portal_language)
617        Code = translate(_('Code'), target_language=portal_language)
618        Title = translate(_('Title'), target_language=portal_language)
619        Cred = translate(_('Cred.'), target_language=portal_language)
[14650]620        CC = translate(_('Cat.'), target_language=portal_language)
[14000]621        if self.show_results:
622            Score = translate(_('Score'), target_language=portal_language)
623            #CA = translate(_('CA'), target_language=portal_language)
624            Grade = translate(_('Grade'), target_language=portal_language)
[13866]625        Signature = translate(_('Lecturer\'s Signature'), 'waeup.aaue',
[9914]626            target_language=portal_language)
627        studentview = StudentBasePDFFormPage(self.context.student,
628            self.request, self.omit_fields)
629        students_utils = getUtility(IStudentsUtils)
[10442]630
631        tabledata = []
632        tableheader = []
633        contenttitle = []
634        for i in range(1,7):
635            tabledata.append(sorted(
636                [value for value in self.context.values() if value.semester == i],
637                key=lambda value: str(value.semester) + value.code))
[14000]638            if self.show_results:
639                tableheader.append([(Code,'code', 2.0),
640                                   (Title,'title', 7),
[14650]641                                   (Cred, 'credits', 1.4),
642                                   (CC, 'course_category', 1.2),
[14000]643                                   (Score, 'score', 1.4),
644                                   #(CA, 'ca', 1.4),
645                                   (Grade, 'grade', 1.4),
646                                   (Signature, 'dummy', 3),
647                                   ])
648            else:
649                tableheader.append([(Code,'code', 2.0),
650                                   (Title,'title', 7),
651                                   (Cred, 'credits', 1.5),
[14650]652                                   (CC, 'course_category', 1.2),
[14000]653                                   (Signature, 'dummy', 3),
654                                   ])
[9914]655        if len(self.label.split('\n')) == 3:
656            topMargin = 1.9
657        elif len(self.label.split('\n')) == 2:
658            topMargin = 1.7
659        else:
660            topMargin = 1.5
661        return students_utils.renderPDF(
662            self, 'course_registration_slip.pdf',
663            self.context.student, studentview,
[10442]664            tableheader=tableheader,
665            tabledata=tabledata,
[9914]666            signatures=self._signatures(),
[10269]667            topMargin=topMargin,
668            omit_fields=self.omit_fields
[9914]669            )
[10566]670
[14165]671class CustomStudyCourseTranscriptPage(StudyCourseTranscriptPage):
672    """ Page to display the student's transcript.
673    """
674    grok.require('waeup.viewStudent')
675
[13059]676class CustomExportPDFTranscriptSlip(ExportPDFTranscriptSlip):
[10566]677    """Deliver a PDF slip of the context.
678    """
[14315]679#    grok.require('waeup.viewStudent')
[10566]680
[14267]681    note = _("""
682<br /><br /><br /><br />
683<font size='10'>
684<strong>Note:</strong> This copy is subject to correction for typographical errors and ratification by the departmental board.
685</font>
686""")
687
[10566]688    def _sigsInFooter(self):
689        return []
690
691    def _signatures(self):
692        return ([(
[14999]693            'O.O OHIKHENA (Manupa)<br />Principal Asst. Registrar<br /> '
[11555]694            'Exams, Records and Data Processing Division <br /> For: Registrar')],)
[10922]695
[13834]696    def render(self):
697        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]698        Term = translate(_('Sem.'), target_language=portal_language)
699        Code = translate(_('Code'), target_language=portal_language)
700        Title = translate(_('Title'), target_language=portal_language)
701        Cred = translate(_('Credits'), target_language=portal_language)
702        Score = translate(_('Score'), target_language=portal_language)
703        Grade = translate(_('Grade'), target_language=portal_language)
[13834]704        studentview = StudentBasePDFFormPage(self.context.student,
705            self.request, self.omit_fields)
706        students_utils = getUtility(IStudentsUtils)
707
708        tableheader = [(Code,'code', 2.5),
709                         (Title,'title', 7),
710                         (Term, 'semester', 1.5),
711                         (Cred, 'credits', 1.5),
[14136]712                         (Score, 'total_score', 1.5),
[13834]713                         (Grade, 'grade', 1.5),
714                         ]
715
[15165]716        pdfstream = students_utils.renderPDFTranscript(
[13834]717            self, 'transcript.pdf',
718            self.context.student, studentview,
719            omit_fields=self.omit_fields,
720            tableheader=tableheader,
721            signatures=self._signatures(),
722            sigs_in_footer=self._sigsInFooter(),
[15165]723            digital_sigs=self._digital_sigs(),
724            save_file=self._save_file(),
[13834]725            )
[15165]726        if not pdfstream:
727            self.redirect(self.url(self.context.student))
728            return
729        return pdfstream
[13834]730
[13059]731class CustomExportPDFAdmissionSlip(ExportPDFAdmissionSlip):
[10922]732    """Deliver a PDF Admission slip.
733    """
734
735    @property
736    def label(self):
737        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]738        return translate(_('e-Admission Slip \n'),
739            target_language=portal_language) \
[10922]740            + ' %s' % self.context.display_fullname
[11597]741
[13059]742class CustomExportPDFClearanceSlip(NigeriaExportPDFClearanceSlip):
[11606]743    """Deliver a PDF slip of the context.
744    """
745
746    @property
747    def label(self):
748        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[14114]749        return translate(_('Verification/Clearance Slip\n'),
[13866]750            target_language=portal_language) \
[11606]751            + ' %s' % self.context.display_fullname
752
[14084]753    @property
754    def form_fields(self):
755        if self.context.is_postgrad:
756            form_fields = grok.AutoFields(
757                ICustomPGStudentClearance).omit('clearance_locked')
758        else:
759            form_fields = grok.AutoFields(
760                ICustomUGStudentClearance).omit('clearance_locked')
761        if not getattr(self.context, 'officer_comment'):
762            form_fields = form_fields.omit('officer_comment')
[14104]763        form_fields = form_fields.omit('def_adm')
[14084]764        return form_fields
765
[11597]766class StudentGetMatricNumberPage(UtilityView, grok.View):
767    """ Construct and set the matriculation number.
768    """
769    grok.context(IStudent)
770    grok.name('get_matric_number')
771    grok.require('waeup.viewStudent')
772
773    def update(self):
774        students_utils = getUtility(IStudentsUtils)
775        msg, mnumber = students_utils.setMatricNumber(self.context)
776        if msg:
777            self.flash(msg, type="danger")
778        else:
779            self.flash(_('Matriculation number %s assigned.' % mnumber))
[11602]780            self.context.writeLogMessage(self, '%s assigned' % mnumber)
[11597]781        self.redirect(self.url(self.context))
782        return
783
784    def render(self):
[11607]785        return
786
[13059]787class ExportPDFMatricNumberSlip(UtilityView, grok.View):
[11607]788    """Deliver a PDF notification slip.
789    """
790    grok.context(ICustomStudent)
791    grok.name('matric_number_slip.pdf')
792    grok.require('waeup.viewStudent')
793    prefix = 'form'
794
795    form_fields = grok.AutoFields(ICustomStudent).select(
796        'student_id', 'matric_number')
[13713]797    omit_fields = ('date_of_birth', 'current_level', 'flash_notice')
[11607]798
799    @property
[13489]800    def title(self):
801        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]802        return translate(_('Matriculation Number'), 'waeup.kofa',
[13489]803            target_language=portal_language)
804
805    @property
[11607]806    def label(self):
807        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]808        return translate(_('Matriculation Number Slip\n'),
809            target_language=portal_language) \
[11607]810            + ' %s' % self.context.display_fullname
811
812    def render(self):
813        if self.context.state not in (PAID,) or not self.context.is_fresh \
814            or not self.context.matric_number:
815            self.flash('Not allowed.', type="danger")
816            self.redirect(self.url(self.context))
817            return
818        students_utils = getUtility(IStudentsUtils)
[11609]819        pre_text = _('Congratulations! Your acceptance fee and school fees ' +
820                     'payments have been received and your matriculation ' +
821                     'number generated with details as follows.')
[11607]822        return students_utils.renderPDFAdmissionLetter(self,
823            self.context.student, omit_fields=self.omit_fields,
[13353]824            pre_text=pre_text, post_text='')
825
[13489]826class ExportPersonalDataSlip(UtilityView, grok.View):
827    """Deliver a PDF notification slip.
828    """
829    grok.context(ICustomStudent)
830    grok.name('personal_data_slip.pdf')
831    grok.require('waeup.viewStudent')
832    prefix = 'form'
833    note = None
834
835    form_fields = grok.AutoFields(ICustomStudentPersonal).omit('personal_updated')
[13713]836    omit_fields = ('suspended', 'suspended_comment', 'adm_code',
837                   'certificate', 'flash_notice')
[13489]838
839    @property
840    def title(self):
841        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]842        return translate(_('Personal Data'), 'waeup.kofa',
[13489]843            target_language=portal_language)
844
845    @property
846    def label(self):
847        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
[13866]848        return translate(_('Personal Data Slip\n'),
849            target_language=portal_language) \
[13489]850            + ' %s' % self.context.display_fullname
851
852    def render(self):
853        studentview = StudentBasePDFFormPage(self.context.student,
854            self.request, self.omit_fields)
855        students_utils = getUtility(IStudentsUtils)
856        return students_utils.renderPDF(self, 'personal_data_slip.pdf',
857            self.context.student, studentview, note=self.note,
858            omit_fields=self.omit_fields)
859
[13462]860class CustomAccommodationManageFormPage(NigeriaAccommodationManageFormPage):
861    """ Page to manage bed tickets.
862    This manage form page is for both students and students officers.
863    """
864    with_hostel_selection = True
865
[13353]866class CustomBedTicketAddPage(BedTicketAddPage):
[13360]867    with_ac = False
[13380]868
869class CustomStudentFilesUploadPage(StudentFilesUploadPage):
870    """ View to upload files by student. Inherit from same class in
871    base package, not from kofacustom.nigeria which
872    requires that no application slip exists.
[13770]873    """
874
875class CustomCourseTicketDisplayFormPage(CourseTicketDisplayFormPage):
876    """ Page to display course tickets
877    """
878
[14453]879    @property
880    def show_results(self):
881        isStudent = getattr(
882            self.request.principal, 'user_type', None) == 'student'
883        if isStudent:
884            return False
885        return True
886
887    @property
888    def form_fields(self):
889        if self.show_results:
890            return grok.AutoFields(ICustomCourseTicket)
891        else:
892            return grok.AutoFields(ICustomCourseTicket).omit('score').omit('ca')
893
[13770]894class CustomCourseTicketManageFormPage(CourseTicketManageFormPage):
895    """ Page to manage course tickets
896    """
897    form_fields = grok.AutoFields(ICustomCourseTicket)
898    form_fields['title'].for_display = True
899    form_fields['fcode'].for_display = True
900    form_fields['dcode'].for_display = True
901    form_fields['semester'].for_display = True
902    form_fields['passmark'].for_display = True
903    form_fields['credits'].for_display = True
904    form_fields['mandatory'].for_display = False
905    form_fields['automatic'].for_display = True
906    form_fields['carry_over'].for_display = True
[15205]907    form_fields['ticket_session'].for_display = True
[15226]908    form_fields['imported_ts'].for_display = True
[13770]909
910class CustomEditScoresPage(EditScoresPage):
911    """Page that filters and lists students.
912    """
913    grok.template('editscorespage')
914
[14700]915    def _searchCatalog(self, session):
916        cat = queryUtility(ICatalog, name='coursetickets_catalog')
917        coursetickets = cat.searchResults(
918            session=(session, session),
919            code=(self.context.code, self.context.code)
920            )
921        try:
922            score_editing_enabled = grok.getSite()[
923                'configuration'][str(session)].score_editing_enabled
924        except KeyError:
925            return []
[14707]926        coursetickets_list = [courseticket for courseticket in coursetickets
927            if courseticket.student.current_mode in score_editing_enabled]
[14700]928        return coursetickets_list
[13937]929
930    def _extract_uploadfile(self, uploadfile):
931        """Get a mapping of student-ids to scores.
932
933        The mapping is constructed by reading contents from `uploadfile`.
934
935        We expect uploadfile to be a regular CSV file with columns
936        ``student_id``, ``score`` and ``ca`` (other cols are ignored).
937        """
938        result = dict()
939        data = StringIO(uploadfile.read())  # ensure we have something seekable
940        reader = csv.DictReader(data)
941        for row in reader:
942            if not ('student_id' in row and 'score' in row and 'ca' in row):
943                continue
944            result[row['student_id']] = (row['score'], row['ca'])
945        return result
946
[14288]947    def _update_scores(self, form):
[13937]948        ob_class = self.__implemented__.__name__.replace('waeup.kofa.', '')
[14288]949        error = ''
[13937]950        if 'UPDATE_FILE' in form:
951            if form['uploadfile']:
952                try:
953                    formvals = self._extract_uploadfile(form['uploadfile'])
954                except:
955                    self.flash(
956                        _('Uploaded file contains illegal data. Ignored'),
957                        type="danger")
[14288]958                    return False
[13937]959            else:
960                self.flash(
961                    _('No file provided.'), type="danger")
[14288]962                return False
[13937]963        else:
964            formvals = dict(zip(form['sids'], zip(form['scores'], form['cas'])))
[14149]965        for ticket in self.editable_tickets:
[13937]966            ticket_error = False
967            score = ticket.score
968            ca = ticket.ca
969            sid = ticket.student.student_id
970            if formvals[sid][0] == '':
971                score = None
972            if formvals[sid][1] == '':
973                ca = None
974            try:
975                if formvals[sid][0]:
976                    score = int(formvals[sid][0])
977                if formvals[sid][1]:
978                    ca = int(formvals[sid][1])
979            except ValueError:
980                error += '%s, ' % ticket.student.display_fullname
981                ticket_error = True
982            if not ticket_error and ticket.score != score:
[14113]983                try:
984                    ticket.score = score
985                except TooBig:
986                    error += '%s, ' % ticket.student.display_fullname
987                    ticket_error = True
988                    pass
[13937]989                ticket.student.__parent__.logger.info(
990                    '%s - %s %s/%s score updated (%s)' %
991                    (ob_class, ticket.student.student_id,
992                     ticket.level, ticket.code, score))
993            if not ticket_error and ticket.ca != ca:
[14113]994                try:
995                    ticket.ca = ca
996                except TooBig:
997                    error += '%s, ' % ticket.student.display_fullname
998                    pass
[13937]999                ticket.student.__parent__.logger.info(
1000                    '%s - %s %s/%s ca updated (%s)' %
1001                    (ob_class, ticket.student.student_id,
1002                     ticket.level, ticket.code, ca))
1003        if error:
1004            self.flash(_('Error: Score(s) and CA(s) of %s have not be updated. '
[14113]1005              % error.strip(', ')), type="danger")
[14288]1006        return True
1007
1008class EditPreviousSessionScoresPage(CustomEditScoresPage):
1009
1010    grok.name('edit_prev_scores')
1011
1012    def update(self,  *args, **kw):
1013        form = self.request.form
1014        self.current_academic_session = grok.getSite()[
1015            'configuration'].current_academic_session
1016        if self.context.__parent__.__parent__.score_editing_disabled:
1017            self.flash(_('Score editing disabled.'), type="warning")
1018            self.redirect(self.url(self.context))
[13939]1019            return
[14288]1020        if not self.current_academic_session:
1021            self.flash(_('Current academic session not set.'), type="warning")
1022            self.redirect(self.url(self.context))
1023            return
1024        previous_session = self.current_academic_session - 1
1025        self.session_title = academic_sessions_vocab.getTerm(
1026            previous_session).title
1027        self.tickets = self._searchCatalog(previous_session)
1028        if not self.tickets:
1029            self.flash(_('No student found.'), type="warning")
1030            self.redirect(self.url(self.context))
1031            return
1032        self.editable_tickets = [
1033            ticket for ticket in self.tickets if ticket.editable_by_lecturer]
1034        if not 'UPDATE_TABLE' in form and not 'UPDATE_FILE' in form:
1035            return
1036        if not self.editable_tickets:
1037            return
1038        success = self._update_scores(form)
1039        if success:
1040            self.flash(_('You successfully updated course results.'))
[13900]1041        return
1042
1043class CustomExportPDFScoresSlip(ExportPDFScoresSlip):
1044    """Deliver a PDF slip of course tickets for a lecturer.
1045    """
1046
[14315]1047    def data(self, session):
[14704]1048        #site = grok.getSite()
[13900]1049        cat = queryUtility(ICatalog, name='coursetickets_catalog')
1050        coursetickets = cat.searchResults(
1051            session=(session, session),
1052            code=(self.context.code, self.context.code)
1053            )
[14707]1054        # Apply filter
1055        try:
1056            score_editing_enabled = grok.getSite()[
1057                'configuration'][str(session)].score_editing_enabled
1058            coursetickets_filtered = [courseticket
1059                for courseticket in coursetickets
[15006]1060                if courseticket.student.current_mode in score_editing_enabled
[15236]1061                and courseticket.total_score is not None
1062                and courseticket.__parent__.__parent__.is_current]
[14707]1063        except KeyError:
1064            coursetickets_filtered = coursetickets
[14149]1065        # In AAUE only editable tickets can be printed
1066        editable_tickets = [
[14707]1067            ticket for ticket in coursetickets_filtered
1068            if ticket.editable_by_lecturer]
[13963]1069        header = [[_(''),
[15006]1070                   _('Student Id'),
[13963]1071                   _('Matric No.'),
[14703]1072                   #_('Reg. No.'),
1073                   #_('Fullname'),
1074                   #_('Status'),
1075                   #_('Course of\nStudies'),
[14704]1076                   _('Department'),
[13900]1077                   _('Level'),
[15201]1078                   _(' CA  '),
1079                   _('Exam\nScore'),
[13964]1080                   _('Total '),
[13963]1081                   _('Grade'),
1082                   ],]
[14288]1083        sorted_tickets = sorted(editable_tickets,
[15199]1084            key=lambda ticket: ticket.student.depcode
1085                               + ticket.student.faccode
[14704]1086                               + ticket.student.matric_number)
[14288]1087        no = 1
[13907]1088        tickets = []
[14315]1089        passed = 0
1090        failed = 0
[15229]1091        with_ca = False
[14149]1092        # In AAUE only editable tickets can be printed
[14288]1093        for ticket in sorted_tickets:
[15229]1094            if ticket.ca > 0:
1095                with_ca = True
[15006]1096            total = ticket.total_score
[15232]1097            if getattr(ticket, 'imported_ts', None):
1098                total = Paragraph(
[15236]1099                    "<b>%s</b>" % ticket.imported_ts, STYLE["Normal"])
[15006]1100            grade = ticket._getGradeWeightFromScore[0]
1101            if grade in ('F', '-'):
1102                failed += 1
[13963]1103            else:
[15006]1104                passed += 1
[13964]1105            fullname = textwrap.fill(ticket.student.display_fullname, 30)
[14704]1106            #deptitle = site['faculties'][ticket.student.faccode][
1107            #    ticket.student.depcode].longtitle
[15240]1108            row = [str(no),
[15006]1109                  ticket.student.student_id,
[13963]1110                  ticket.student.matric_number,
[14703]1111                  #ticket.student.reg_number,
1112                  #fullname,
1113                  #ticket.student.translated_state,
1114                  #ticket.student.certcode,
[14704]1115                  ticket.student.faccode + ' / ' + ticket.student.depcode,
[13900]1116                  ticket.level,
[15201]1117                  ticket.ca,
1118                  ticket.score,
[13963]1119                  total,
[13964]1120                  grade,
[13963]1121                  ]
[13907]1122            tickets.append(row)
[13963]1123            no += 1
[14317]1124        total = passed + failed
[14320]1125        passed_perc = 0
1126        failed_perc = 0
[14317]1127        if total:
[14320]1128            passed_perc = 100 * passed / total
1129            failed_perc = 100 * failed / total
[14710]1130        dep = self.context.__parent__.__parent__.longtitle
1131        fac = self.context.__parent__.__parent__.__parent__.longtitle
[15229]1132        # remove CA column if not necessary
1133        if not with_ca:
1134            header = [[_(''),
1135                       _('Student Id'),
1136                       _('Matric No.'),
1137                       #_('Reg. No.'),
1138                       #_('Fullname'),
1139                       #_('Status'),
1140                       #_('Course of\nStudies'),
1141                       _('Department'),
1142                       _('Level'),
1143                       #_(' CA  '),
1144                       _('Exam\nScore'),
1145                       _('Total '),
1146                       _('Grade'),
1147                       ],]
1148            for ticket in tickets:
1149                del(ticket[5])
[14320]1150        return header + tickets, [
[14710]1151            dep, fac, total, passed, passed_perc, failed, failed_perc]
[14288]1152
[14703]1153    def render(self):
1154        session = grok.getSite()['configuration'].current_academic_session
1155        lecturers = [i['user_title'] for i in self.getUsersWithLocalRoles()
1156                     if i['local_role'] == 'waeup.local.Lecturer']
1157        lecturers =  ', '.join(lecturers)
1158        students_utils = getUtility(IStudentsUtils)
1159        # only orientation is different
1160        return students_utils.renderPDFCourseticketsOverview(
[15198]1161            self, session, self.data(session), lecturers, '', 45)
[14703]1162
[14288]1163class DownloadPreviousSessionScoresView(DownloadScoresView):
1164    """View that exports scores.
1165    """
1166    grok.name('download_prev_scores')
1167
1168    def update(self):
1169        self.current_academic_session = grok.getSite()[
1170            'configuration'].current_academic_session
1171        if self.context.__parent__.__parent__.score_editing_disabled:
1172            self.flash(_('Score editing disabled.'), type="warning")
1173            self.redirect(self.url(self.context))
1174            return
1175        if not self.current_academic_session:
1176            self.flash(_('Current academic session not set.'), type="warning")
1177            self.redirect(self.url(self.context))
1178            return
1179        site = grok.getSite()
1180        exporter = getUtility(ICSVExporter, name='lecturer')
1181        self.csv = exporter.export_filtered(site, filepath=None,
1182                                 catalog='coursetickets',
1183                                 session=self.current_academic_session-1,
1184                                 level=None,
1185                                 code=self.context.code)
[14306]1186        return
1187
1188class AlumniRequestPasswordPage(StudentRequestPasswordPage):
1189    """Captcha'd request password page for students.
1190    """
1191    grok.name('alumni_requestpw')
1192    grok.require('waeup.Anonymous')
1193    grok.template('alumni_requestpw')
1194    form_fields = grok.AutoFields(IStudentRequestPW).select(
1195        'lastname','number','email')
1196    label = _('Search student record and send password for first-time login')
1197
1198    def _redirect_no_student(self):
1199        self.flash(_('No student record found.'), type="warning")
1200        self.redirect(self.application_url() + '/applicants/trans2017/register')
[14288]1201        return
Note: See TracBrowser for help on using the repository browser.