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

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

Temporarily we allow students to pay for next session, so their current_session might have increased.

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