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

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

Use string for sorting.

  • Property svn:keywords set to Id
File size: 51.7 KB
Line 
1## $Id: browser.py 15911 2020-01-08 10:03:02Z henrik $
2##
3## Copyright (C) 2012 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18import grok
19import csv
20import textwrap
21import pytz
22from cStringIO import StringIO
23from datetime import datetime
24from zope.i18n import translate
25from zope.component import getUtility, queryUtility
26from zope.schema.interfaces import TooBig, TooSmall
27from zope.security import checkPermission
28from zope.catalog.interfaces import ICatalog
29from zope.formlib.textwidgets import BytesDisplayWidget
30from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
31from waeup.kofa.browser.layout import UtilityView, KofaEditFormPage, jsaction
32from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget
33from waeup.kofa.interfaces import (
34    IKofaUtils, academic_sessions_vocab, ICSVExporter, IKofaObject)
35from waeup.kofa.students.interfaces import (
36    IStudentsUtils, IStudent, IStudentRequestPW, IStudentStudyLevel)
37from waeup.kofa.students.workflow import PAID, REGISTERED, RETURNING, GRADUATED
38from waeup.kofa.students.browser import (
39    StartClearancePage,
40    StudentBasePDFFormPage,
41    CourseTicketAddFormPage,
42    StudyLevelDisplayFormPage,
43    StudyLevelManageFormPage,
44    StudyLevelEditFormPage,
45    ExportPDFTranscriptSlip,
46    ExportPDFAdmissionSlip,
47    StudentFilesUploadPage,
48    PaymentsManageFormPage,
49    CourseTicketDisplayFormPage,
50    CourseTicketManageFormPage,
51    EditScoresPage,
52    ExportPDFScoresSlip,
53    StudyCourseTranscriptPage,
54    DownloadScoresView,
55    StudentRequestPasswordPage,
56    StudyCourseManageFormPage,
57    UnregisterCoursesView,
58    addCourseTicket,
59    AddStudyLevelFormPage,
60    emit_lock_message
61    )
62from kofacustom.nigeria.students.browser import (
63    NigeriaOnlinePaymentDisplayFormPage,
64    NigeriaOnlinePaymentAddFormPage,
65    NigeriaExportPDFPaymentSlip,
66    NigeriaExportPDFCourseRegistrationSlip,
67    NigeriaStudentPersonalDisplayFormPage,
68    NigeriaStudentPersonalEditFormPage,
69    NigeriaStudentPersonalManageFormPage,
70    NigeriaStudentClearanceDisplayFormPage,
71    NigeriaExportPDFClearanceSlip,
72    NigeriaStudentClearanceManageFormPage,
73    NigeriaStudentClearanceEditFormPage,
74    NigeriaAccommodationManageFormPage,
75    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
742    note = _("""
743<br /><br /><br /><br />
744<font size='10'>
745<strong>Note:</strong> This copy is subject to correction for typographical errors and ratification by the departmental board.
746</font>
747""")
748
749    def _sigsInFooter(self):
750        return []
751
752    def _signatures(self):
753        if self.context.student.state == GRADUATED:
754            return ([(
755                'O.O OHIKHENA (Manupa)<br />'
756                'Principal Asst. Registrar<br /> '
757                'Exams, Records and Data Processing Division <br /> '
758                'For: Registrar')],)
759        return
760
761
762    def render(self):
763        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
764        Term = translate(_('Sem.'), target_language=portal_language)
765        Code = translate(_('Code'), target_language=portal_language)
766        Title = translate(_('Title'), target_language=portal_language)
767        Cred = translate(_('Credits'), target_language=portal_language)
768        Score = translate(_('Score'), target_language=portal_language)
769        Grade = translate(_('Grade'), target_language=portal_language)
770        studentview = StudentBasePDFFormPage(self.context.student,
771            self.request, self.omit_fields)
772        students_utils = getUtility(IStudentsUtils)
773
774        tableheader = [(Code,'code', 2.5),
775                         (Title,'title', 7),
776                         (Term, 'semester', 1.5),
777                         (Cred, 'credits', 1.5),
778                         (Score, 'total_score', 1.5),
779                         (Grade, 'grade', 1.5),
780                         ]
781
782        pdfstream = students_utils.renderPDFTranscript(
783            self, 'transcript.pdf',
784            self.context.student, studentview,
785            omit_fields=self.omit_fields,
786            tableheader=tableheader,
787            signatures=self._signatures(),
788            sigs_in_footer=self._sigsInFooter(),
789            digital_sigs=self._digital_sigs(),
790            save_file=self._save_file(),
791            )
792        if not pdfstream:
793            self.redirect(self.url(self.context.student))
794            return
795        return pdfstream
796
797class CustomExportPDFAdmissionSlip(ExportPDFAdmissionSlip):
798    """Deliver a PDF Admission slip.
799    """
800
801    @property
802    def label(self):
803        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
804        return translate(_('e-Admission Slip \n'),
805            target_language=portal_language) \
806            + ' %s' % self.context.display_fullname
807
808class CustomExportPDFClearanceSlip(NigeriaExportPDFClearanceSlip):
809    """Deliver a PDF slip of the context.
810    """
811
812    @property
813    def label(self):
814        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
815        return translate(_('Verification/Clearance Slip\n'),
816            target_language=portal_language) \
817            + ' %s' % self.context.display_fullname
818
819    @property
820    def form_fields(self):
821        if self.context.is_postgrad:
822            form_fields = grok.AutoFields(
823                ICustomPGStudentClearance).omit('clearance_locked')
824        else:
825            form_fields = grok.AutoFields(
826                ICustomUGStudentClearance).omit('clearance_locked')
827        if not getattr(self.context, 'officer_comment'):
828            form_fields = form_fields.omit('officer_comment')
829        form_fields = form_fields.omit('def_adm')
830        return form_fields
831
832class StudentGetMatricNumberPage(UtilityView, grok.View):
833    """ Construct and set the matriculation number.
834    """
835    grok.context(IStudent)
836    grok.name('get_matric_number')
837    grok.require('waeup.viewStudent')
838
839    def update(self):
840        students_utils = getUtility(IStudentsUtils)
841        msg, mnumber = students_utils.setMatricNumber(self.context)
842        if msg:
843            self.flash(msg, type="danger")
844        else:
845            self.flash(_('Matriculation number %s assigned.' % mnumber))
846            self.context.writeLogMessage(self, '%s assigned' % mnumber)
847        self.redirect(self.url(self.context))
848        return
849
850    def render(self):
851        return
852
853class ExportPDFMatricNumberSlip(UtilityView, grok.View):
854    """Deliver a PDF notification slip.
855    """
856    grok.context(ICustomStudent)
857    grok.name('matric_number_slip.pdf')
858    grok.require('waeup.viewStudent')
859    prefix = 'form'
860
861    form_fields = grok.AutoFields(ICustomStudent).select(
862        'student_id', 'matric_number')
863    omit_fields = ('date_of_birth', 'current_level', 'flash_notice')
864
865    @property
866    def title(self):
867        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
868        return translate(_('Matriculation Number'), 'waeup.kofa',
869            target_language=portal_language)
870
871    @property
872    def label(self):
873        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
874        return translate(_('Matriculation Number Slip\n'),
875            target_language=portal_language) \
876            + ' %s' % self.context.display_fullname
877
878    def render(self):
879        if self.context.state not in (PAID,) or not self.context.is_fresh \
880            or not self.context.matric_number:
881            self.flash('Not allowed.', type="danger")
882            self.redirect(self.url(self.context))
883            return
884        students_utils = getUtility(IStudentsUtils)
885        pre_text = _('Congratulations! Your acceptance fee and school fees ' +
886                     'payments have been received and your matriculation ' +
887                     'number generated with details as follows.')
888        return students_utils.renderPDFAdmissionLetter(self,
889            self.context.student, omit_fields=self.omit_fields,
890            pre_text=pre_text, post_text='')
891
892class ExportPersonalDataSlip(UtilityView, grok.View):
893    """Deliver a PDF notification slip.
894    """
895    grok.context(ICustomStudent)
896    grok.name('personal_data_slip.pdf')
897    grok.require('waeup.viewStudent')
898    prefix = 'form'
899    note = None
900
901    form_fields = grok.AutoFields(ICustomStudentPersonal).omit('personal_updated')
902    omit_fields = ('suspended', 'suspended_comment', 'adm_code',
903                   'certificate', 'flash_notice')
904
905    @property
906    def title(self):
907        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
908        return translate(_('Personal Data'), 'waeup.kofa',
909            target_language=portal_language)
910
911    @property
912    def label(self):
913        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
914        return translate(_('Personal Data Slip\n'),
915            target_language=portal_language) \
916            + ' %s' % self.context.display_fullname
917
918    def render(self):
919        studentview = StudentBasePDFFormPage(self.context.student,
920            self.request, self.omit_fields)
921        students_utils = getUtility(IStudentsUtils)
922        return students_utils.renderPDF(self, 'personal_data_slip.pdf',
923            self.context.student, studentview, note=self.note,
924            omit_fields=self.omit_fields)
925
926class CustomAccommodationManageFormPage(NigeriaAccommodationManageFormPage):
927    """ Page to manage bed tickets.
928    This manage form page is for both students and students officers.
929    """
930    with_hostel_selection = True
931
932class CustomBedTicketAddPage(NigeriaBedTicketAddPage):
933    with_ac = False
934
935class CustomStudentFilesUploadPage(StudentFilesUploadPage):
936    """ View to upload files by student. Inherit from same class in
937    base package, not from kofacustom.nigeria which
938    requires that no application slip exists.
939    """
940
941class CustomCourseTicketDisplayFormPage(CourseTicketDisplayFormPage):
942    """ Page to display course tickets
943    """
944
945    @property
946    def show_results(self):
947        isStudent = getattr(
948            self.request.principal, 'user_type', None) == 'student'
949        if isStudent:
950            return False
951        return True
952
953    @property
954    def form_fields(self):
955        if self.show_results:
956            return grok.AutoFields(ICustomCourseTicket)
957        else:
958            return grok.AutoFields(ICustomCourseTicket).omit('score').omit('ca')
959
960class CustomCourseTicketManageFormPage(CourseTicketManageFormPage):
961    """ Page to manage course tickets
962    """
963    form_fields = grok.AutoFields(ICustomCourseTicket)
964    form_fields['title'].for_display = True
965    form_fields['fcode'].for_display = True
966    form_fields['dcode'].for_display = True
967    form_fields['semester'].for_display = True
968    form_fields['passmark'].for_display = True
969    form_fields['credits'].for_display = True
970    form_fields['mandatory'].for_display = False
971    form_fields['automatic'].for_display = True
972    form_fields['carry_over'].for_display = True
973    form_fields['ticket_session'].for_display = True
974    form_fields['imported_ts'].for_display = True
975
976class CustomEditScoresPage(EditScoresPage):
977    """Page that filters and lists students.
978    """
979    grok.template('editscorespage')
980
981    def _searchCatalog(self, session):
982        cat = queryUtility(ICatalog, name='coursetickets_catalog')
983        coursetickets = cat.searchResults(
984            session=(session, session),
985            code=(self.context.code, self.context.code)
986            )
987        try:
988            score_editing_enabled = grok.getSite()[
989                'configuration'][str(session)].score_editing_enabled
990        except KeyError:
991            return []
992        coursetickets_list = [courseticket for courseticket in coursetickets
993            if courseticket.student.current_mode in score_editing_enabled]
994        return coursetickets_list
995
996    def _extract_uploadfile(self, uploadfile):
997        """Get a mapping of student-ids to scores.
998
999        The mapping is constructed by reading contents from `uploadfile`.
1000
1001        We expect uploadfile to be a regular CSV file with columns
1002        ``student_id``, ``score``, ``imported_ts``
1003        and ``ca`` (other cols are ignored).
1004        """
1005        result = dict()
1006        data = StringIO(uploadfile.read())  # ensure we have something seekable
1007        reader = csv.DictReader(data)
1008        for row in reader:
1009            if not ('student_id' in row and 'score' in row and 'ca' in row and
1010                'imported_ts' in row):
1011                continue
1012            result[row['student_id']] = (
1013                row['score'], row['ca'], row['imported_ts'])
1014        return result
1015
1016    def _update_scores(self, form):
1017        ob_class = self.__implemented__.__name__.replace('waeup.kofa.', '')
1018        error = ''
1019        if 'UPDATE_FILE' in form:
1020            if form['uploadfile']:
1021                try:
1022                    formvals = self._extract_uploadfile(form['uploadfile'])
1023                except:
1024                    self.flash(
1025                        _('Uploaded file contains illegal data. Ignored'),
1026                        type="danger")
1027                    return False
1028            else:
1029                self.flash(
1030                    _('No file provided.'), type="danger")
1031                return False
1032        else:
1033            formvals = dict(zip(form['sids'], zip(
1034                form['scores'], form['cas'], form['imported_ts'])))
1035        for ticket in self.editable_tickets:
1036            ticket_error = False
1037            score = ticket.score
1038            ca = ticket.ca
1039            imported_ts = ticket.imported_ts
1040            sid = ticket.student.student_id
1041            if sid not in formvals:
1042                continue
1043            if formvals[sid][0] == '':
1044                score = None
1045            if formvals[sid][1] == '':
1046                ca = None
1047            if formvals[sid][2] == '':
1048                imported_ts = None
1049            try:
1050                if formvals[sid][0]:
1051                    score = int(formvals[sid][0])
1052                if formvals[sid][1]:
1053                    ca = int(formvals[sid][1])
1054                if formvals[sid][2]:
1055                    imported_ts = int(formvals[sid][2])
1056            except ValueError:
1057                error += '%s, ' % ticket.student.display_fullname
1058                ticket_error = True
1059            if not ticket_error and ticket.score != score:
1060                try:
1061                    ticket.score = score
1062                except TooBig:
1063                    error += '%s, ' % ticket.student.display_fullname
1064                    ticket_error = True
1065                    pass
1066                ticket.student.__parent__.logger.info(
1067                    '%s - %s %s/%s score updated (%s)' %
1068                    (ob_class, ticket.student.student_id,
1069                     ticket.level, ticket.code, score))
1070            if not ticket_error and ticket.ca != ca:
1071                try:
1072                    ticket.ca = ca
1073                except TooBig:
1074                    error += '%s, ' % ticket.student.display_fullname
1075                    pass
1076                ticket.student.__parent__.logger.info(
1077                    '%s - %s %s/%s ca updated (%s)' %
1078                    (ob_class, ticket.student.student_id,
1079                     ticket.level, ticket.code, ca))
1080            if not ticket_error and ticket.imported_ts != imported_ts:
1081                try:
1082                    ticket.imported_ts = imported_ts
1083                except TooBig:
1084                    error += '%s, ' % ticket.student.display_fullname
1085                    pass
1086                ticket.student.__parent__.logger.info(
1087                    '%s - %s %s/%s imported_ts updated (%s)' %
1088                    (ob_class, ticket.student.student_id,
1089                     ticket.level, ticket.code, imported_ts))
1090        if error:
1091            self.flash(_('Error: Score(s), CA(s) and Imported TS(s) of %s have not be updated. '
1092              % error.strip(', ')), type="danger")
1093        return True
1094
1095class EditPreviousSessionScoresPage(CustomEditScoresPage):
1096
1097    grok.name('edit_prev_scores')
1098
1099    def update(self,  *args, **kw):
1100        form = self.request.form
1101        self.current_academic_session = grok.getSite()[
1102            'configuration'].current_academic_session
1103        if self.context.__parent__.__parent__.score_editing_disabled:
1104            self.flash(_('Score editing disabled.'), type="warning")
1105            self.redirect(self.url(self.context))
1106            return
1107        if not self.current_academic_session:
1108            self.flash(_('Current academic session not set.'), type="warning")
1109            self.redirect(self.url(self.context))
1110            return
1111        previous_session = self.current_academic_session - 1
1112        self.session_title = academic_sessions_vocab.getTerm(
1113            previous_session).title
1114        self.tickets = self._searchCatalog(previous_session)
1115        if not self.tickets:
1116            self.flash(_('No student found.'), type="warning")
1117            self.redirect(self.url(self.context))
1118            return
1119        self.editable_tickets = [
1120            ticket for ticket in self.tickets if ticket.editable_by_lecturer]
1121        if not 'UPDATE_TABLE' in form and not 'UPDATE_FILE' in form:
1122            return
1123        if not self.editable_tickets:
1124            return
1125        success = self._update_scores(form)
1126        if success:
1127            self.flash(_('You successfully updated course results.'))
1128        return
1129
1130class CustomExportPDFScoresSlip(ExportPDFScoresSlip):
1131    """Deliver a PDF slip of course tickets for a lecturer.
1132    """
1133
1134    note = u'\nUpgraded scores are with asterisks.'
1135
1136    def data(self, session):
1137        #site = grok.getSite()
1138        cat = queryUtility(ICatalog, name='coursetickets_catalog')
1139        coursetickets = cat.searchResults(
1140            session=(session, session),
1141            code=(self.context.code, self.context.code)
1142            )
1143        # Apply filter
1144        try:
1145            score_editing_enabled = grok.getSite()[
1146                'configuration'][str(session)].score_editing_enabled
1147            if checkPermission('waeup.manageAcademics', self.context):
1148                score_editing_enabled = True
1149            coursetickets_filtered = [courseticket
1150                for courseticket in coursetickets
1151                if (checkPermission('waeup.manageAcademics', self.context)
1152                    or (courseticket.student.current_mode in
1153                        score_editing_enabled))
1154                and courseticket.total_score is not None
1155                and courseticket.__parent__.__parent__.is_current]
1156        except KeyError:
1157            coursetickets_filtered = coursetickets
1158        # In AAUE only editable tickets can be printed (deactivated on 02/10/19)
1159        #editable_tickets = [
1160        #    ticket for ticket in coursetickets_filtered
1161        #    if ticket.editable_by_lecturer]
1162        header = [[_(''),
1163                   _('Student Id'),
1164                   _('Matric No.'),
1165                   #_('Reg. No.'),
1166                   #_('Fullname'),
1167                   #_('Status'),
1168                   #_('Course of\nStudies'),
1169                   _('Department'),
1170                   _('Level'),
1171                   _(' CA  '),
1172                   _('Exam\nScore'),
1173                   _('Total '),
1174                   _('Grade'),
1175                   ],]
1176        sorted_tickets = sorted(coursetickets_filtered, # editable_tickets,
1177            key=lambda ticket: ticket.student.depcode
1178                               + ticket.student.faccode
1179                               + str(ticket.student.matric_number)
1180                               )
1181        no = 1
1182        tickets = []
1183        passed = 0
1184        failed = 0
1185        with_ca = False
1186        grade_stats = {'A':0, 'B':0, 'C':0, 'D':0, 'E':0, 'F':0, }
1187        for ticket in sorted_tickets:
1188            if ticket.ca > 0:
1189                with_ca = True
1190            total = ticket.total_score
1191            if getattr(ticket, 'imported_ts', None):
1192                total = "**%s**" % ticket.imported_ts
1193            grade = ticket._getGradeWeightFromScore[0]
1194            if grade in grade_stats.keys():
1195                grade_stats[grade] += 1
1196            if grade in ('F', '-'):
1197                failed += 1
1198            else:
1199                passed += 1
1200            fullname = textwrap.fill(ticket.student.display_fullname, 30)
1201            #deptitle = site['faculties'][ticket.student.faccode][
1202            #    ticket.student.depcode].longtitle
1203            row = [str(no),
1204                  ticket.student.student_id,
1205                  ticket.student.matric_number,
1206                  #ticket.student.reg_number,
1207                  #fullname,
1208                  #ticket.student.translated_state,
1209                  #ticket.student.certcode,
1210                  ticket.student.faccode + ' / ' + ticket.student.depcode,
1211                  ticket.level,
1212                  ticket.ca,
1213                  ticket.score,
1214                  total,
1215                  grade,
1216                  ]
1217            tickets.append(row)
1218            no += 1
1219        total = passed + failed
1220        passed_perc = 0
1221        failed_perc = 0
1222        if total:
1223            passed_perc = round(100.0 * passed / total)
1224            failed_perc = round(100.0 * failed / total)
1225        dep = self.context.__parent__.__parent__.longtitle
1226        fac = self.context.__parent__.__parent__.__parent__.longtitle
1227        # remove CA column if not necessary
1228        if not with_ca:
1229            header = [[_(''),
1230                       _('Student Id'),
1231                       _('Matric No.'),
1232                       #_('Reg. No.'),
1233                       #_('Fullname'),
1234                       #_('Status'),
1235                       #_('Course of\nStudies'),
1236                       _('Department'),
1237                       _('Level'),
1238                       #_(' CA  '),
1239                       _('Exam\nScore'),
1240                       _('Total '),
1241                       _('Grade'),
1242                       ],]
1243            for ticket in tickets:
1244                del(ticket[5])
1245        return header + tickets, [
1246            dep, fac, total, passed, passed_perc, failed, failed_perc, grade_stats]
1247
1248    def render(self):
1249        session = grok.getSite()['configuration'].current_academic_session
1250        lecturers = [i['user_title'] for i in self.getUsersWithLocalRoles()
1251                     if i['local_role'] == 'waeup.local.Lecturer']
1252        lecturers = sorted(lecturers)
1253        lecturers =  ', '.join(lecturers)
1254        students_utils = getUtility(IStudentsUtils)
1255        # only orientation is different
1256        return students_utils.renderPDFCourseticketsOverview(
1257            self, 'coursetickets',
1258            session, self.data(session), lecturers, '', 45, self.note)
1259
1260class DownloadPreviousSessionScoresView(DownloadScoresView):
1261    """View that exports scores.
1262    """
1263    grok.name('download_prev_scores')
1264
1265    def update(self):
1266        self.current_academic_session = grok.getSite()[
1267            'configuration'].current_academic_session
1268        if self.context.__parent__.__parent__.score_editing_disabled:
1269            self.flash(_('Score editing disabled.'), type="warning")
1270            self.redirect(self.url(self.context))
1271            return
1272        if not self.current_academic_session:
1273            self.flash(_('Current academic session not set.'), type="warning")
1274            self.redirect(self.url(self.context))
1275            return
1276        site = grok.getSite()
1277        exporter = getUtility(ICSVExporter, name='lecturer')
1278        self.csv = exporter.export_filtered(site, filepath=None,
1279                                 catalog='coursetickets',
1280                                 session=self.current_academic_session-1,
1281                                 level=None,
1282                                 code=self.context.code)
1283        return
1284
1285class AlumniRequestPasswordPage(StudentRequestPasswordPage):
1286    """Captcha'd request password page for students.
1287    """
1288    grok.name('alumni_requestpw')
1289    grok.require('waeup.Anonymous')
1290    grok.template('alumni_requestpw')
1291    form_fields = grok.AutoFields(IStudentRequestPW).select(
1292        'lastname','number','email')
1293    label = _('Search student record and send password for first-time login')
1294
1295    def _redirect_no_student(self):
1296        self.flash(_('No student record found.'), type="warning")
1297        self.redirect(self.application_url() + '/applicants/trans2017/register')
1298        return
1299
1300class CustomAddStudyLevelFormPage(AddStudyLevelFormPage):
1301    """ This page is temporarily locked.
1302    """
1303
1304    def update(self):
1305        if self.context.student.current_mode == 'ug_ft' \
1306            and self.context.student.current_session == 2019:
1307            emit_lock_message(self)
1308            return
1309        super(CustomAddStudyLevelFormPage, self).update()
1310        return
Note: See TracBrowser for help on using the repository browser.