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

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

No outstanding courses on course registration slip.

  • Property svn:keywords set to Id
File size: 52.4 KB
Line 
1## $Id: browser.py 16041 2020-03-16 09:15:27Z 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        if not self.context.student.current_mode:
679            self.flash('No certificate assigned.', type="danger")
680            self.redirect(self.url(self.context))
681            return
682        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
683        Sem = translate(_('Sem.'), target_language=portal_language)
684        Code = translate(_('Code'), target_language=portal_language)
685        Title = translate(_('Title'), target_language=portal_language)
686        Cred = translate(_('Cred.'), target_language=portal_language)
687        CC = translate(_('Cat.'), target_language=portal_language)
688        if self.show_results:
689            TotalScore = translate(_('Total Score'), target_language=portal_language)
690            #CA = translate(_('CA'), target_language=portal_language)
691            Grade = translate(_('Grade'), target_language=portal_language)
692        Signature = translate(_('Lecturer\'s Signature'), 'waeup.aaue',
693            target_language=portal_language)
694        studentview = StudentBasePDFFormPage(self.context.student,
695            self.request, self.omit_fields)
696        students_utils = getUtility(IStudentsUtils)
697
698        tabledata = []
699        tableheader = []
700        contenttitle = []
701        for i in range(1,7):
702            tabledata.append(sorted(
703                [value for value in self.context.values()
704                 if value.semester == i and not value.outstanding],
705                 key=lambda value: str(value.semester) + value.code))
706            if self.show_results:
707                tableheader.append([(Code,'code', 2.0),
708                                   (Title,'title', 7),
709                                   (Cred, 'credits', 1.4),
710                                   (CC, 'course_category', 1.2),
711                                   (TotalScore, 'total_score', 1.4),
712                                   #(CA, 'ca', 1.4),
713                                   (Grade, 'grade', 1.4),
714                                   (Signature, 'dummy', 3),
715                                   ])
716            else:
717                tableheader.append([(Code,'code', 2.0),
718                                   (Title,'title', 7),
719                                   (Cred, 'credits', 1.5),
720                                   (CC, 'course_category', 1.2),
721                                   (Signature, 'dummy', 3),
722                                   ])
723        if len(self.label.split('\n')) == 3:
724            topMargin = 1.9
725        elif len(self.label.split('\n')) == 2:
726            topMargin = 1.7
727        else:
728            topMargin = 1.5
729        return students_utils.renderPDF(
730            self, 'course_registration_slip.pdf',
731            self.context.student, studentview,
732            tableheader=tableheader,
733            tabledata=tabledata,
734            signatures=self._signatures(),
735            topMargin=topMargin,
736            omit_fields=self.omit_fields
737            )
738
739class CustomStudyCourseTranscriptPage(StudyCourseTranscriptPage):
740    """ Page to display the student's transcript.
741    """
742    grok.require('waeup.viewStudent')
743
744class CustomExportPDFTranscriptSlip(ExportPDFTranscriptSlip):
745    """Deliver a PDF slip of the context.
746    """
747
748    note = _("""
749<br /><br /><br /><br />
750<font size='10'>
751<strong>Note:</strong> This copy is subject to correction for typographical errors and ratification by the departmental board.
752</font>
753""")
754
755    def _sigsInFooter(self):
756        return []
757
758    def _signatures(self):
759        if self.context.student.state == GRADUATED:
760            return ([(
761                'O.O OHIKHENA (Manupa)<br />'
762                'Principal Asst. Registrar<br /> '
763                'Exams, Records and Data Processing Division <br /> '
764                'For: Registrar')],)
765        elif self.context.certificate is not None:
766            dep = self.context.certificate.__parent__.__parent__
767            if dep.officer_1:
768                return ([(dep.officer_1)],)
769        return
770
771    def render(self):
772        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
773        Term = translate(_('Sem.'), target_language=portal_language)
774        Code = translate(_('Code'), target_language=portal_language)
775        Title = translate(_('Title'), target_language=portal_language)
776        Cred = translate(_('Credits'), target_language=portal_language)
777        Score = translate(_('Score'), target_language=portal_language)
778        Grade = translate(_('Grade'), target_language=portal_language)
779        studentview = StudentBasePDFFormPage(self.context.student,
780            self.request, self.omit_fields)
781        students_utils = getUtility(IStudentsUtils)
782
783        tableheader = [(Code,'code', 2.5),
784                         (Title,'title', 7),
785                         (Term, 'semester', 1.5),
786                         (Cred, 'credits', 1.5),
787                         (Score, 'total_score', 1.5),
788                         (Grade, 'grade', 1.5),
789                         ]
790
791        pdfstream = students_utils.renderPDFTranscript(
792            self, 'transcript.pdf',
793            self.context.student, studentview,
794            omit_fields=self.omit_fields,
795            tableheader=tableheader,
796            signatures=self._signatures(),
797            sigs_in_footer=self._sigsInFooter(),
798            digital_sigs=self._digital_sigs(),
799            save_file=self._save_file(),
800            )
801        if not pdfstream:
802            self.redirect(self.url(self.context.student))
803            return
804        return pdfstream
805
806class CustomExportPDFAdmissionSlip(ExportPDFAdmissionSlip):
807    """Deliver a PDF Admission slip.
808    """
809
810    @property
811    def label(self):
812        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
813        return translate(_('e-Admission Slip \n'),
814            target_language=portal_language) \
815            + ' %s' % self.context.display_fullname
816
817class CustomExportPDFClearanceSlip(NigeriaExportPDFClearanceSlip):
818    """Deliver a PDF slip of the context.
819    """
820
821    @property
822    def label(self):
823        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
824        return translate(_('Verification/Clearance Slip\n'),
825            target_language=portal_language) \
826            + ' %s' % self.context.display_fullname
827
828    @property
829    def form_fields(self):
830        if self.context.is_postgrad:
831            form_fields = grok.AutoFields(
832                ICustomPGStudentClearance).omit('clearance_locked')
833        else:
834            form_fields = grok.AutoFields(
835                ICustomUGStudentClearance).omit('clearance_locked')
836        if not getattr(self.context, 'officer_comment'):
837            form_fields = form_fields.omit('officer_comment')
838        form_fields = form_fields.omit('def_adm')
839        return form_fields
840
841class StudentGetMatricNumberPage(UtilityView, grok.View):
842    """ Construct and set the matriculation number.
843    """
844    grok.context(IStudent)
845    grok.name('get_matric_number')
846    grok.require('waeup.viewStudent')
847
848    def update(self):
849        students_utils = getUtility(IStudentsUtils)
850        msg, mnumber = students_utils.setMatricNumber(self.context)
851        if msg:
852            self.flash(msg, type="danger")
853        else:
854            self.flash(_('Matriculation number %s assigned.' % mnumber))
855            self.context.writeLogMessage(self, '%s assigned' % mnumber)
856        self.redirect(self.url(self.context))
857        return
858
859    def render(self):
860        return
861
862class ExportPDFMatricNumberSlip(UtilityView, grok.View):
863    """Deliver a PDF notification slip.
864    """
865    grok.context(ICustomStudent)
866    grok.name('matric_number_slip.pdf')
867    grok.require('waeup.viewStudent')
868    prefix = 'form'
869
870    form_fields = grok.AutoFields(ICustomStudent).select(
871        'student_id', 'matric_number')
872    omit_fields = ('date_of_birth', 'current_level', 'flash_notice')
873
874    @property
875    def title(self):
876        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
877        return translate(_('Matriculation Number'), 'waeup.kofa',
878            target_language=portal_language)
879
880    @property
881    def label(self):
882        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
883        return translate(_('Matriculation Number Slip\n'),
884            target_language=portal_language) \
885            + ' %s' % self.context.display_fullname
886
887    def render(self):
888        if self.context.state not in (PAID,) or not self.context.is_fresh \
889            or not self.context.matric_number:
890            self.flash('Not allowed.', type="danger")
891            self.redirect(self.url(self.context))
892            return
893        students_utils = getUtility(IStudentsUtils)
894        pre_text = _('Congratulations! Your acceptance fee and school fees ' +
895                     'payments have been received and your matriculation ' +
896                     'number generated with details as follows.')
897        return students_utils.renderPDFAdmissionLetter(self,
898            self.context.student, omit_fields=self.omit_fields,
899            pre_text=pre_text, post_text='')
900
901class ExportPersonalDataSlip(UtilityView, grok.View):
902    """Deliver a PDF notification slip.
903    """
904    grok.context(ICustomStudent)
905    grok.name('personal_data_slip.pdf')
906    grok.require('waeup.viewStudent')
907    prefix = 'form'
908    note = None
909
910    form_fields = grok.AutoFields(ICustomStudentPersonal).omit('personal_updated')
911    omit_fields = ('suspended', 'suspended_comment', 'adm_code',
912                   'certificate', 'flash_notice')
913
914    @property
915    def title(self):
916        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
917        return translate(_('Personal Data'), 'waeup.kofa',
918            target_language=portal_language)
919
920    @property
921    def label(self):
922        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
923        return translate(_('Personal Data Slip\n'),
924            target_language=portal_language) \
925            + ' %s' % self.context.display_fullname
926
927    def render(self):
928        studentview = StudentBasePDFFormPage(self.context.student,
929            self.request, self.omit_fields)
930        students_utils = getUtility(IStudentsUtils)
931        return students_utils.renderPDF(self, 'personal_data_slip.pdf',
932            self.context.student, studentview, note=self.note,
933            omit_fields=self.omit_fields)
934
935class CustomAccommodationDisplayFormPage(NigeriaAccommodationDisplayFormPage):
936    """ Page to view bed tickets.
937    """
938    with_hostel_selection = True
939
940class CustomAccommodationManageFormPage(NigeriaAccommodationManageFormPage):
941    """ Page to manage bed tickets.
942    This manage form page is for both students and students officers.
943    """
944    with_hostel_selection = True
945
946class CustomBedTicketAddPage(NigeriaBedTicketAddPage):
947    with_ac = False
948
949class CustomStudentFilesUploadPage(StudentFilesUploadPage):
950    """ View to upload files by student. Inherit from same class in
951    base package, not from kofacustom.nigeria which
952    requires that no application slip exists.
953    """
954
955class CustomCourseTicketDisplayFormPage(CourseTicketDisplayFormPage):
956    """ Page to display course tickets
957    """
958
959    @property
960    def show_results(self):
961        isStudent = getattr(
962            self.request.principal, 'user_type', None) == 'student'
963        if isStudent:
964            return False
965        return True
966
967    @property
968    def form_fields(self):
969        if self.show_results:
970            return grok.AutoFields(ICustomCourseTicket)
971        else:
972            return grok.AutoFields(ICustomCourseTicket).omit('score').omit('ca')
973
974class CustomCourseTicketManageFormPage(CourseTicketManageFormPage):
975    """ Page to manage course tickets
976    """
977    form_fields = grok.AutoFields(ICustomCourseTicket)
978    form_fields['title'].for_display = True
979    form_fields['fcode'].for_display = True
980    form_fields['dcode'].for_display = True
981    form_fields['semester'].for_display = True
982    form_fields['passmark'].for_display = True
983    form_fields['credits'].for_display = True
984    form_fields['mandatory'].for_display = False
985    form_fields['automatic'].for_display = True
986    form_fields['carry_over'].for_display = True
987    form_fields['ticket_session'].for_display = True
988    form_fields['imported_ts'].for_display = True
989
990class CustomEditScoresPage(EditScoresPage):
991    """Page that filters and lists students.
992    """
993    grok.template('editscorespage')
994
995    def _searchCatalog(self, session):
996        cat = queryUtility(ICatalog, name='coursetickets_catalog')
997        coursetickets = cat.searchResults(
998            session=(session, session),
999            code=(self.context.code, self.context.code)
1000            )
1001        try:
1002            score_editing_enabled = grok.getSite()[
1003                'configuration'][str(session)].score_editing_enabled
1004        except KeyError:
1005            return []
1006        coursetickets_list = [courseticket for courseticket in coursetickets
1007            if courseticket.student.current_mode in score_editing_enabled]
1008        return coursetickets_list
1009
1010    def _extract_uploadfile(self, uploadfile):
1011        """Get a mapping of student-ids to scores.
1012
1013        The mapping is constructed by reading contents from `uploadfile`.
1014
1015        We expect uploadfile to be a regular CSV file with columns
1016        ``student_id``, ``score``, ``imported_ts``
1017        and ``ca`` (other cols are ignored).
1018        """
1019        result = dict()
1020        data = StringIO(uploadfile.read())  # ensure we have something seekable
1021        reader = csv.DictReader(data)
1022        for row in reader:
1023            if not ('student_id' in row and 'score' in row and 'ca' in row and
1024                'imported_ts' in row):
1025                continue
1026            result[row['student_id']] = (
1027                row['score'], row['ca'], row['imported_ts'])
1028        return result
1029
1030    def _update_scores(self, form):
1031        ob_class = self.__implemented__.__name__.replace('waeup.kofa.', '')
1032        error = ''
1033        if 'UPDATE_FILE' in form:
1034            if form['uploadfile']:
1035                try:
1036                    formvals = self._extract_uploadfile(form['uploadfile'])
1037                except:
1038                    self.flash(
1039                        _('Uploaded file contains illegal data. Ignored'),
1040                        type="danger")
1041                    return False
1042            else:
1043                self.flash(
1044                    _('No file provided.'), type="danger")
1045                return False
1046        else:
1047            formvals = dict(zip(form['sids'], zip(
1048                form['scores'], form['cas'], form['imported_ts'])))
1049        for ticket in self.editable_tickets:
1050            ticket_error = False
1051            score = ticket.score
1052            ca = ticket.ca
1053            imported_ts = ticket.imported_ts
1054            sid = ticket.student.student_id
1055            if sid not in formvals:
1056                continue
1057            if formvals[sid][0] == '':
1058                score = None
1059            if formvals[sid][1] == '':
1060                ca = None
1061            if formvals[sid][2] == '':
1062                imported_ts = None
1063            try:
1064                if formvals[sid][0]:
1065                    score = int(formvals[sid][0])
1066                if formvals[sid][1]:
1067                    ca = int(formvals[sid][1])
1068                if formvals[sid][2]:
1069                    imported_ts = int(formvals[sid][2])
1070            except ValueError:
1071                error += '%s, ' % ticket.student.display_fullname
1072                ticket_error = True
1073            if not ticket_error and ticket.score != score:
1074                try:
1075                    ticket.score = score
1076                except TooBig:
1077                    error += '%s, ' % ticket.student.display_fullname
1078                    ticket_error = True
1079                    pass
1080                ticket.student.__parent__.logger.info(
1081                    '%s - %s %s/%s score updated (%s)' %
1082                    (ob_class, ticket.student.student_id,
1083                     ticket.level, ticket.code, score))
1084            if not ticket_error and ticket.ca != ca:
1085                try:
1086                    ticket.ca = ca
1087                except TooBig:
1088                    error += '%s, ' % ticket.student.display_fullname
1089                    pass
1090                ticket.student.__parent__.logger.info(
1091                    '%s - %s %s/%s ca updated (%s)' %
1092                    (ob_class, ticket.student.student_id,
1093                     ticket.level, ticket.code, ca))
1094            if not ticket_error and ticket.imported_ts != imported_ts:
1095                try:
1096                    ticket.imported_ts = imported_ts
1097                except TooBig:
1098                    error += '%s, ' % ticket.student.display_fullname
1099                    pass
1100                ticket.student.__parent__.logger.info(
1101                    '%s - %s %s/%s imported_ts updated (%s)' %
1102                    (ob_class, ticket.student.student_id,
1103                     ticket.level, ticket.code, imported_ts))
1104        if error:
1105            self.flash(_('Error: Score(s), CA(s) and Imported TS(s) of %s have not be updated. '
1106              % error.strip(', ')), type="danger")
1107        return True
1108
1109class EditPreviousSessionScoresPage(CustomEditScoresPage):
1110
1111    grok.name('edit_prev_scores')
1112
1113    def update(self,  *args, **kw):
1114        form = self.request.form
1115        self.current_academic_session = grok.getSite()[
1116            'configuration'].current_academic_session
1117        if self.context.__parent__.__parent__.score_editing_disabled:
1118            self.flash(_('Score editing disabled.'), type="warning")
1119            self.redirect(self.url(self.context))
1120            return
1121        if not self.current_academic_session:
1122            self.flash(_('Current academic session not set.'), type="warning")
1123            self.redirect(self.url(self.context))
1124            return
1125        previous_session = self.current_academic_session - 1
1126        self.session_title = academic_sessions_vocab.getTerm(
1127            previous_session).title
1128        self.tickets = self._searchCatalog(previous_session)
1129        if not self.tickets:
1130            self.flash(_('No student found.'), type="warning")
1131            self.redirect(self.url(self.context))
1132            return
1133        self.editable_tickets = [
1134            ticket for ticket in self.tickets if ticket.editable_by_lecturer]
1135        if not 'UPDATE_TABLE' in form and not 'UPDATE_FILE' in form:
1136            return
1137        if not self.editable_tickets:
1138            return
1139        success = self._update_scores(form)
1140        if success:
1141            self.flash(_('You successfully updated course results.'))
1142        return
1143
1144class CustomExportPDFScoresSlip(ExportPDFScoresSlip):
1145    """Deliver a PDF slip of course tickets for a lecturer.
1146    """
1147
1148    note = u'\nUpgraded scores are with asterisks.'
1149
1150    def data(self, session):
1151        #site = grok.getSite()
1152        cat = queryUtility(ICatalog, name='coursetickets_catalog')
1153        coursetickets = cat.searchResults(
1154            session=(session, session),
1155            code=(self.context.code, self.context.code)
1156            )
1157        # Apply filter
1158        try:
1159            score_editing_enabled = grok.getSite()[
1160                'configuration'][str(session)].score_editing_enabled
1161            if checkPermission('waeup.manageAcademics', self.context):
1162                score_editing_enabled = True
1163            coursetickets_filtered = [courseticket
1164                for courseticket in coursetickets
1165                if (checkPermission('waeup.manageAcademics', self.context)
1166                    or (courseticket.student.current_mode in
1167                        score_editing_enabled))
1168                and courseticket.total_score is not None
1169                and courseticket.__parent__.__parent__.is_current]
1170        except KeyError:
1171            coursetickets_filtered = coursetickets
1172        # In AAUE only editable tickets can be printed (deactivated on 02/10/19)
1173        #editable_tickets = [
1174        #    ticket for ticket in coursetickets_filtered
1175        #    if ticket.editable_by_lecturer]
1176        header = [[_(''),
1177                   _('Student Id'),
1178                   _('Matric No.'),
1179                   #_('Reg. No.'),
1180                   #_('Fullname'),
1181                   #_('Status'),
1182                   #_('Course of\nStudies'),
1183                   _('Department'),
1184                   _('Level'),
1185                   _(' CA  '),
1186                   _('Exam\nScore'),
1187                   _('Total '),
1188                   _('Grade'),
1189                   ],]
1190        sorted_tickets = sorted(coursetickets_filtered, # editable_tickets,
1191            key=lambda ticket: str(ticket.student.depcode)
1192                               + str(ticket.student.faccode)
1193                               + str(ticket.student.matric_number)
1194                               )
1195        no = 1
1196        tickets = []
1197        passed = 0
1198        failed = 0
1199        with_ca = False
1200        grade_stats = {'A':0, 'B':0, 'C':0, 'D':0, 'E':0, 'F':0, }
1201        for ticket in sorted_tickets:
1202            if ticket.ca > 0:
1203                with_ca = True
1204            total = ticket.total_score
1205            if getattr(ticket, 'imported_ts', None):
1206                total = "**%s**" % ticket.imported_ts
1207            grade = ticket._getGradeWeightFromScore[0]
1208            if grade in grade_stats.keys():
1209                grade_stats[grade] += 1
1210            if grade in ('F', '-'):
1211                failed += 1
1212            else:
1213                passed += 1
1214            fullname = textwrap.fill(ticket.student.display_fullname, 30)
1215            #deptitle = site['faculties'][ticket.student.faccode][
1216            #    ticket.student.depcode].longtitle
1217            row = [str(no),
1218                  ticket.student.student_id,
1219                  ticket.student.matric_number,
1220                  #ticket.student.reg_number,
1221                  #fullname,
1222                  #ticket.student.translated_state,
1223                  #ticket.student.certcode,
1224                  str(ticket.student.faccode) + ' / ' + str(ticket.student.depcode),
1225                  ticket.level,
1226                  ticket.ca,
1227                  ticket.score,
1228                  total,
1229                  grade,
1230                  ]
1231            tickets.append(row)
1232            no += 1
1233        total = passed + failed
1234        passed_perc = 0
1235        failed_perc = 0
1236        if total:
1237            passed_perc = round(100.0 * passed / total)
1238            failed_perc = round(100.0 * failed / total)
1239        dep = self.context.__parent__.__parent__.longtitle
1240        fac = self.context.__parent__.__parent__.__parent__.longtitle
1241        # remove CA column if not necessary
1242        if not with_ca:
1243            header = [[_(''),
1244                       _('Student Id'),
1245                       _('Matric No.'),
1246                       #_('Reg. No.'),
1247                       #_('Fullname'),
1248                       #_('Status'),
1249                       #_('Course of\nStudies'),
1250                       _('Department'),
1251                       _('Level'),
1252                       #_(' CA  '),
1253                       _('Exam\nScore'),
1254                       _('Total '),
1255                       _('Grade'),
1256                       ],]
1257            for ticket in tickets:
1258                del(ticket[5])
1259        return header + tickets, [
1260            dep, fac, total, passed, passed_perc, failed, failed_perc, grade_stats]
1261
1262    def render(self):
1263        session = grok.getSite()['configuration'].current_academic_session
1264        lecturers = [i['user_title'] for i in self.getUsersWithLocalRoles()
1265                     if i['local_role'] == 'waeup.local.Lecturer']
1266        lecturers = sorted(lecturers)
1267        lecturers =  ', '.join(lecturers)
1268        students_utils = getUtility(IStudentsUtils)
1269        # only orientation is different
1270        return students_utils.renderPDFCourseticketsOverview(
1271            self, 'coursetickets',
1272            session, self.data(session), lecturers, '', 45, self.note)
1273
1274class DownloadPreviousSessionScoresView(DownloadScoresView):
1275    """View that exports scores.
1276    """
1277    grok.name('download_prev_scores')
1278
1279    def update(self):
1280        self.current_academic_session = grok.getSite()[
1281            'configuration'].current_academic_session
1282        if self.context.__parent__.__parent__.score_editing_disabled:
1283            self.flash(_('Score editing disabled.'), type="warning")
1284            self.redirect(self.url(self.context))
1285            return
1286        if not self.current_academic_session:
1287            self.flash(_('Current academic session not set.'), type="warning")
1288            self.redirect(self.url(self.context))
1289            return
1290        site = grok.getSite()
1291        exporter = getUtility(ICSVExporter, name='lecturer')
1292        self.csv = exporter.export_filtered(site, filepath=None,
1293                                 catalog='coursetickets',
1294                                 session=self.current_academic_session-1,
1295                                 level=None,
1296                                 code=self.context.code)
1297        return
1298
1299class AlumniRequestPasswordPage(StudentRequestPasswordPage):
1300    """Captcha'd request password page for students.
1301    """
1302    grok.name('alumni_requestpw')
1303    grok.require('waeup.Anonymous')
1304    grok.template('alumni_requestpw')
1305    form_fields = grok.AutoFields(IStudentRequestPW).select(
1306        'lastname','number','email')
1307    label = _('Search student record and send password for first-time login')
1308
1309    def _redirect_no_student(self):
1310        self.flash(_('No student record found.'), type="warning")
1311        self.redirect(self.application_url() + '/applicants/trans2017/register')
1312        return
1313
1314class CustomAddStudyLevelFormPage(AddStudyLevelFormPage):
1315    """ This page is temporarily locked.
1316    """
1317
1318    def update(self):
1319        if self.context.student.current_mode == 'ug_ft' \
1320            and self.context.student.current_session == 2019:
1321            emit_lock_message(self)
1322            return
1323        super(CustomAddStudyLevelFormPage, self).update()
1324        return
Note: See TracBrowser for help on using the repository browser.