## $Id: browser.py 14385 2017-01-10 21:31:22Z henrik $
##
## Copyright (C) 2012 Uli Fouquet & Henrik Bettermann
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
import grok
import csv
import textwrap
from cStringIO import StringIO
from zope.i18n import translate
from zope.component import getUtility, queryUtility
from zope.schema.interfaces import TooBig, TooSmall
from zope.security import checkPermission
from zope.catalog.interfaces import ICatalog
from zope.formlib.textwidgets import BytesDisplayWidget
from waeup.kofa.browser.layout import UtilityView
from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget
from waeup.kofa.interfaces import (
IKofaUtils, academic_sessions_vocab, ICSVExporter, IKofaObject)
from waeup.kofa.students.interfaces import (
IStudentsUtils, IStudent, IStudentRequestPW)
from waeup.kofa.students.workflow import PAID, REGISTERED, RETURNING
from waeup.kofa.students.studylevel import getGradeWeightFromScore
from waeup.kofa.students.browser import (
StartClearancePage,
StudentBasePDFFormPage,
CourseTicketAddFormPage,
StudyLevelDisplayFormPage,
StudyLevelManageFormPage,
StudyLevelEditFormPage,
ExportPDFTranscriptSlip,
ExportPDFAdmissionSlip,
BedTicketAddPage,
StudentFilesUploadPage,
PaymentsManageFormPage,
CourseTicketDisplayFormPage,
CourseTicketManageFormPage,
EditScoresPage,
ExportPDFScoresSlip,
StudyCourseTranscriptPage,
DownloadScoresView,
StudentRequestPasswordPage
)
from kofacustom.nigeria.students.browser import (
NigeriaOnlinePaymentDisplayFormPage,
NigeriaOnlinePaymentAddFormPage,
NigeriaExportPDFPaymentSlip,
NigeriaExportPDFCourseRegistrationSlip,
NigeriaStudentPersonalDisplayFormPage,
NigeriaStudentPersonalEditFormPage,
NigeriaStudentPersonalManageFormPage,
NigeriaStudentClearanceDisplayFormPage,
NigeriaExportPDFClearanceSlip,
NigeriaStudentClearanceManageFormPage,
NigeriaStudentClearanceEditFormPage,
NigeriaAccommodationManageFormPage,
NigeriaStudentBaseDisplayFormPage,
NigeriaStudentBaseManageFormPage
)
from waeup.aaue.students.interfaces import (
ICustomStudentOnlinePayment,
ICustomStudentStudyLevel,
ICustomStudent,
ICustomStudentPersonal,
ICustomStudentPersonalEdit,
ICustomUGStudentClearance,
ICustomUGStudentClearanceEdit,
ICustomPGStudentClearance,
ICustomCourseTicket,
ICustomStudentBase)
from waeup.aaue.interswitch.browser import gateway_net_amt
from waeup.aaue.interfaces import MessageFactory as _
grok.context(IKofaObject) # Make IKofaObject the default context
class CustomStudentBaseDisplayFormPage(NigeriaStudentBaseDisplayFormPage):
""" Page to display student base data
"""
form_fields = grok.AutoFields(ICustomStudentBase).omit(
'password', 'suspended', 'suspended_comment', 'flash_notice')
form_fields[
'financial_clearance_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
class CustomStudentBaseManageFormPage(NigeriaStudentBaseManageFormPage):
""" View to manage student base data
"""
form_fields = grok.AutoFields(ICustomStudentBase).omit(
'student_id', 'adm_code', 'suspended',
'financially_cleared_by', 'financial_clearance_date')
class CustomStudentPersonalDisplayFormPage(NigeriaStudentPersonalDisplayFormPage):
""" Page to display student personal data
"""
form_fields = grok.AutoFields(ICustomStudentPersonal)
form_fields['perm_address'].custom_widget = BytesDisplayWidget
form_fields['father_address'].custom_widget = BytesDisplayWidget
form_fields['mother_address'].custom_widget = BytesDisplayWidget
form_fields['next_kin_address'].custom_widget = BytesDisplayWidget
form_fields[
'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
class CustomStudentPersonalEditFormPage(NigeriaStudentPersonalEditFormPage):
""" Page to edit personal data
"""
form_fields = grok.AutoFields(ICustomStudentPersonalEdit).omit('personal_updated')
class CustomStudentPersonalManageFormPage(NigeriaStudentPersonalManageFormPage):
""" Page to edit personal data
"""
form_fields = grok.AutoFields(ICustomStudentPersonal)
form_fields['personal_updated'].for_display = True
form_fields[
'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
class CustomStudentClearanceDisplayFormPage(NigeriaStudentClearanceDisplayFormPage):
""" Page to display student clearance data
"""
@property
def form_fields(self):
if self.context.is_postgrad:
form_fields = grok.AutoFields(
ICustomPGStudentClearance).omit('clearance_locked')
else:
form_fields = grok.AutoFields(
ICustomUGStudentClearance).omit('clearance_locked')
if not getattr(self.context, 'officer_comment'):
form_fields = form_fields.omit('officer_comment')
else:
form_fields['officer_comment'].custom_widget = BytesDisplayWidget
form_fields = form_fields.omit('def_adm')
return form_fields
class CustomStudentClearanceManageFormPage(NigeriaStudentClearanceManageFormPage):
""" Page to edit student clearance data
"""
@property
def form_fields(self):
if self.context.is_postgrad:
form_fields = grok.AutoFields(
ICustomPGStudentClearance).omit('clr_code')
else:
form_fields = grok.AutoFields(
ICustomUGStudentClearance).omit('clr_code')
form_fields = form_fields.omit('def_adm')
return form_fields
class CustomStudentClearanceEditFormPage(NigeriaStudentClearanceEditFormPage):
""" View to edit student clearance data by student
"""
@property
def form_fields(self):
if self.context.is_postgrad:
form_fields = grok.AutoFields(ICustomPGStudentClearance).omit(
'clearance_locked', 'nysc_location', 'clr_code', 'officer_comment',
'physical_clearance_date')
else:
form_fields = grok.AutoFields(ICustomUGStudentClearanceEdit).omit(
'clearance_locked', 'clr_code', 'officer_comment',
'physical_clearance_date', 'date_of_birth', 'nationality', 'lga')
form_fields = form_fields.omit('def_adm')
return form_fields
class CustomStartClearancePage(StartClearancePage):
with_ac = False
@property
def all_required_fields_filled(self):
if not self.context.email:
return _("Email address is missing."), 'edit_base'
if not self.context.phone:
return _("Phone number is missing."), 'edit_base'
if not self.context.father_name:
return _("Personal data form is not properly filled."), 'edit_personal'
return
class CustomOnlinePaymentDisplayFormPage(NigeriaOnlinePaymentDisplayFormPage):
""" Page to view an online payment ticket
"""
grok.context(ICustomStudentOnlinePayment)
form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
form_fields[
'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
form_fields[
'payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
class CustomOnlinePaymentAddFormPage(NigeriaOnlinePaymentAddFormPage):
""" Page to add an online payment ticket
"""
form_fields = grok.AutoFields(ICustomStudentOnlinePayment).select(
'p_category')
alumni_payment_cats = {
'transcript_local': 'Transcript Fee Local',
'transcript_inter': 'Transcript Fee International',
}
@property
def selectable_categories(self):
if 'alumni' in self.application_url():
return self.alumni_payment_cats.items()
categories = getUtility(IKofaUtils).SELECTABLE_PAYMENT_CATEGORIES
return sorted(categories.items())
class CustomPaymentsManageFormPage(PaymentsManageFormPage):
""" Page to manage the student payments.
This manage form page is for both students and students officers.
"""
@property
def manage_payments_allowed(self):
return checkPermission('waeup.manageStudent', self.context)
class CustomExportPDFPaymentSlip(NigeriaExportPDFPaymentSlip):
"""Deliver a PDF slip of the context.
"""
grok.context(ICustomStudentOnlinePayment)
form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
@property
def note(self):
p_session = self.context.p_session
try:
academic_session = grok.getSite()['configuration'][str(p_session)]
except KeyError:
academic_session = None
text = '\n\n The Amount Authorized is inclusive of: '
if self.context.p_category in ('schoolfee_incl', 'schoolfee_1') \
and academic_session:
#welfare_fee = gateway_net_amt(academic_session.welfare_fee)
#union_fee = gateway_net_amt(academic_session.union_fee)
if self.context.student.entry_session == 2016 \
and self.context.student.entry_mode == 'ug_ft' \
and self.context.p_session == 2016:
# Add student id card fee to first school fee payment.
## Attention: The payment slip does not contain any information
## whether the fee was added or not.
## We can only draw conclusions from from the student's entry
## session whether the fee had been included.
#id_card_fee = gateway_net_amt(academic_session.id_card_fee)
#text += ('School Fee, '
# '%s Naira Student ID Card Fee, '
# '%s Naira Student Union Dues, '
# '%s Naira Student Welfare Assurance Fee and '
# % (id_card_fee, union_fee, welfare_fee))
text += ('School Fee, '
'Student ID Card Fee, '
'Student Union Dues, '
'Student Welfare Assurance Fee and ')
else:
#text += ('School Fee, '
# '%s Naira Student Union Dues, '
# '%s Naira Student Welfare Assurance Fee and '
# % (union_fee, welfare_fee))
text += ('School Fee, '
'Student Union Dues, '
'Student Welfare Assurance Fee and ')
elif self.context.p_category in (
'clearance_incl', 'clearance_medical_incl') and academic_session:
#matric_gown_fee = gateway_net_amt(academic_session.matric_gown_fee)
#lapel_fee = gateway_net_amt(academic_session.lapel_fee)
#text += ('Acceptance Fee, '
# '%s Naira Matriculation Gown Fee, '
# '%s Naira Lapel/File Fee and '
# % (matric_gown_fee, lapel_fee))
text += ('Acceptance Fee, '
'Matriculation Gown Fee, '
'Lapel/File Fee and ')
#return text + '250.0 Naira Transaction Charge.'
return text + 'Transaction Charge.'
class CustomStudyLevelDisplayFormPage(StudyLevelDisplayFormPage):
""" Page to display student study levels
"""
grok.context(ICustomStudentStudyLevel)
form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
'total_credits', 'gpa', 'level', 'imported_gpa', 'imported_cgpa')
form_fields[
'validation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
@property
def show_results(self):
isStudent = getattr(
self.request.principal, 'user_type', None) == 'student'
# Temporarily disabled on 1/12/2016
if isStudent:
return False
#if isStudent and self.context.student.state != RETURNING \
# and self.context.student.current_level == self.context.level:
# return False
return True
class CustomStudyLevelManageFormPage(StudyLevelManageFormPage):
""" Page to edit the student study level data
"""
grok.context(ICustomStudentStudyLevel)
class CustomStudyLevelEditFormPage(StudyLevelEditFormPage):
""" Page to edit the student study level data by students
"""
grok.context(ICustomStudentStudyLevel)
class CustomExportPDFCourseRegistrationSlip(
NigeriaExportPDFCourseRegistrationSlip):
"""Deliver a PDF slip of the context.
"""
grok.context(ICustomStudentStudyLevel)
form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
'level_session', 'level_verdict',
'validated_by', 'validation_date', 'gpa', 'level',
'imported_gpa', 'imported_cgpa')
omit_fields = ('password', 'suspended', 'suspended_comment',
'phone', 'adm_code', 'sex', 'email', 'date_of_birth',
'department', 'current_mode', 'current_level', 'flash_notice')
@property
def show_results(self):
isStudent = getattr(
self.request.principal, 'user_type', None) == 'student'
# Temporarily disabled on 1/12/2016
if isStudent:
return False
#if isStudent and self.context.student.state != RETURNING \
# and self.context.student.current_level == self.context.level:
# return False
return True
def update(self):
if self.context.student.state != REGISTERED \
and self.context.student.current_level == self.context.level:
self.flash(_('Forbidden'), type="warning")
self.redirect(self.url(self.context))
return
@property
def label(self):
portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
lang = self.request.cookies.get('kofa.language', portal_language)
level_title = translate(self.context.level_title, 'waeup.kofa',
target_language=lang)
line0 = ''
if self.context.student.is_postgrad:
line0 = 'SCHOOL OF POSTGRADUATE STUDIES\n'
elif self.context.student.current_mode.endswith('_pt'):
line0 = 'DIRECTORATE OF PART-TIME DEGREE PROGRAMMES\n'
line1 = translate(_('Course Registration Slip'),
target_language=portal_language) \
+ ' %s' % level_title
line2 = translate(_('Session'),
target_language=portal_language) \
+ ' %s' % self.context.getSessionString
return '%s%s\n%s' % (line0, line1, line2)
@property
def title(self):
portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
return translate(_('Units Registered'), target_language=portal_language)
def _signatures(self):
if self.context.student.current_mode.endswith('_pt') \
or self.context.student.current_mode == 'found':
return (
[('I have selected the course on the advise of my Head of '
'Department.
', _('Student\'s Signature'), '
')],
[('This student has satisfied the department\'s requirements. '
'I recommend to approve the course registration.
',
_('Head of Department\'s Signature'), '
')],
[('' , _('Deputy Registrar\'s Signature'), '
')],
[('', _('Director\'s Signature'))]
)
if self.context.student.current_mode in (
'de_ft', 'ug_ft', 'dp_ft', 'transfer'):
return ([_('Academic Adviser\'s Signature'),
_('Faculty Officer\'s Signature'),
_('Student\'s Signature')],)
if self.context.student.current_mode in ('special_pg_ft', 'special_pg_pt'):
return (
[('I declare that all items of information supplied above are correct:' ,
_('Student\'s Signature'), '
')],
[('We approved the above registration:',
_('Major Supervisor (Name / Signature)'), '')],
[('', _('Co-Supervisor (Name / Signature)'), '')],
[('', _('Head of Department'), '
')],
[('The student has satisfied the conditions for renewal of '
'registration for graduate school programme in this university:',
_('Secretary
(School of Postgraduate Studies)'), '')],
[('', _('Dean
(School of Postgraduate Studies)'), '')],
)
return None
def render(self):
portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
Sem = translate(_('Sem.'), target_language=portal_language)
Code = translate(_('Code'), target_language=portal_language)
Title = translate(_('Title'), target_language=portal_language)
Cred = translate(_('Cred.'), target_language=portal_language)
if self.show_results:
Score = translate(_('Score'), target_language=portal_language)
#CA = translate(_('CA'), target_language=portal_language)
Grade = translate(_('Grade'), target_language=portal_language)
Signature = translate(_('Lecturer\'s Signature'), 'waeup.aaue',
target_language=portal_language)
studentview = StudentBasePDFFormPage(self.context.student,
self.request, self.omit_fields)
students_utils = getUtility(IStudentsUtils)
tabledata = []
tableheader = []
contenttitle = []
for i in range(1,7):
tabledata.append(sorted(
[value for value in self.context.values() if value.semester == i],
key=lambda value: str(value.semester) + value.code))
if self.show_results:
tableheader.append([(Code,'code', 2.0),
(Title,'title', 7),
(Cred, 'credits', 1.5),
(Score, 'score', 1.4),
#(CA, 'ca', 1.4),
(Grade, 'grade', 1.4),
(Signature, 'dummy', 3),
])
else:
tableheader.append([(Code,'code', 2.0),
(Title,'title', 7),
(Cred, 'credits', 1.5),
(Signature, 'dummy', 3),
])
if len(self.label.split('\n')) == 3:
topMargin = 1.9
elif len(self.label.split('\n')) == 2:
topMargin = 1.7
else:
topMargin = 1.5
return students_utils.renderPDF(
self, 'course_registration_slip.pdf',
self.context.student, studentview,
tableheader=tableheader,
tabledata=tabledata,
signatures=self._signatures(),
topMargin=topMargin,
omit_fields=self.omit_fields
)
class CustomStudyCourseTranscriptPage(StudyCourseTranscriptPage):
""" Page to display the student's transcript.
"""
grok.require('waeup.viewStudent')
class CustomExportPDFTranscriptSlip(ExportPDFTranscriptSlip):
"""Deliver a PDF slip of the context.
"""
# grok.require('waeup.viewStudent')
note = _("""
Note: This copy is subject to correction for typographical errors and ratification by the departmental board.
""")
def _sigsInFooter(self):
return []
def _signatures(self):
return ([(
'Mrs. Uniamikogbo, S.O., mnim, manupa
Prin. Asst Registrar
'
'Exams, Records and Data Processing Division
For: Registrar')],)
def render(self):
portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
Term = translate(_('Sem.'), target_language=portal_language)
Code = translate(_('Code'), target_language=portal_language)
Title = translate(_('Title'), target_language=portal_language)
Cred = translate(_('Credits'), target_language=portal_language)
Score = translate(_('Score'), target_language=portal_language)
Grade = translate(_('Grade'), target_language=portal_language)
studentview = StudentBasePDFFormPage(self.context.student,
self.request, self.omit_fields)
students_utils = getUtility(IStudentsUtils)
tableheader = [(Code,'code', 2.5),
(Title,'title', 7),
(Term, 'semester', 1.5),
(Cred, 'credits', 1.5),
(Score, 'total_score', 1.5),
(Grade, 'grade', 1.5),
]
return students_utils.renderPDFTranscript(
self, 'transcript.pdf',
self.context.student, studentview,
omit_fields=self.omit_fields,
tableheader=tableheader,
signatures=self._signatures(),
sigs_in_footer=self._sigsInFooter(),
note = self.note,
no_passport=True
)
class CustomExportPDFAdmissionSlip(ExportPDFAdmissionSlip):
"""Deliver a PDF Admission slip.
"""
@property
def label(self):
portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
return translate(_('e-Admission Slip \n'),
target_language=portal_language) \
+ ' %s' % self.context.display_fullname
class CustomExportPDFClearanceSlip(NigeriaExportPDFClearanceSlip):
"""Deliver a PDF slip of the context.
"""
@property
def label(self):
portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
return translate(_('Verification/Clearance Slip\n'),
target_language=portal_language) \
+ ' %s' % self.context.display_fullname
@property
def form_fields(self):
if self.context.is_postgrad:
form_fields = grok.AutoFields(
ICustomPGStudentClearance).omit('clearance_locked')
else:
form_fields = grok.AutoFields(
ICustomUGStudentClearance).omit('clearance_locked')
if not getattr(self.context, 'officer_comment'):
form_fields = form_fields.omit('officer_comment')
form_fields = form_fields.omit('def_adm')
return form_fields
class StudentGetMatricNumberPage(UtilityView, grok.View):
""" Construct and set the matriculation number.
"""
grok.context(IStudent)
grok.name('get_matric_number')
grok.require('waeup.viewStudent')
def update(self):
students_utils = getUtility(IStudentsUtils)
msg, mnumber = students_utils.setMatricNumber(self.context)
if msg:
self.flash(msg, type="danger")
else:
self.flash(_('Matriculation number %s assigned.' % mnumber))
self.context.writeLogMessage(self, '%s assigned' % mnumber)
self.redirect(self.url(self.context))
return
def render(self):
return
class ExportPDFMatricNumberSlip(UtilityView, grok.View):
"""Deliver a PDF notification slip.
"""
grok.context(ICustomStudent)
grok.name('matric_number_slip.pdf')
grok.require('waeup.viewStudent')
prefix = 'form'
form_fields = grok.AutoFields(ICustomStudent).select(
'student_id', 'matric_number')
omit_fields = ('date_of_birth', 'current_level', 'flash_notice')
@property
def title(self):
portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
return translate(_('Matriculation Number'), 'waeup.kofa',
target_language=portal_language)
@property
def label(self):
portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
return translate(_('Matriculation Number Slip\n'),
target_language=portal_language) \
+ ' %s' % self.context.display_fullname
def render(self):
if self.context.state not in (PAID,) or not self.context.is_fresh \
or not self.context.matric_number:
self.flash('Not allowed.', type="danger")
self.redirect(self.url(self.context))
return
students_utils = getUtility(IStudentsUtils)
pre_text = _('Congratulations! Your acceptance fee and school fees ' +
'payments have been received and your matriculation ' +
'number generated with details as follows.')
return students_utils.renderPDFAdmissionLetter(self,
self.context.student, omit_fields=self.omit_fields,
pre_text=pre_text, post_text='')
class ExportPersonalDataSlip(UtilityView, grok.View):
"""Deliver a PDF notification slip.
"""
grok.context(ICustomStudent)
grok.name('personal_data_slip.pdf')
grok.require('waeup.viewStudent')
prefix = 'form'
note = None
form_fields = grok.AutoFields(ICustomStudentPersonal).omit('personal_updated')
omit_fields = ('suspended', 'suspended_comment', 'adm_code',
'certificate', 'flash_notice')
@property
def title(self):
portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
return translate(_('Personal Data'), 'waeup.kofa',
target_language=portal_language)
@property
def label(self):
portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
return translate(_('Personal Data Slip\n'),
target_language=portal_language) \
+ ' %s' % self.context.display_fullname
def render(self):
studentview = StudentBasePDFFormPage(self.context.student,
self.request, self.omit_fields)
students_utils = getUtility(IStudentsUtils)
return students_utils.renderPDF(self, 'personal_data_slip.pdf',
self.context.student, studentview, note=self.note,
omit_fields=self.omit_fields)
class CustomAccommodationManageFormPage(NigeriaAccommodationManageFormPage):
""" Page to manage bed tickets.
This manage form page is for both students and students officers.
"""
with_hostel_selection = True
class CustomBedTicketAddPage(BedTicketAddPage):
with_ac = False
class CustomStudentFilesUploadPage(StudentFilesUploadPage):
""" View to upload files by student. Inherit from same class in
base package, not from kofacustom.nigeria which
requires that no application slip exists.
"""
class CustomCourseTicketDisplayFormPage(CourseTicketDisplayFormPage):
""" Page to display course tickets
"""
form_fields = grok.AutoFields(ICustomCourseTicket)
class CustomCourseTicketManageFormPage(CourseTicketManageFormPage):
""" Page to manage course tickets
"""
form_fields = grok.AutoFields(ICustomCourseTicket)
form_fields['title'].for_display = True
form_fields['fcode'].for_display = True
form_fields['dcode'].for_display = True
form_fields['semester'].for_display = True
form_fields['passmark'].for_display = True
form_fields['credits'].for_display = True
form_fields['mandatory'].for_display = False
form_fields['automatic'].for_display = True
form_fields['carry_over'].for_display = True
class CustomEditScoresPage(EditScoresPage):
"""Page that filters and lists students.
"""
grok.template('editscorespage')
def _extract_uploadfile(self, uploadfile):
"""Get a mapping of student-ids to scores.
The mapping is constructed by reading contents from `uploadfile`.
We expect uploadfile to be a regular CSV file with columns
``student_id``, ``score`` and ``ca`` (other cols are ignored).
"""
result = dict()
data = StringIO(uploadfile.read()) # ensure we have something seekable
reader = csv.DictReader(data)
for row in reader:
if not ('student_id' in row and 'score' in row and 'ca' in row):
continue
result[row['student_id']] = (row['score'], row['ca'])
return result
def _update_scores(self, form):
ob_class = self.__implemented__.__name__.replace('waeup.kofa.', '')
error = ''
if 'UPDATE_FILE' in form:
if form['uploadfile']:
try:
formvals = self._extract_uploadfile(form['uploadfile'])
except:
self.flash(
_('Uploaded file contains illegal data. Ignored'),
type="danger")
return False
else:
self.flash(
_('No file provided.'), type="danger")
return False
else:
formvals = dict(zip(form['sids'], zip(form['scores'], form['cas'])))
for ticket in self.editable_tickets:
ticket_error = False
score = ticket.score
ca = ticket.ca
sid = ticket.student.student_id
if formvals[sid][0] == '':
score = None
if formvals[sid][1] == '':
ca = None
try:
if formvals[sid][0]:
score = int(formvals[sid][0])
if formvals[sid][1]:
ca = int(formvals[sid][1])
except ValueError:
error += '%s, ' % ticket.student.display_fullname
ticket_error = True
if not ticket_error and ticket.score != score:
try:
ticket.score = score
except TooBig:
error += '%s, ' % ticket.student.display_fullname
ticket_error = True
pass
ticket.student.__parent__.logger.info(
'%s - %s %s/%s score updated (%s)' %
(ob_class, ticket.student.student_id,
ticket.level, ticket.code, score))
if not ticket_error and ticket.ca != ca:
try:
ticket.ca = ca
except TooBig:
error += '%s, ' % ticket.student.display_fullname
pass
ticket.student.__parent__.logger.info(
'%s - %s %s/%s ca updated (%s)' %
(ob_class, ticket.student.student_id,
ticket.level, ticket.code, ca))
if error:
self.flash(_('Error: Score(s) and CA(s) of %s have not be updated. '
% error.strip(', ')), type="danger")
return True
class EditPreviousSessionScoresPage(CustomEditScoresPage):
grok.name('edit_prev_scores')
def update(self, *args, **kw):
form = self.request.form
self.current_academic_session = grok.getSite()[
'configuration'].current_academic_session
if self.context.__parent__.__parent__.score_editing_disabled:
self.flash(_('Score editing disabled.'), type="warning")
self.redirect(self.url(self.context))
return
if not self.current_academic_session:
self.flash(_('Current academic session not set.'), type="warning")
self.redirect(self.url(self.context))
return
previous_session = self.current_academic_session - 1
self.session_title = academic_sessions_vocab.getTerm(
previous_session).title
self.tickets = self._searchCatalog(previous_session)
if not self.tickets:
self.flash(_('No student found.'), type="warning")
self.redirect(self.url(self.context))
return
self.editable_tickets = [
ticket for ticket in self.tickets if ticket.editable_by_lecturer]
if not 'UPDATE_TABLE' in form and not 'UPDATE_FILE' in form:
return
if not self.editable_tickets:
return
success = self._update_scores(form)
if success:
self.flash(_('You successfully updated course results.'))
return
class CustomExportPDFScoresSlip(ExportPDFScoresSlip):
"""Deliver a PDF slip of course tickets for a lecturer.
"""
def data(self, session):
cat = queryUtility(ICatalog, name='coursetickets_catalog')
coursetickets = cat.searchResults(
session=(session, session),
code=(self.context.code, self.context.code)
)
# In AAUE only editable tickets can be printed
editable_tickets = [
ticket for ticket in coursetickets if ticket.editable_by_lecturer]
header = [[_(''),
_('Matric No.'),
_('Reg. No.'),
_('Fullname'),
_('Status'),
_('Course of\nStudies'),
_('Level'),
_('Exam\nScore'),
_(' CA '),
_('Total '),
_('Grade'),
],]
sorted_tickets = sorted(editable_tickets,
key=lambda ticket: ticket.student.certcode +
ticket.student.display_fullname + str(ticket.level))
no = 1
tickets = []
passed = 0
failed = 0
# In AAUE only editable tickets can be printed
for ticket in sorted_tickets:
if None in (ticket.score, ticket.ca):
total = 'n/a'
grade = 'n/a'
else:
total = ticket.score + ticket.ca
grade = getGradeWeightFromScore(total, ticket.student)[0]
if grade == 'F':
failed += 1
else:
passed += 1
fullname = textwrap.fill(ticket.student.display_fullname, 30)
row = [no,
ticket.student.matric_number,
ticket.student.reg_number,
fullname,
ticket.student.translated_state,
ticket.student.certcode,
ticket.level,
ticket.ca,
ticket.score,
total,
grade,
]
tickets.append(row)
no += 1
total = passed + failed
passed_perc = 0
failed_perc = 0
if total:
passed_perc = 100 * passed / total
failed_perc = 100 * failed / total
return header + tickets, [
total, passed, passed_perc, failed, failed_perc]
class DownloadPreviousSessionScoresView(DownloadScoresView):
"""View that exports scores.
"""
grok.name('download_prev_scores')
def update(self):
self.current_academic_session = grok.getSite()[
'configuration'].current_academic_session
if self.context.__parent__.__parent__.score_editing_disabled:
self.flash(_('Score editing disabled.'), type="warning")
self.redirect(self.url(self.context))
return
if not self.current_academic_session:
self.flash(_('Current academic session not set.'), type="warning")
self.redirect(self.url(self.context))
return
site = grok.getSite()
exporter = getUtility(ICSVExporter, name='lecturer')
self.csv = exporter.export_filtered(site, filepath=None,
catalog='coursetickets',
session=self.current_academic_session-1,
level=None,
code=self.context.code)
return
class AlumniRequestPasswordPage(StudentRequestPasswordPage):
"""Captcha'd request password page for students.
"""
grok.name('alumni_requestpw')
grok.require('waeup.Anonymous')
grok.template('alumni_requestpw')
form_fields = grok.AutoFields(IStudentRequestPW).select(
'lastname','number','email')
label = _('Search student record and send password for first-time login')
def _redirect_no_student(self):
self.flash(_('No student record found.'), type="warning")
self.redirect(self.application_url() + '/applicants/trans2017/register')
return