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

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

Add officer_1 signature if student is not graduated.

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