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

Last change on this file since 15362 was 15343, checked in by Henrik Bettermann, 6 years ago

Re-activate etranzact module.

Change note on CustomExportPDFScoresSlip.

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