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

Last change on this file since 16189 was 16143, checked in by Henrik Bettermann, 4 years ago

Revert r15919.

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