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

Last change on this file since 17959 was 17958, checked in by Henrik Bettermann, 7 days ago

Change signature again.

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