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

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

Remove bug.

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