## $Id: browser.py 8246 2012-04-22 12:51:15Z henrik $ ## ## Copyright (C) 2011 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 ## """UI components for students and related components. """ import sys import grok from urllib import urlencode from time import time from datetime import datetime from copy import deepcopy from zope.event import notify from zope.i18n import translate from zope.catalog.interfaces import ICatalog from zope.component import queryUtility, getUtility, createObject from zope.schema.interfaces import ConstraintNotSatisfied from zope.formlib.textwidgets import BytesDisplayWidget from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState from waeup.kofa.accesscodes import ( invalidate_accesscode, get_access_code, create_accesscode) from waeup.kofa.accesscodes.workflow import USED from waeup.kofa.browser import ( KofaPage, KofaEditFormPage, KofaAddFormPage, KofaDisplayFormPage, ContactAdminForm, KofaForm, NullValidator) from waeup.kofa.browser.interfaces import ICaptchaManager from waeup.kofa.browser.breadcrumbs import Breadcrumb from waeup.kofa.browser.resources import datepicker, datatable, tabs, warning from waeup.kofa.browser.layout import jsaction, action, UtilityView from waeup.kofa.interfaces import ( IKofaObject, IUserAccount, IExtFileStore, IPasswordValidator, IContactForm, IKofaUtils, IUniversity) from waeup.kofa.interfaces import MessageFactory as _ from waeup.kofa.widgets.datewidget import ( FriendlyDateWidget, FriendlyDateDisplayWidget, FriendlyDatetimeDisplayWidget) from waeup.kofa.widgets.restwidget import ReSTDisplayWidget from waeup.kofa.students.interfaces import ( IStudentsContainer, IStudent, IUGStudentClearance,IPGStudentClearance, IStudentPersonal, IStudentBase, IStudentStudyCourse, IStudentAccommodation, IStudentStudyLevel, ICourseTicket, ICourseTicketAdd, IStudentPaymentsContainer, IStudentOnlinePayment, IBedTicket, IStudentsUtils, IStudentChangePassword ) from waeup.kofa.students.catalog import search from waeup.kofa.students.workflow import (ADMITTED, PAID, CLEARANCE, REQUESTED, RETURNING, CLEARED, REGISTERED, VALIDATED) from waeup.kofa.students.studylevel import StudentStudyLevel, CourseTicket from waeup.kofa.students.vocabularies import StudyLevelSource from waeup.kofa.browser.resources import toggleall from waeup.kofa.hostels.hostel import NOT_OCCUPIED from waeup.kofa.utils.helpers import get_current_principal def write_log_message(view, message): ob_class = view.__implemented__.__name__.replace('waeup.kofa.','') view.context.getStudent().loggerInfo(ob_class, message) return # Save function used for save methods in pages def msave(view, **data): changed_fields = view.applyData(view.context, **data) # Turn list of lists into single list if changed_fields: changed_fields = reduce(lambda x,y: x+y, changed_fields.values()) # Inform catalog if certificate has changed # (applyData does this only for the context) if 'certificate' in changed_fields: notify(grok.ObjectModifiedEvent(view.context.getStudent())) fields_string = ' + '.join(changed_fields) view.flash(_('Form has been saved.')) if fields_string: write_log_message(view, 'saved: %s' % fields_string) return def emit_lock_message(view): """Flash a lock message. """ view.flash(_('The requested form is locked (read-only).')) view.redirect(view.url(view.context)) return class StudentsBreadcrumb(Breadcrumb): """A breadcrumb for the students container. """ grok.context(IStudentsContainer) title = _('Students') @property def target(self): user = get_current_principal() if getattr(user, 'user_type', None) == 'student': return None return self.viewname class StudentBreadcrumb(Breadcrumb): """A breadcrumb for the student container. """ grok.context(IStudent) def title(self): return self.context.display_fullname class SudyCourseBreadcrumb(Breadcrumb): """A breadcrumb for the student study course. """ grok.context(IStudentStudyCourse) title = _('Study Course') class PaymentsBreadcrumb(Breadcrumb): """A breadcrumb for the student payments folder. """ grok.context(IStudentPaymentsContainer) title = _('Payments') class OnlinePaymentBreadcrumb(Breadcrumb): """A breadcrumb for payments. """ grok.context(IStudentOnlinePayment) @property def title(self): return self.context.p_id class AccommodationBreadcrumb(Breadcrumb): """A breadcrumb for the student accommodation folder. """ grok.context(IStudentAccommodation) title = _('Accommodation') class BedTicketBreadcrumb(Breadcrumb): """A breadcrumb for bed tickets. """ grok.context(IBedTicket) @property def title(self): return _('Bed Ticket ${a}', mapping = {'a':self.context.getSessionString()}) class StudyLevelBreadcrumb(Breadcrumb): """A breadcrumb for course lists. """ grok.context(IStudentStudyLevel) @property def title(self): return self.context.level_title class StudentsContainerPage(KofaPage): """The standard view for student containers. """ grok.context(IStudentsContainer) grok.name('index') grok.require('waeup.viewStudentsContainer') grok.template('containerpage') label = _('Student Section') search_button = _('Search') pnav = 4 def update(self, *args, **kw): datatable.need() form = self.request.form self.hitlist = [] if 'searchterm' in form and form['searchterm']: self.searchterm = form['searchterm'] self.searchtype = form['searchtype'] elif 'old_searchterm' in form: self.searchterm = form['old_searchterm'] self.searchtype = form['old_searchtype'] else: if 'search' in form: self.flash(_('Empty search string')) return if self.searchtype == 'current_session': try: self.searchterm = int(self.searchterm) except ValueError: self.flash('Only year dates allowed (e.g. 2011).') return self.hitlist = search(query=self.searchterm, searchtype=self.searchtype, view=self) if not self.hitlist: self.flash('No student found.') return class StudentsContainerManagePage(KofaPage): """The manage page for student containers. """ grok.context(IStudentsContainer) grok.name('manage') grok.require('waeup.manageStudent') grok.template('containermanagepage') pnav = 4 label = _('Manage student section') search_button = _('Search') remove_button = _('Remove selected') def update(self, *args, **kw): datatable.need() toggleall.need() warning.need() form = self.request.form self.hitlist = [] if 'searchterm' in form and form['searchterm']: self.searchterm = form['searchterm'] self.searchtype = form['searchtype'] elif 'old_searchterm' in form: self.searchterm = form['old_searchterm'] self.searchtype = form['old_searchtype'] else: if 'search' in form: self.flash(_('Empty search string')) return if self.searchtype == 'current_session': try: self.searchterm = int(self.searchterm) except ValueError: self.flash('Only year dates allowed (e.g. 2011).') return if not 'entries' in form: self.hitlist = search(query=self.searchterm, searchtype=self.searchtype, view=self) if not self.hitlist: self.flash(_('No student found.')) if 'remove' in form: self.flash(_('No item selected.')) return entries = form['entries'] if isinstance(entries, basestring): entries = [entries] deleted = [] for entry in entries: if 'remove' in form: del self.context[entry] deleted.append(entry) self.hitlist = search(query=self.searchterm, searchtype=self.searchtype, view=self) if len(deleted): self.flash(_('Successfully removed: ${a}', mapping = {'a':', '.join(deleted)})) return class StudentAddFormPage(KofaAddFormPage): """Add-form to add a student. """ grok.context(IStudentsContainer) grok.require('waeup.manageStudent') grok.name('addstudent') form_fields = grok.AutoFields(IStudent).select( 'firstname', 'middlename', 'lastname', 'reg_number') label = _('Add student') pnav = 4 @action(_('Create student record'), style='primary') def addStudent(self, **data): student = createObject(u'waeup.Student') self.applyData(student, **data) self.context.addStudent(student) self.flash(_('Student record created.')) self.redirect(self.url(self.context[student.student_id], 'index')) return class StudentBaseDisplayFormPage(KofaDisplayFormPage): """ Page to display student base data """ grok.context(IStudent) grok.name('index') grok.require('waeup.viewStudent') grok.template('basepage') form_fields = grok.AutoFields(IStudentBase).omit('password') pnav = 4 @property def label(self): return _('${a}: Base Data', mapping = {'a':self.context.display_fullname}) @property def hasPassword(self): if self.context.password: return _('set') return _('unset') class ContactStudentForm(ContactAdminForm): grok.context(IStudent) grok.name('contactstudent') grok.require('waeup.viewStudent') pnav = 4 form_fields = grok.AutoFields(IContactForm).select('subject', 'body') def update(self, subject=u''): self.form_fields.get('subject').field.default = subject self.subject = subject return def label(self): return _(u'Send message to ${a}', mapping = {'a':self.context.display_fullname}) @action('Send message now', style='primary') def send(self, *args, **data): try: email = self.request.principal.email except AttributeError: email = self.config.email_admin usertype = getattr(self.request.principal, 'user_type', 'system').title() kofa_utils = getUtility(IKofaUtils) success = kofa_utils.sendContactForm( self.request.principal.title,email, self.context.display_fullname,self.context.email, self.request.principal.id,usertype, self.config.name, data['body'],data['subject']) if success: self.flash(_('Your message has been sent.')) else: self.flash(_('An smtp server error occurred.')) return class StudentBaseManageFormPage(KofaEditFormPage): """ View to manage student base data """ grok.context(IStudent) grok.name('manage_base') grok.require('waeup.manageStudent') form_fields = grok.AutoFields(IStudentBase).omit('student_id') grok.template('basemanagepage') label = _('Manage base data') pnav = 4 def update(self): datepicker.need() # Enable jQuery datepicker in date fields. tabs.need() self.tab1 = self.tab2 = '' qs = self.request.get('QUERY_STRING', '') if not qs: qs = 'tab1' setattr(self, qs, 'active') super(StudentBaseManageFormPage, self).update() self.wf_info = IWorkflowInfo(self.context) return def getTransitions(self): """Return a list of dicts of allowed transition ids and titles. Each list entry provides keys ``name`` and ``title`` for internal name and (human readable) title of a single transition. """ allowed_transitions = self.wf_info.getManualTransitions() return [dict(name='', title=_('No transition'))] +[ dict(name=x, title=y) for x, y in allowed_transitions] @action(_('Save'), style='primary') def save(self, **data): form = self.request.form password = form.get('password', None) password_ctl = form.get('control_password', None) if password: validator = getUtility(IPasswordValidator) errors = validator.validate_password(password, password_ctl) if errors: self.flash( ' '.join(errors)) return changed_fields = self.applyData(self.context, **data) # Turn list of lists into single list if changed_fields: changed_fields = reduce(lambda x,y: x+y, changed_fields.values()) else: changed_fields = [] if password: # Now we know that the form has no errors and can set password ... IUserAccount(self.context).setPassword(password) changed_fields.append('password') # ... and execute transition if form.has_key('transition') and form['transition']: transition_id = form['transition'] self.wf_info.fireTransition(transition_id) fields_string = ' + '.join(changed_fields) self.flash(_('Form has been saved.')) if fields_string: write_log_message(self, 'saved: % s' % fields_string) return class StudentClearanceDisplayFormPage(KofaDisplayFormPage): """ Page to display student clearance data """ grok.context(IStudent) grok.name('view_clearance') grok.require('waeup.viewStudent') pnav = 4 @property def separators(self): return getUtility(IStudentsUtils).SEPARATORS_DICT @property def form_fields(self): cm = getattr(self.context,'current_mode', None) if cm is not None and cm.startswith('pg'): form_fields = grok.AutoFields(IPGStudentClearance).omit('clearance_locked') else: form_fields = grok.AutoFields(IUGStudentClearance).omit('clearance_locked') return form_fields @property def label(self): return _('${a}: Clearance Data', mapping = {'a':self.context.display_fullname}) class ExportPDFClearanceSlipPage(grok.View): """Deliver a PDF slip of the context. """ grok.context(IStudent) grok.name('clearance.pdf') grok.require('waeup.viewStudent') prefix = 'form' @property def form_fields(self): cm = getattr(self.context,'current_mode', None) if cm is not None and cm.startswith('pg'): form_fields = grok.AutoFields(IPGStudentClearance).omit('clearance_locked') else: form_fields = grok.AutoFields(IUGStudentClearance).omit('clearance_locked') return form_fields @property def title(self): portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE return translate(_('Clearance Data'), 'waeup.kofa', target_language=portal_language) @property def label(self): portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE return translate(_('Clearance Slip of '), 'waeup.kofa', target_language=portal_language) \ + ' %s' % self.context.display_fullname def render(self): studentview = StudentBaseDisplayFormPage(self.context.getStudent(), self.request) students_utils = getUtility(IStudentsUtils) return students_utils.renderPDF( self, 'clearance.pdf', self.context.getStudent(), studentview) class StudentClearanceManageFormPage(KofaEditFormPage): """ Page to manage student clearance data """ grok.context(IStudent) grok.name('manage_clearance') grok.require('waeup.manageStudent') grok.template('clearanceeditpage') label = _('Manage clearance data') pnav = 4 @property def separators(self): return getUtility(IStudentsUtils).SEPARATORS_DICT @property def form_fields(self): cm = getattr(self.context,'current_mode', None) if cm is not None and cm.startswith('pg'): form_fields = grok.AutoFields(IPGStudentClearance) else: form_fields = grok.AutoFields(IUGStudentClearance) return form_fields def update(self): datepicker.need() # Enable jQuery datepicker in date fields. tabs.need() self.tab1 = self.tab2 = '' qs = self.request.get('QUERY_STRING', '') if not qs: qs = 'tab1' setattr(self, qs, 'active') return super(StudentClearanceManageFormPage, self).update() @action(_('Save'), style='primary') def save(self, **data): msave(self, **data) return class StudentClearPage(UtilityView, grok.View): """ Clear student by clearance officer """ grok.context(IStudent) grok.name('clear') grok.require('waeup.clearStudent') def update(self): if self.context.state == REQUESTED: IWorkflowInfo(self.context).fireTransition('clear') self.flash(_('Student has been cleared.')) else: self.flash(_('Student is in wrong state.')) self.redirect(self.url(self.context,'view_clearance')) return def render(self): return class StudentRejectClearancePage(UtilityView, grok.View): """ Reject clearance by clearance officers """ grok.context(IStudent) grok.name('reject_clearance') grok.require('waeup.clearStudent') def update(self): if self.context.state == CLEARED: IWorkflowInfo(self.context).fireTransition('reset4') message = _('Clearance has been annulled.') self.flash(message) elif self.context.state == REQUESTED: IWorkflowInfo(self.context).fireTransition('reset3') message = _('Clearance request has been rejected.') self.flash(message) else: self.flash(_('Student is in wrong state.')) self.redirect(self.url(self.context,'view_clearance')) return args = {'subject':message} self.redirect(self.url(self.context) + '/contactstudent?%s' % urlencode(args)) return def render(self): return class StudentPersonalDisplayFormPage(KofaDisplayFormPage): """ Page to display student personal data """ grok.context(IStudent) grok.name('view_personal') grok.require('waeup.viewStudent') form_fields = grok.AutoFields(IStudentPersonal) form_fields['perm_address'].custom_widget = BytesDisplayWidget pnav = 4 @property def label(self): return _('${a}: Personal Data', mapping = {'a':self.context.display_fullname}) class StudentPersonalEditFormPage(KofaEditFormPage): """ Page to edit personal data """ grok.context(IStudent) grok.name('edit_personal') grok.require('waeup.handleStudent') form_fields = grok.AutoFields(IStudentPersonal) label = _('Edit personal data') pnav = 4 @action(_('Save'), style='primary') def save(self, **data): msave(self, **data) return class StudyCourseDisplayFormPage(KofaDisplayFormPage): """ Page to display the student study course data """ grok.context(IStudentStudyCourse) grok.name('index') grok.require('waeup.viewStudent') form_fields = grok.AutoFields(IStudentStudyCourse) grok.template('studycoursepage') pnav = 4 @property def label(self): return _('${a}: Study Course', mapping = {'a':self.context.__parent__.display_fullname}) @property def current_mode(self): if self.context.certificate is not None: studymodes_dict = getUtility(IKofaUtils).STUDY_MODES_DICT return studymodes_dict[self.context.certificate.study_mode] return @property def department(self): if self.context.certificate is not None: return self.context.certificate.__parent__.__parent__ return @property def faculty(self): if self.context.certificate is not None: return self.context.certificate.__parent__.__parent__.__parent__ return class StudyCourseManageFormPage(KofaEditFormPage): """ Page to edit the student study course data """ grok.context(IStudentStudyCourse) grok.name('manage') grok.require('waeup.manageStudent') grok.template('studycoursemanagepage') form_fields = grok.AutoFields(IStudentStudyCourse) label = _('Manage study course') pnav = 4 taboneactions = [_('Save'),_('Cancel')] tabtwoactions = [_('Remove selected levels'),_('Cancel')] tabthreeactions = [_('Add study level')] def update(self): super(StudyCourseManageFormPage, self).update() tabs.need() self.tab1 = self.tab2 = '' qs = self.request.get('QUERY_STRING', '') if not qs: qs = 'tab1' setattr(self, qs, 'active') warning.need() datatable.need() return @action(_('Save'), style='primary') def save(self, **data): try: msave(self, **data) except ConstraintNotSatisfied: # The selected level might not exist in certificate self.flash(_('Current level not available for certificate.')) return notify(grok.ObjectModifiedEvent(self.context.__parent__)) return @property def level_dict(self): studylevelsource = StudyLevelSource().factory for code in studylevelsource.getValues(self.context): title = studylevelsource.getTitle(self.context, code) yield(dict(code=code, title=title)) @action(_('Add study level')) def addStudyLevel(self, **data): level_code = self.request.form.get('addlevel', None) studylevel = StudentStudyLevel() studylevel.level = int(level_code) try: self.context.addStudentStudyLevel( self.context.certificate,studylevel) self.flash(_('Study level has been added.')) except KeyError: self.flash(_('This level exists.')) self.redirect(self.url(self.context, u'@@manage')+'?tab2') return @jsaction(_('Remove selected levels')) def delStudyLevels(self, **data): form = self.request.form if form.has_key('val_id'): child_id = form['val_id'] else: self.flash(_('No study level selected.')) self.redirect(self.url(self.context, '@@manage')+'?tab2') return if not isinstance(child_id, list): child_id = [child_id] deleted = [] for id in child_id: del self.context[id] deleted.append(id) if len(deleted): self.flash(_('Successfully removed: ${a}', mapping = {'a':', '.join(deleted)})) self.redirect(self.url(self.context, u'@@manage')+'?tab2') return class StudyLevelDisplayFormPage(KofaDisplayFormPage): """ Page to display student study levels """ grok.context(IStudentStudyLevel) grok.name('index') grok.require('waeup.viewStudent') form_fields = grok.AutoFields(IStudentStudyLevel) grok.template('studylevelpage') pnav = 4 def update(self): super(StudyLevelDisplayFormPage, self).update() datatable.need() return @property def translated_values(self): lang = self.request.cookies.get('kofa.language') for value in self.context.values(): # We have to make a deepcopy otherwise we'll override # the object's dict values (not the attributes). value_dict = deepcopy(value.__dict__) value_dict['mandatory'] = translate(str(value.mandatory), 'zope', target_language=lang) value_dict['carry_over'] = translate(str(value.carry_over), 'zope', target_language=lang) value_dict['automatic'] = translate(str(value.automatic), 'zope', target_language=lang) yield value_dict @property def label(self): # Here we know that the cookie has been set lang = self.request.cookies.get('kofa.language') level_title = translate(self.context.level_title, 'waeup.kofa', target_language=lang) return _('${a}: Study Level ${b}', mapping = { 'a':self.context.getStudent().display_fullname, 'b':level_title}) @property def total_credits(self): total_credits = 0 for key, val in self.context.items(): total_credits += val.credits return total_credits class ExportPDFCourseRegistrationSlipPage(UtilityView, grok.View): """Deliver a PDF slip of the context. """ grok.context(IStudentStudyLevel) grok.name('course_registration.pdf') grok.require('waeup.viewStudent') form_fields = grok.AutoFields(IStudentStudyLevel) prefix = 'form' @property def title(self): portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE return translate(_('Level Data'), 'waeup.kofa', target_language=portal_language) @property def content_title(self): portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE return translate(_('Course List'), 'waeup.kofa', target_language=portal_language) @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) return translate(_('Course Registration Slip'), 'waeup.kofa', target_language=portal_language) \ + ' %s' % level_title def render(self): portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE Sem = translate(_('Sem.'), 'waeup.kofa', target_language=portal_language) Code = translate(_('Code'), 'waeup.kofa', target_language=portal_language) Title = translate(_('Title'), 'waeup.kofa', target_language=portal_language) Dept = translate(_('Dept.'), 'waeup.kofa', target_language=portal_language) Faculty = translate(_('Faculty'), 'waeup.kofa', target_language=portal_language) Cred = translate(_('Cred.'), 'waeup.kofa', target_language=portal_language) Mand = translate(_('Mand.'), 'waeup.kofa', target_language=portal_language) Score = translate(_('Score'), 'waeup.kofa', target_language=portal_language) studentview = StudentBaseDisplayFormPage(self.context.getStudent(), self.request) students_utils = getUtility(IStudentsUtils) tabledata = sorted(self.context.values(), key=lambda value: str(value.semester) + value.code) return students_utils.renderPDF( self, 'course_registration.pdf', self.context.getStudent(), studentview, tableheader=[(Sem,'semester', 1.5),(Code,'code', 2.5), (Title,'title', 5), (Dept,'dcode', 1.5), (Faculty,'fcode', 1.5), (Cred, 'credits', 1.5), (Mand, 'mandatory', 1.5), (Score, 'score', 1.5), #('Auto', 'automatic', 1.5) ], tabledata=tabledata) class StudyLevelManageFormPage(KofaEditFormPage): """ Page to edit the student study level data """ grok.context(IStudentStudyLevel) grok.name('manage') grok.require('waeup.manageStudent') grok.template('studylevelmanagepage') form_fields = grok.AutoFields(IStudentStudyLevel) pnav = 4 taboneactions = [_('Save'),_('Cancel')] tabtwoactions = [_('Add course ticket'), _('Remove selected tickets'),_('Cancel')] def update(self): super(StudyLevelManageFormPage, self).update() tabs.need() self.tab1 = self.tab2 = '' qs = self.request.get('QUERY_STRING', '') if not qs: qs = 'tab1' setattr(self, qs, 'active') warning.need() datatable.need() return @property def label(self): # Here we know that the cookie has been set lang = self.request.cookies.get('kofa.language') level_title = translate(self.context.level_title, 'waeup.kofa', target_language=lang) return _('Manage study level ${a}', mapping = {'a':level_title}) @action(_('Save'), style='primary') def save(self, **data): msave(self, **data) return @action(_('Add course ticket')) def addCourseTicket(self, **data): self.redirect(self.url(self.context, '@@add')) @jsaction(_('Remove selected tickets')) def delCourseTicket(self, **data): form = self.request.form if form.has_key('val_id'): child_id = form['val_id'] else: self.flash(_('No ticket selected.')) self.redirect(self.url(self.context, '@@manage')+'?tab2') return if not isinstance(child_id, list): child_id = [child_id] deleted = [] for id in child_id: del self.context[id] deleted.append(id) if len(deleted): self.flash(_('Successfully removed: ${a}', mapping = {'a':', '.join(deleted)})) self.redirect(self.url(self.context, u'@@manage')+'?tab2') return class ValidateCoursesPage(UtilityView, grok.View): """ Validate course list by course adviser """ grok.context(IStudentStudyLevel) grok.name('validate_courses') grok.require('waeup.validateStudent') def update(self): if str(self.context.__parent__.current_level) != self.context.__name__: self.flash(_('This level does not correspond current level.')) elif self.context.getStudent().state == REGISTERED: IWorkflowInfo(self.context.getStudent()).fireTransition( 'validate_courses') self.flash(_('Course list has been validated.')) else: self.flash(_('Student is in the wrong state.')) self.redirect(self.url(self.context)) return def render(self): return class RejectCoursesPage(UtilityView, grok.View): """ Reject course list by course adviser """ grok.context(IStudentStudyLevel) grok.name('reject_courses') grok.require('waeup.validateStudent') def update(self): if str(self.context.__parent__.current_level) != self.context.__name__: self.flash(_('This level does not correspond current level.')) self.redirect(self.url(self.context)) return elif self.context.getStudent().state == VALIDATED: IWorkflowInfo(self.context.getStudent()).fireTransition('reset8') message = _('Course list request has been annulled.') self.flash(message) elif self.context.getStudent().state == REGISTERED: IWorkflowInfo(self.context.getStudent()).fireTransition('reset7') message = _('Course list request has been rejected:') self.flash(message) else: self.flash(_('Student is in the wrong state.')) self.redirect(self.url(self.context)) return args = {'subject':message} self.redirect(self.url(self.context.getStudent()) + '/contactstudent?%s' % urlencode(args)) return def render(self): return class CourseTicketAddFormPage(KofaAddFormPage): """Add a course ticket. """ grok.context(IStudentStudyLevel) grok.name('add') grok.require('waeup.manageStudent') label = _('Add course ticket') form_fields = grok.AutoFields(ICourseTicketAdd).omit( 'score', 'automatic', 'carry_over') pnav = 4 @action(_('Add course ticket')) def addCourseTicket(self, **data): ticket = CourseTicket() course = data['course'] ticket.automatic = False ticket.carry_over = False ticket.code = course.code ticket.title = course.title ticket.fcode = course.__parent__.__parent__.__parent__.code ticket.dcode = course.__parent__.__parent__.code ticket.credits = course.credits ticket.passmark = course.passmark ticket.semester = course.semester try: self.context.addCourseTicket(ticket) except KeyError: self.flash(_('The ticket exists.')) return self.flash(_('Successfully added ${a}.', mapping = {'a':ticket.code})) self.redirect(self.url(self.context, u'@@manage')+'?tab2') return @action(_('Cancel'), validator=NullValidator) def cancel(self, **data): self.redirect(self.url(self.context)) class CourseTicketDisplayFormPage(KofaDisplayFormPage): """ Page to display course tickets """ grok.context(ICourseTicket) grok.name('index') grok.require('waeup.viewStudent') form_fields = grok.AutoFields(ICourseTicket) grok.template('courseticketpage') pnav = 4 @property def label(self): return _('${a}: Course Ticket ${b}', mapping = { 'a':self.context.getStudent().display_fullname, 'b':self.context.code}) class CourseTicketManageFormPage(KofaEditFormPage): """ Page to manage course tickets """ grok.context(ICourseTicket) grok.name('manage') grok.require('waeup.manageStudent') form_fields = grok.AutoFields(ICourseTicket) grok.template('courseticketmanagepage') pnav = 4 @property def label(self): return _('Manage course ticket ${a}', mapping = {'a':self.context.code}) @action('Save', style='primary') def save(self, **data): msave(self, **data) return class PaymentsManageFormPage(KofaEditFormPage): """ Page to manage the student payments This manage form page is for both students and students officers. """ grok.context(IStudentPaymentsContainer) grok.name('index') grok.require('waeup.payStudent') form_fields = grok.AutoFields(IStudentPaymentsContainer) grok.template('paymentsmanagepage') pnav = 4 def unremovable(self, ticket): usertype = getattr(self.request.principal, 'user_type', None) if not usertype: return False return (self.request.principal.user_type == 'student' and ticket.r_code) @property def label(self): return _('${a}: Payments', mapping = {'a':self.context.__parent__.display_fullname}) def update(self): super(PaymentsManageFormPage, self).update() datatable.need() warning.need() return @jsaction(_('Remove selected tickets')) def delPaymentTicket(self, **data): form = self.request.form if form.has_key('val_id'): child_id = form['val_id'] else: self.flash(_('No payment selected.')) self.redirect(self.url(self.context)) return if not isinstance(child_id, list): child_id = [child_id] deleted = [] for id in child_id: # Students are not allowed to remove used payment tickets if not self.unremovable(self.context[id]): del self.context[id] deleted.append(id) if len(deleted): self.flash(_('Successfully removed: ${a}', mapping = {'a': ', '.join(deleted)})) write_log_message(self,'removed: % s' % ', '.join(deleted)) self.redirect(self.url(self.context)) return @action(_('Add online payment ticket')) def addPaymentTicket(self, **data): self.redirect(self.url(self.context, '@@addop')) class OnlinePaymentAddFormPage(KofaAddFormPage): """ Page to add an online payment ticket """ grok.context(IStudentPaymentsContainer) grok.name('addop') grok.require('waeup.payStudent') form_fields = grok.AutoFields(IStudentOnlinePayment).select( 'p_category') label = _('Add online payment') pnav = 4 factory = u'waeup.StudentOnlinePayment' def _fillCustomFields(self, payment, pay_details): """No custom fields in the base package """ return payment @action(_('Create ticket'), style='primary') def createTicket(self, **data): p_category = data['p_category'] student = self.context.__parent__ if p_category == 'bed_allocation' and student[ 'studycourse'].current_session != grok.getSite()[ 'configuration'].accommodation_session: self.flash( _('Your current session does not match ' + \ 'accommodation session.')) self.redirect(self.url(self.context)) return students_utils = getUtility(IStudentsUtils) try: pay_details = students_utils.getPaymentDetails( p_category,student) except AttributeError: self.flash( _('Study course data are incomplete.')) self.redirect(self.url(self.context)) return if pay_details['error']: self.flash(pay_details['error']) self.redirect(self.url(self.context)) return p_item = pay_details['p_item'] p_session = pay_details['p_session'] for key in self.context.keys(): ticket = self.context[key] if ticket.p_state == 'paid' and\ ticket.p_category == p_category and \ ticket.p_item == p_item and \ ticket.p_session == p_session: self.flash( _('This type of payment has already been made.')) self.redirect(self.url(self.context)) return payment = createObject(self.factory) self.applyData(payment, **data) timestamp = "%d" % int(time()*1000) payment.p_id = "p%s" % timestamp payment.p_item = p_item payment.p_session = p_session payment.amount_auth = pay_details['amount'] payment = self._fillCustomFields(payment, pay_details) self.context[payment.p_id] = payment self.flash(_('Payment ticket created.')) self.redirect(self.url(self.context)) return class OnlinePaymentDisplayFormPage(KofaDisplayFormPage): """ Page to view an online payment ticket """ grok.context(IStudentOnlinePayment) grok.name('index') grok.require('waeup.viewStudent') form_fields = grok.AutoFields(IStudentOnlinePayment) form_fields[ 'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le') form_fields[ 'payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le') pnav = 4 @property def label(self): return _('${a}: Online Payment Ticket ${b}', mapping = { 'a':self.context.getStudent().display_fullname, 'b':self.context.p_id}) class OnlinePaymentCallbackPage(UtilityView, grok.View): """ Callback view """ grok.context(IStudentOnlinePayment) grok.name('simulate_callback') grok.require('waeup.payStudent') # This update method simulates a valid callback und must be # neutralized in the customization package. def update(self): if self.context.p_state == 'paid': self.flash(_('This ticket has already been paid.')) return student = self.context.getStudent() write_log_message(self,'valid callback: %s' % self.context.p_id) self.context.r_amount_approved = self.context.amount_auth self.context.r_code = u'00' self.context.p_state = 'paid' self.context.payment_date = datetime.utcnow() if self.context.p_category == 'clearance': # Create CLR access code pin, error = create_accesscode('CLR',0,student.student_id) if error: self.flash(_('Valid callback received. ${a}', mapping = {'a':error})) return self.context.ac = pin elif self.context.p_category == 'schoolfee': # Create SFE access code pin, error = create_accesscode('SFE',0,student.student_id) if error: self.flash(_('Valid callback received. ${a}', mapping = {'a':error})) return self.context.ac = pin elif self.context.p_category == 'bed_allocation': # Create HOS access code pin, error = create_accesscode('HOS',0,student.student_id) if error: self.flash(_('Valid callback received. ${a}', mapping = {'a':error})) return self.context.ac = pin self.flash(_('Valid callback received.')) return def render(self): self.redirect(self.url(self.context, '@@index')) return class ExportPDFPaymentSlipPage(UtilityView, grok.View): """Deliver a PDF slip of the context. """ grok.context(IStudentOnlinePayment) grok.name('payment_receipt.pdf') grok.require('waeup.viewStudent') form_fields = grok.AutoFields(IStudentOnlinePayment) form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le') form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le') prefix = 'form' title = 'Payment Data' @property def label(self): return 'Online Payment Receipt %s' % self.context.p_id def render(self): if self.context.p_state != 'paid': self.flash('Ticket not yet paid.') self.redirect(self.url(self.context)) return studentview = StudentBaseDisplayFormPage(self.context.getStudent(), self.request) students_utils = getUtility(IStudentsUtils) return students_utils.renderPDF(self, 'payment_receipt.pdf', self.context.getStudent(), studentview) class AccommodationManageFormPage(KofaEditFormPage): """ Page to manage bed tickets. This manage form page is for both students and students officers. """ grok.context(IStudentAccommodation) grok.name('index') grok.require('waeup.handleAccommodation') form_fields = grok.AutoFields(IStudentAccommodation) grok.template('accommodationmanagepage') pnav = 4 officers_only_actions = [_('Remove selected')] @property def label(self): return _('${a}: Accommodation', mapping = {'a':self.context.__parent__.display_fullname}) def update(self): super(AccommodationManageFormPage, self).update() datatable.need() warning.need() return @jsaction(_('Remove selected')) def delBedTickets(self, **data): if getattr(self.request.principal, 'user_type', None) == 'student': self.flash(_('You are not allowed to remove bed tickets.')) self.redirect(self.url(self.context)) return form = self.request.form if form.has_key('val_id'): child_id = form['val_id'] else: self.flash(_('No bed ticket selected.')) self.redirect(self.url(self.context)) return if not isinstance(child_id, list): child_id = [child_id] deleted = [] for id in child_id: del self.context[id] deleted.append(id) if len(deleted): self.flash(_('Successfully removed: ${a}', mapping = {'a':', '.join(deleted)})) write_log_message(self,'removed: % s' % ', '.join(deleted)) self.redirect(self.url(self.context)) return @property def selected_actions(self): if getattr(self.request.principal, 'user_type', None) == 'student': return [action for action in self.actions if not action.label in self.officers_only_actions] return self.actions class BedTicketAddPage(KofaPage): """ Page to add an online payment ticket """ grok.context(IStudentAccommodation) grok.name('add') grok.require('waeup.handleAccommodation') grok.template('enterpin') ac_prefix = 'HOS' label = _('Add bed ticket') pnav = 4 buttonname = _('Create bed ticket') notice = '' def update(self, SUBMIT=None): student = self.context.getStudent() students_utils = getUtility(IStudentsUtils) acc_details = students_utils.getAccommodationDetails(student) if not acc_details: self.flash(_("Your data are incomplete.")) self.redirect(self.url(self.context)) return if not student.state in acc_details['allowed_states']: self.flash(_("You are in the wrong registration state.")) self.redirect(self.url(self.context)) return if student['studycourse'].current_session != acc_details[ 'booking_session']: self.flash( _('Your current session does not match accommodation session.')) self.redirect(self.url(self.context)) return if str(acc_details['booking_session']) in self.context.keys(): self.flash( _('You already booked a bed space in current ' \ + 'accommodation session.')) self.redirect(self.url(self.context)) return self.ac_series = self.request.form.get('ac_series', None) self.ac_number = self.request.form.get('ac_number', None) if SUBMIT is None: return pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number) code = get_access_code(pin) if not code: self.flash(_('Activation code is invalid.')) return # Search and book bed cat = queryUtility(ICatalog, name='beds_catalog', default=None) entries = cat.searchResults( owner=(student.student_id,student.student_id)) if len(entries): # If bed space has bee manually allocated use this bed bed = [entry for entry in entries][0] else: # else search for other available beds entries = cat.searchResults( bed_type=(acc_details['bt'],acc_details['bt'])) available_beds = [ entry for entry in entries if entry.owner == NOT_OCCUPIED] if available_beds: students_utils = getUtility(IStudentsUtils) bed = students_utils.selectBed(available_beds) bed.bookBed(student.student_id) else: self.flash(_('There is no free bed in your category ${a}.', mapping = {'a':acc_details['bt']})) return # Mark pin as used (this also fires a pin related transition) if code.state == USED: self.flash(_('Activation code has already been used.')) return else: comment = _(u'invalidated') # Here we know that the ac is in state initialized so we do not # expect an exception, but the owner might be different if not invalidate_accesscode( pin,comment,self.context.getStudent().student_id): self.flash(_('You are not the owner of this access code.')) return # Create bed ticket bedticket = createObject(u'waeup.BedTicket') bedticket.booking_code = pin bedticket.booking_session = acc_details['booking_session'] bedticket.bed_type = acc_details['bt'] bedticket.bed = bed hall_title = bed.__parent__.hostel_name coordinates = bed.getBedCoordinates()[1:] block, room_nr, bed_nr = coordinates bc = _('${a}, Block ${b}, Room ${c}, Bed ${d} (${e})', mapping = { 'a':hall_title, 'b':block, 'c':room_nr, 'd':bed_nr, 'e':bed.bed_type}) portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE bedticket.bed_coordinates = translate( bc, 'waeup.kofa',target_language=portal_language) key = str(acc_details['booking_session']) self.context[key] = bedticket self.flash(_('Bed ticket created and bed booked: ${a}', mapping = {'a':bedticket.bed_coordinates})) self.redirect(self.url(self.context)) return class BedTicketDisplayFormPage(KofaDisplayFormPage): """ Page to display bed tickets """ grok.context(IBedTicket) grok.name('index') grok.require('waeup.handleAccommodation') form_fields = grok.AutoFields(IBedTicket) pnav = 4 @property def label(self): return _('Bed Ticket for Session ${a}', mapping = {'a':self.context.getSessionString()}) class ExportPDFBedTicketSlipPage(UtilityView, grok.View): """Deliver a PDF slip of the context. """ grok.context(IBedTicket) grok.name('bed_allocation.pdf') grok.require('waeup.handleAccommodation') form_fields = grok.AutoFields(IBedTicket) form_fields['booking_date'].custom_widget = FriendlyDatetimeDisplayWidget('le') prefix = 'form' @property def title(self): portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE return translate(_('Bed Allocation Data'), 'waeup.kofa', target_language=portal_language) @property def label(self): portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE return translate(_('Bed Allocation: '), 'waeup.kofa', target_language=portal_language) \ + ' %s' % self.context.bed_coordinates def render(self): studentview = StudentBaseDisplayFormPage(self.context.getStudent(), self.request) students_utils = getUtility(IStudentsUtils) return students_utils.renderPDF( self, 'bed_allocation.pdf', self.context.getStudent(), studentview) class BedTicketRelocationPage(UtilityView, grok.View): """ Callback view """ grok.context(IBedTicket) grok.name('relocate') grok.require('waeup.manageHostels') # Relocate student if student parameters have changed or the bed_type # of the bed has changed def update(self): student = self.context.getStudent() students_utils = getUtility(IStudentsUtils) acc_details = students_utils.getAccommodationDetails(student) if self.context.bed != None and \ 'reserved' in self.context.bed.bed_type: self.flash(_("Students in reserved beds can't be relocated.")) self.redirect(self.url(self.context)) return if acc_details['bt'] == self.context.bed_type and \ self.context.bed != None and \ self.context.bed.bed_type == self.context.bed_type: self.flash(_("Student can't be relocated.")) self.redirect(self.url(self.context)) return # Search a bed cat = queryUtility(ICatalog, name='beds_catalog', default=None) entries = cat.searchResults( owner=(student.student_id,student.student_id)) if len(entries) and self.context.bed == None: # If booking has been cancelled but other bed space has been # manually allocated after cancellation use this bed new_bed = [entry for entry in entries][0] else: # Search for other available beds entries = cat.searchResults( bed_type=(acc_details['bt'],acc_details['bt'])) available_beds = [ entry for entry in entries if entry.owner == NOT_OCCUPIED] if available_beds: students_utils = getUtility(IStudentsUtils) new_bed = students_utils.selectBed(available_beds) new_bed.bookBed(student.student_id) else: self.flash(_('There is no free bed in your category ${a}.', mapping = {'a':acc_details['bt']})) self.redirect(self.url(self.context)) return # Release old bed if exists if self.context.bed != None: self.context.bed.owner = NOT_OCCUPIED notify(grok.ObjectModifiedEvent(self.context.bed)) # Alocate new bed self.context.bed_type = acc_details['bt'] self.context.bed = new_bed hall_title = new_bed.__parent__.hostel_name coordinates = new_bed.getBedCoordinates()[1:] block, room_nr, bed_nr = coordinates bc = _('${a}, Block ${b}, Room ${c}, Bed ${d} (${e})', mapping = { 'a':hall_title, 'b':block, 'c':room_nr, 'd':bed_nr, 'e':new_bed.bed_type}) portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE self.context.bed_coordinates = translate( bc, 'waeup.kofa',target_language=portal_language) self.flash(_('Student relocated: ${a}', mapping = {'a':self.context.bed_coordinates})) self.redirect(self.url(self.context)) return def render(self): return class StudentHistoryPage(KofaPage): """ Page to display student clearance data """ grok.context(IStudent) grok.name('history') grok.require('waeup.viewStudent') grok.template('studenthistory') pnav = 4 @property def label(self): return _('${a}: History', mapping = {'a':self.context.display_fullname}) # Pages for students only class StudentBaseEditFormPage(KofaEditFormPage): """ View to edit student base data """ grok.context(IStudent) grok.name('edit_base') grok.require('waeup.handleStudent') form_fields = grok.AutoFields(IStudentBase).select( 'email', 'phone') label = _('Edit base data') pnav = 4 @action(_('Save'), style='primary') def save(self, **data): msave(self, **data) return class StudentChangePasswordPage(KofaEditFormPage): """ View to manage student base data """ grok.context(IStudent) grok.name('change_password') grok.require('waeup.handleStudent') grok.template('change_password') label = _('Change password') pnav = 4 @action(_('Save'), style='primary') def save(self, **data): form = self.request.form password = form.get('change_password', None) password_ctl = form.get('change_password_repeat', None) if password: validator = getUtility(IPasswordValidator) errors = validator.validate_password(password, password_ctl) if not errors: IUserAccount(self.context).setPassword(password) write_log_message(self, 'saved: password') self.flash(_('Password changed.')) else: self.flash( ' '.join(errors)) return class StudentFilesUploadPage(KofaPage): """ View to upload files by student """ grok.context(IStudent) grok.name('change_portrait') grok.require('waeup.uploadStudentFile') grok.template('filesuploadpage') label = _('Upload portrait') pnav = 4 def update(self): if self.context.getStudent().state != ADMITTED: emit_lock_message(self) return super(StudentFilesUploadPage, self).update() return class StartClearancePage(KofaPage): grok.context(IStudent) grok.name('start_clearance') grok.require('waeup.handleStudent') grok.template('enterpin') label = _('Start clearance') ac_prefix = 'CLR' notice = '' pnav = 4 buttonname = _('Start clearance now') @property def all_required_fields_filled(self): if self.context.email and self.context.phone: return True return False @property def portrait_uploaded(self): store = getUtility(IExtFileStore) if store.getFileByContext(self.context, attr=u'passport.jpg'): return True return False def update(self, SUBMIT=None): if not self.context.state == ADMITTED: self.flash(_("Wrong state")) self.redirect(self.url(self.context)) return if not self.portrait_uploaded: self.flash(_("No portrait uploaded.")) self.redirect(self.url(self.context, 'change_portrait')) return if not self.all_required_fields_filled: self.flash(_("Not all required fields filled.")) self.redirect(self.url(self.context, 'edit_base')) return self.ac_series = self.request.form.get('ac_series', None) self.ac_number = self.request.form.get('ac_number', None) if SUBMIT is None: return pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number) code = get_access_code(pin) if not code: self.flash(_('Activation code is invalid.')) return # Mark pin as used (this also fires a pin related transition) # and fire transition start_clearance if code.state == USED: self.flash(_('Activation code has already been used.')) return else: comment = _(u"invalidated") # Here we know that the ac is in state initialized so we do not # expect an exception, but the owner might be different if not invalidate_accesscode(pin,comment,self.context.student_id): self.flash(_('You are not the owner of this access code.')) return self.context.clr_code = pin IWorkflowInfo(self.context).fireTransition('start_clearance') self.flash(_('Clearance process has been started.')) self.redirect(self.url(self.context,'cedit')) return class StudentClearanceEditFormPage(StudentClearanceManageFormPage): """ View to edit student clearance data by student """ grok.context(IStudent) grok.name('cedit') grok.require('waeup.handleStudent') label = _('Edit clearance data') @property def form_fields(self): cm = getattr(self.context,'current_mode', None) if cm is not None and cm.startswith('pg'): form_fields = grok.AutoFields(IPGStudentClearance).omit('clearance_locked') else: form_fields = grok.AutoFields(IUGStudentClearance).omit('clearance_locked') return form_fields def update(self): if self.context.clearance_locked: emit_lock_message(self) return return super(StudentClearanceEditFormPage, self).update() @action(_('Save'), style='primary') def save(self, **data): self.applyData(self.context, **data) self.flash(_('Clearance form has been saved.')) return def dataNotComplete(self): """To be implemented in the customization package. """ return False @action(_('Save and request clearance'), style='primary') def requestClearance(self, **data): self.applyData(self.context, **data) if self.dataNotComplete(): self.flash(self.dataNotComplete()) return self.flash(_('Clearance form has been saved.')) self.redirect(self.url(self.context,'request_clearance')) return class RequestClearancePage(KofaPage): grok.context(IStudent) grok.name('request_clearance') grok.require('waeup.handleStudent') grok.template('enterpin') label = _('Request clearance') notice = _('Enter the CLR access code used for starting clearance.') ac_prefix = 'CLR' pnav = 4 buttonname = _('Request clearance now') def update(self, SUBMIT=None): self.ac_series = self.request.form.get('ac_series', None) self.ac_number = self.request.form.get('ac_number', None) if SUBMIT is None: return pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number) if self.context.clr_code != pin: self.flash(_("This isn't your CLR access code.")) return state = IWorkflowState(self.context).getState() # This shouldn't happen, but the application officer # might have forgotten to lock the form after changing the state if state != CLEARANCE: self.flash(_('This form cannot be submitted. Wrong state!')) return IWorkflowInfo(self.context).fireTransition('request_clearance') self.flash(_('Clearance has been requested.')) self.redirect(self.url(self.context)) return class StartCourseRegistrationPage(KofaPage): grok.context(IStudentStudyCourse) grok.name('start_course_registration') grok.require('waeup.handleStudent') grok.template('enterpin') label = _('Start course registration') ac_prefix = 'SFE' notice = '' pnav = 4 buttonname = _('Start course registration now') def update(self, SUBMIT=None): if not self.context.getStudent().state in (CLEARED,RETURNING): self.flash(_("Wrong state")) self.redirect(self.url(self.context)) return self.ac_series = self.request.form.get('ac_series', None) self.ac_number = self.request.form.get('ac_number', None) if SUBMIT is None: return pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number) code = get_access_code(pin) if not code: self.flash(_('Activation code is invalid.')) return # Mark pin as used (this also fires a pin related transition) # and fire transition start_clearance if code.state == USED: self.flash(_('Activation code has already been used.')) return else: comment = _(u"invalidated") # Here we know that the ac is in state initialized so we do not # expect an exception, but the owner might be different if not invalidate_accesscode( pin,comment,self.context.getStudent().student_id): self.flash(_('You are not the owner of this access code.')) return if self.context.getStudent().state == CLEARED: IWorkflowInfo(self.context.getStudent()).fireTransition( 'pay_first_school_fee') elif self.context.getStudent().state == RETURNING: IWorkflowInfo(self.context.getStudent()).fireTransition( 'pay_school_fee') self.flash(_('Course registration has been started.')) self.redirect(self.url(self.context)) return class AddStudyLevelFormPage(KofaEditFormPage): """ Page for students to add current study levels """ grok.context(IStudentStudyCourse) grok.name('add') grok.require('waeup.handleStudent') grok.template('studyleveladdpage') form_fields = grok.AutoFields(IStudentStudyCourse) pnav = 4 @property def label(self): studylevelsource = StudyLevelSource().factory code = self.context.current_level title = studylevelsource.getTitle(self.context, code) return _('Add current level ${a}', mapping = {'a':title}) def update(self): if self.context.getStudent().state != PAID: emit_lock_message(self) return super(AddStudyLevelFormPage, self).update() return @action(_('Create course list now'), style='primary') def addStudyLevel(self, **data): studylevel = StudentStudyLevel() studylevel.level = self.context.current_level studylevel.level_session = self.context.current_session try: self.context.addStudentStudyLevel( self.context.certificate,studylevel) except KeyError: self.flash(_('This level exists.')) self.redirect(self.url(self.context)) return class StudyLevelEditFormPage(KofaEditFormPage): """ Page to edit the student study level data by students """ grok.context(IStudentStudyLevel) grok.name('edit') grok.require('waeup.handleStudent') grok.template('studyleveleditpage') form_fields = grok.AutoFields(IStudentStudyLevel).omit( 'level_session', 'level_verdict') pnav = 4 def update(self): if self.context.getStudent().state != PAID: emit_lock_message(self) return super(StudyLevelEditFormPage, self).update() datatable.need() warning.need() return @property def label(self): # Here we know that the cookie has been set lang = self.request.cookies.get('kofa.language') level_title = translate(self.context.level_title, 'waeup.kofa', target_language=lang) return _('Add and remove course tickets of study level ${a}', mapping = {'a':level_title}) @property def total_credits(self): total_credits = 0 for key, val in self.context.items(): total_credits += val.credits return total_credits @action(_('Add course ticket')) def addCourseTicket(self, **data): self.redirect(self.url(self.context, 'ctadd')) @jsaction(_('Remove selected tickets')) def delCourseTicket(self, **data): form = self.request.form if form.has_key('val_id'): child_id = form['val_id'] else: self.flash(_('No ticket selected.')) self.redirect(self.url(self.context, '@@edit')) return if not isinstance(child_id, list): child_id = [child_id] deleted = [] for id in child_id: # Students are not allowed to remove core tickets if not self.context[id].mandatory: del self.context[id] deleted.append(id) if len(deleted): self.flash(_('Successfully removed: ${a}', mapping = {'a':', '.join(deleted)})) self.redirect(self.url(self.context, u'@@edit')) return @action(_('Register course list'), style='primary') def RegisterCourses(self, **data): IWorkflowInfo(self.context.getStudent()).fireTransition( 'register_courses') self.flash(_('Course list has been registered.')) self.redirect(self.url(self.context)) return class CourseTicketAddFormPage2(CourseTicketAddFormPage): """Add a course ticket by student. """ grok.name('ctadd') grok.require('waeup.handleStudent') form_fields = grok.AutoFields(ICourseTicketAdd).omit( 'score', 'mandatory', 'automatic', 'carry_over') def update(self): if self.context.getStudent().state != PAID: emit_lock_message(self) return super(CourseTicketAddFormPage2, self).update() return @action(_('Add course ticket')) def addCourseTicket(self, **data): # Safety belt if self.context.getStudent().state != PAID: return ticket = CourseTicket() course = data['course'] for name in ['code', 'title', 'credits', 'passmark', 'semester']: setattr(ticket, name, getattr(course, name)) ticket.automatic = False try: self.context.addCourseTicket(ticket) except KeyError: self.flash(_('The ticket exists.')) return self.flash(_('Successfully added ${a}.', mapping = {'a':ticket.code})) self.redirect(self.url(self.context, u'@@edit')) return class ChangePasswordRequestPage(KofaForm): """Captcha'd page for students to request a password change. """ grok.context(IUniversity) grok.name('sendpw') grok.require('waeup.Anonymous') grok.template('sendpassword') label = _('Send me a new password') form_fields = grok.AutoFields(IStudentChangePassword) def update(self): # Handle captcha self.captcha = getUtility(ICaptchaManager).getCaptcha() self.captcha_result = self.captcha.verify(self.request) self.captcha_code = self.captcha.display(self.captcha_result.error_code) return @action(_('Get new login credentials'), style='primary') def request(self, **data): if not self.captcha_result.is_valid: # Captcha will display error messages automatically. # No need to flash something. return # Search student or applicant reg_number = data['reg_number'] email = data['email'] cat = queryUtility(ICatalog, name='students_catalog') results = cat.searchResults( reg_number=(reg_number, reg_number), email=(email,email)) if len(results) == 0: # Try also the applicants_catalog if no student record was found. cat = queryUtility(ICatalog, name='applicants_catalog') if cat is None: self.flash(_('Application package not installed.')) return results = cat.searchResults( reg_number=(reg_number, reg_number), email=(email,email)) if len(results) == 0: self.flash(_('No record found.')) return student = list(results)[0] # Change password kofa_utils = getUtility(IKofaUtils) pwd = kofa_utils.genPassword() IUserAccount(student).setPassword(pwd) # Send email with new redentials msg = _('You have successfully changed your password for the') login_url = self.url(grok.getSite(), 'login') success = kofa_utils.sendCredentials( IUserAccount(student),pwd,login_url,msg) if success: self.flash(_('An email with your user name and password ' + 'has been sent to ${a}.', mapping = {'a':email})) else: self.flash(_('An smtp server error occurred.')) return class SetPasswordPage(KofaPage): grok.context(IKofaObject) grok.name('setpassword') grok.require('waeup.Anonymous') grok.template('setpassword') label = _('Set password for first-time login') ac_prefix = 'PWD' pnav = 0 set_button = _('Set') def update(self, SUBMIT=None): self.reg_number = self.request.form.get('reg_number', None) self.ac_series = self.request.form.get('ac_series', None) self.ac_number = self.request.form.get('ac_number', None) if SUBMIT is None: return hitlist = search(query=self.reg_number, searchtype='reg_number', view=self) if not hitlist: self.flash(_('No student found.')) return if len(hitlist) != 1: # Cannot happen but anyway self.flash(_('More than one student found.')) return student = hitlist[0].context self.student_id = student.student_id student_pw = student.password pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number) code = get_access_code(pin) if not code: self.flash(_('Access code is invalid.')) return if student_pw and pin == student.adm_code: self.flash(_( 'Password has already been set. Your Student Id is ${a}', mapping = {'a':self.student_id})) return elif student_pw: self.flash( _('Password has already been set. You are using the ' + 'wrong Access Code.')) return # Mark pin as used (this also fires a pin related transition) # and set student password if code.state == USED: self.flash(_('Access code has already been used.')) return else: comment = _(u"invalidated") # Here we know that the ac is in state initialized so we do not # expect an exception invalidate_accesscode(pin,comment) IUserAccount(student).setPassword(self.ac_number) student.adm_code = pin self.flash(_('Password has been set. Your Student Id is ${a}', mapping = {'a':self.student_id})) return