## 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 grok from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState from zope.component import createObject from waeup.sirp.accesscodes import invalidate_accesscode, get_access_code from waeup.sirp.accesscodes.workflow import USED from waeup.sirp.browser import ( WAeUPPage, WAeUPEditFormPage, WAeUPAddFormPage, WAeUPDisplayFormPage) from waeup.sirp.browser.breadcrumbs import Breadcrumb from waeup.sirp.browser.resources import datepicker, datatable, tabs from waeup.sirp.browser.viewlets import ( ManageActionButton, PrimaryNavTab, AddActionButton) from waeup.sirp.interfaces import IWAeUPObject, IUserAccount from waeup.sirp.widgets.datewidget import ( FriendlyDateWidget, FriendlyDateDisplayWidget) from waeup.sirp.students.interfaces import ( IStudentsContainer, IStudent, IStudentClearance, IStudentPasswordSetting, IStudentPersonal, IStudentBase, IStudentStudyCourse, IStudentPayments, IStudentAccommodation, IStudentClearanceEdit, IStudentStudyLevel, ICourseTicket, ICourseTicketAdd, ) from waeup.sirp.students.catalog import search from waeup.sirp.students.workflow import CLEARANCE from waeup.sirp.students.studylevel import StudentStudyLevel, CourseTicket from waeup.sirp.students.vocabularies import StudyLevelSource from waeup.sirp.browser.resources import toggleall # Save function used for save methods in manager pages def msave(view, **data): form = view.request.form ob_class = view.__implemented__.__name__.replace('waeup.sirp.','') 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()) fields_string = ' + '.join(changed_fields) view.context._p_changed = True view.flash('Form has been saved.') if fields_string: view.context.getStudent().loggerInfo(ob_class, 'saved: % s' % fields_string) return class StudentsTab(PrimaryNavTab): """Students tab in primary navigation. """ grok.context(IWAeUPObject) grok.order(3) grok.require('waeup.viewStudent') grok.template('primarynavtab') pnav = 4 tab_title = u'Students' @property def link_target(self): return self.view.application_url('students') class StudentsBreadcrumb(Breadcrumb): """A breadcrumb for the students container. """ grok.context(IStudentsContainer) title = u'Students' class StudentBreadcrumb(Breadcrumb): """A breadcrumb for the student container. """ grok.context(IStudent) def title(self): return self.context.fullname class SudyCourseBreadcrumb(Breadcrumb): """A breadcrumb for the student study course. """ grok.context(IStudentStudyCourse) title = u'Study Course' class PaymentsBreadcrumb(Breadcrumb): """A breadcrumb for the student payments folder. """ grok.context(IStudentPayments) title = u'Payments' class AccommodationBreadcrumb(Breadcrumb): """A breadcrumb for the student accommodation folder. """ grok.context(IStudentAccommodation) title = u'Accommodation' class StudyLevelBreadcrumb(Breadcrumb): """A breadcrumb for course lists. """ grok.context(IStudentStudyLevel) @property def title(self): return self.context.level_title class StudentsContainerPage(WAeUPPage): """The standard view for student containers. """ grok.context(IStudentsContainer) grok.name('index') grok.require('waeup.viewStudent') grok.template('containerpage') label = 'Student Section' title = 'Students' 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 self.hitlist = search(query=self.searchterm, searchtype=self.searchtype, view=self) if not self.hitlist: self.flash('No student found.') return class SetPasswordPage(WAeUPPage): grok.context(IWAeUPObject) grok.name('setpassword') grok.require('waeup.Public') grok.template('setpassword') title = '' label = 'Set password for first-time login' ac_prefix = 'PWD' pnav = 0 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 %s' % 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"AC invalidated for %s" % self.student_id # Here we know that the ac is in state initialized so we do not # expect an exception #import pdb; pdb.set_trace() invalidate_accesscode(pin,comment) IUserAccount(student).setPassword(self.ac_number) student.adm_code = pin self.flash('Password has been set. Your Student Id is %s' % self.student_id) return class StudentsContainerManageActionButton(ManageActionButton): grok.order(1) grok.context(IStudentsContainer) grok.view(StudentsContainerPage) grok.require('waeup.manageStudents') text = 'Manage student section' class StudentsContainerManagePage(WAeUPPage): """The manage page for student containers. """ grok.context(IStudentsContainer) grok.name('manage') grok.require('waeup.manageStudents') grok.template('containermanagepage') pnav = 4 title = 'Manage student section' @property def label(self): return self.title def update(self, *args, **kw): datatable.need() toggleall.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 not 'entries' in form: self.hitlist = search(query=self.searchterm, searchtype=self.searchtype, view=self) if not self.hitlist: self.flash('No student found.') 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: %s' % ', '.join(deleted)) return class StudentsContainerAddActionButton(AddActionButton): grok.order(1) grok.context(IStudentsContainer) grok.view(StudentsContainerManagePage) grok.require('waeup.manageStudents') text = 'Add student' target = 'addstudent' class StudentAddFormPage(WAeUPAddFormPage): """Add-form to add a student. """ grok.context(IStudentsContainer) grok.require('waeup.manageStudents') grok.name('addstudent') grok.template('studentaddpage') form_fields = grok.AutoFields(IStudent) title = 'Students' label = 'Add student' pnav = 4 @grok.action('Create student record') 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(WAeUPDisplayFormPage): """ 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 title = 'Base Data' @property def label(self): return '%s: Base Data' % self.context.fullname @property def hasPassword(self): if self.context.password: return 'set' return 'unset' class StudentBaseManageActionButton(ManageActionButton): grok.order(1) grok.context(IStudent) grok.view(StudentBaseDisplayFormPage) grok.require('waeup.manageStudents') text = 'Manage' target = 'edit_base' class StudentBaseManageFormPage(WAeUPEditFormPage): """ View to edit student base data """ grok.context(IStudent) grok.name('edit_base') grok.require('waeup.manageStudents') form_fields = grok.AutoFields(IStudentBase).omit('student_id') grok.template('basemanagepage') label = 'Manage base data' title = 'Base Data' pnav = 4 def update(self): datepicker.need() # Enable jQuery datepicker in date fields. 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] @grok.action('Save') def save(self, **data): form = self.request.form ob_class = self.__implemented__.__name__.replace('waeup.sirp.','') password = form.get('password', None) password_ctl = form.get('control_password', None) if password: if (password != password_ctl): self.flash('Passwords do not match.') else: # XXX: This is too early. PW should only be saved if there # are no (other) errors left in form. IUserAccount(self.context).setPassword(password) self.context.loggerInfo(ob_class, 'password changed') #self.reg_number = form.get('form.reg_number', None) #if self.reg_number: # hitlist = search(query=self.reg_number,searchtype='reg_number', view=self) # if hitlist and hitlist[0].student_id != self.context.student_id: # self.flash('Registration number exists.') # return #self.matric_number = form.get('form.matric_number', None) #if self.matric_number: # hitlist = search(query=self.matric_number, # searchtype='matric_number', view=self) # if hitlist and hitlist[0].student_id != self.context.student_id: # self.flash('Matriculation number exists.') # return # Turn list of lists into single list changed_fields = self.applyData(self.context, **data) if changed_fields: changed_fields = reduce(lambda x,y: x+y, changed_fields.values()) fields_string = ' + '.join(changed_fields) self.context._p_changed = True if form.has_key('transition') and form['transition']: transition_id = form['transition'] self.wf_info.fireTransition(transition_id) self.flash('Form has been saved.') if fields_string: self.context.loggerInfo(ob_class, 'saved: % s' % fields_string) return class StudentClearanceDisplayFormPage(WAeUPDisplayFormPage): """ Page to display student clearance data """ grok.context(IStudent) grok.name('view_clearance') grok.require('waeup.viewStudent') form_fields = grok.AutoFields(IStudentClearance).omit('clearance_locked') form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le') title = 'Clearance Data' pnav = 4 @property def label(self): return '%s: Clearance Data' % self.context.fullname class StudentClearanceManageActionButton(ManageActionButton): grok.order(1) grok.context(IStudent) grok.view(StudentClearanceDisplayFormPage) grok.require('waeup.manageStudents') text = 'Manage' target = 'edit_clearance' class StudentClearanceManageFormPage(WAeUPEditFormPage): """ Page to edit student clearance data """ grok.context(IStudent) grok.name('edit_clearance') grok.require('waeup.manageStudents') form_fields = grok.AutoFields(IStudentClearance) label = 'Manage clearance data' title = 'Clearance Data' pnav = 4 form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year') def update(self): datepicker.need() # Enable jQuery datepicker in date fields. return super(StudentClearanceManageFormPage, self).update() @grok.action('Save') def save(self, **data): msave(self, **data) return class StudentPersonalDisplayFormPage(WAeUPDisplayFormPage): """ Page to display student personal data """ grok.context(IStudent) grok.name('view_personal') grok.require('waeup.viewStudent') form_fields = grok.AutoFields(IStudentPersonal) title = 'Personal Data' pnav = 4 @property def label(self): return '%s: Personal Data' % self.context.fullname class StudentPersonalManageActionButton(ManageActionButton): grok.order(1) grok.context(IStudent) grok.view(StudentPersonalDisplayFormPage) grok.require('waeup.manageStudents') text = 'Manage' target = 'edit_personal' class StudentPersonalManageFormPage(WAeUPEditFormPage): """ Page to edit student clearance data """ grok.context(IStudent) grok.name('edit_personal') grok.require('waeup.viewStudent') form_fields = grok.AutoFields(IStudentPersonal) label = 'Manage personal data' title = 'Personal Data' pnav = 4 @grok.action('Save') def save(self, **data): msave(self, **data) return class StudyCourseDisplayFormPage(WAeUPDisplayFormPage): """ 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') title = 'Study Course' pnav = 4 @property def label(self): return '%s: Study Course' % self.context.__parent__.fullname class StudyCourseManageActionButton(ManageActionButton): grok.order(1) grok.context(IStudentStudyCourse) grok.view(StudyCourseDisplayFormPage) grok.require('waeup.manageStudents') text = 'Manage' target = 'manage' class StudyCourseManageFormPage(WAeUPEditFormPage): """ Page to edit the student study course data """ grok.context(IStudentStudyCourse) grok.name('manage') grok.require('waeup.manageStudents') grok.template('studycoursemanagepage') form_fields = grok.AutoFields(IStudentStudyCourse) title = 'Study Course' 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() datatable.need() return @grok.action('Save') def save(self, **data): msave(self, **data) 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)) @grok.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) except KeyError: self.flash('This level exists.') self.redirect(self.url(self.context, u'@@manage')+'#tab-2') return @grok.action('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')+'#tab-2') return if not isinstance(child_id, list): child_id = [child_id] deleted = [] for id in child_id: try: del self.context[id] deleted.append(id) except: self.flash('Could not delete %s: %s: %s' % ( id, sys.exc_info()[0], sys.exc_info()[1])) if len(deleted): self.flash('Successfully removed: %s' % ', '.join(deleted)) self.redirect(self.url(self.context, u'@@manage')+'#tab-2') return class StudyLevelDisplayFormPage(WAeUPDisplayFormPage): """ 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 @property def title(self): return 'Study Level %s' % self.context.level_title @property def label(self): return '%s: Study Level %s' % ( self.context.getStudent().fullname,self.context.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 StudyLevelManageActionButton(ManageActionButton): grok.order(1) grok.context(IStudentStudyLevel) grok.view(StudyLevelDisplayFormPage) grok.require('waeup.manageStudents') text = 'Manage' target = 'manage' class StudyLevelManageFormPage(WAeUPEditFormPage): """ Page to edit the student study level data """ grok.context(IStudentStudyLevel) grok.name('manage') grok.require('waeup.manageStudents') 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() datatable.need() return @property def title(self): return 'Study Level %s' % self.context.level_title @property def label(self): return 'Manage study level %s' % self.context.level_title @grok.action('Save') def save(self, **data): msave(self, **data) return @grok.action('Add course ticket') def addCourseTicket(self, **data): self.redirect(self.url(self.context, '@@add')) @grok.action('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')+'#tab-2') return if not isinstance(child_id, list): child_id = [child_id] deleted = [] for id in child_id: try: del self.context[id] deleted.append(id) except: self.flash('Could not delete %s: %s: %s' % ( id, sys.exc_info()[0], sys.exc_info()[1])) if len(deleted): self.flash('Successfully removed: %s' % ', '.join(deleted)) self.redirect(self.url(self.context, u'@@manage')+'#tab-2') return class CourseTicketAddFormPage(WAeUPAddFormPage): """Add a course ticket. """ grok.context(IStudentStudyLevel) grok.name('add') grok.require('waeup.manageStudents') label = 'Add course ticket' form_fields = grok.AutoFields(ICourseTicketAdd).omit( 'grade', 'score', 'automatic') pnav = 4 @property def title(self): return 'Study Level %s' % self.context.level_title @grok.action('Add course ticket') def addCourseTicket(self, **data): ticket = CourseTicket() course = data['course'] ticket.core_or_elective = data['core_or_elective'] ticket.automatic = False ticket.code = course.code ticket.title = course.title ticket.faculty = course.__parent__.__parent__.__parent__.title ticket.department = course.__parent__.__parent__.title 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 %s.' % ticket.code) self.redirect(self.url(self.context, u'@@manage')+'#tab-2') return @grok.action('Cancel') def cancel(self, **data): self.redirect(self.url(self.context)) class CourseTicketDisplayFormPage(WAeUPDisplayFormPage): """ 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 title(self): return 'Course Ticket %s' % self.context.code @property def label(self): return '%s: Course Ticket %s' % ( self.context.getStudent().fullname,self.context.code) class CourseTicketManageActionButton(ManageActionButton): grok.order(1) grok.context(ICourseTicket) grok.view(CourseTicketDisplayFormPage) grok.require('waeup.manageStudents') text = 'Manage' target = 'manage' class CourseTicketManageFormPage(WAeUPEditFormPage): """ Page to manage course tickets """ grok.context(ICourseTicket) grok.name('manage') grok.require('waeup.manageStudents') form_fields = grok.AutoFields(ICourseTicket) grok.template('courseticketmanagepage') pnav = 4 @property def title(self): return 'Course Ticket %s' % self.context.code @property def label(self): return 'Manage course ticket %s' % self.context.code @grok.action('Save') def save(self, **data): msave(self, **data) return class PaymentsDisplayFormPage(WAeUPDisplayFormPage): """ Page to display the student payments """ grok.context(IStudentPayments) grok.name('index') grok.require('waeup.viewStudent') form_fields = grok.AutoFields(IStudentPayments) #grok.template('paymentspage') title = 'Payments' pnav = 4 @property def label(self): return '%s: Payments' % self.context.__parent__.fullname class AccommodationDisplayFormPage(WAeUPDisplayFormPage): """ Page to display the student accommodation data """ grok.context(IStudentAccommodation) grok.name('index') grok.require('waeup.viewStudent') form_fields = grok.AutoFields(IStudentAccommodation) #grok.template('accommodationpage') title = 'Accommodation' pnav = 4 @property def label(self): return '%s: Accommodation Data' % self.context.__parent__.fullname class StudentHistoryPage(WAeUPPage): """ Page to display student clearance data """ grok.context(IStudent) grok.name('history') grok.require('waeup.viewStudent') grok.template('studenthistory') title = 'History' pnav = 4 @property def label(self): return '%s: History' % self.context.fullname # Pages for students only class StudentBaseEditActionButton(ManageActionButton): grok.order(1) grok.context(IStudent) grok.view(StudentBaseDisplayFormPage) grok.require('waeup.handleStudent') text = 'Change password' target = 'bedit' class StudentPasswordSetting(grok.Adapter): """Adapt IStudent to data needed for password settings. We provide password getters/setters for the attached context (an IStudent object) that cooperate seamless with the usual formlib/form techniques. """ grok.context(IStudent) grok.provides(IStudentPasswordSetting) def __init__(self, context): self.name = context.fullname self.password_repeat = context.password self.context = context return def getPassword(self): return self.context.password def setPassword(self, password): IUserAccount(self.context).setPassword(password) return password = property(getPassword, setPassword) class StudentBaseEditFormPage(WAeUPEditFormPage): """ View to edit student base data by student """ grok.context(IStudent) grok.name('bedit') grok.require('waeup.handleStudent') #form_fields = grok.AutoFields(IStudentBaseEdit).omit( # 'student_id', 'reg_number', 'matric_number') form_fields = grok.AutoFields(IStudentPasswordSetting) grok.template('baseeditpage') label = 'Change password' title = 'Base Data' pnav = 4 def update(self): super(StudentBaseEditFormPage, self).update() self.wf_info = IWorkflowInfo(self.context) return def onFailure(self, action, data, errors): new_status = [] other_errors = False for error in errors: msg = getattr(error, 'message', '') if isinstance(msg, basestring) and msg != '': new_status.append(msg) else: other_errors = True if other_errors: if new_status: new_status.append('see below for further errors') else: new_status.append('See below for details.') if new_status: self.status = u'There were errors: %s' % ', '.join(new_status) return @grok.action('Save', failure=onFailure) def save(self, **data): self.applyData(self.context, **data) self.flash('Form has been saved.') return class StudentClearanceStartActionButton(ManageActionButton): grok.order(1) grok.context(IStudent) grok.view(StudentClearanceDisplayFormPage) grok.require('waeup.handleStudent') icon = 'actionicon_start.png' text = 'Start clearance' target = 'start_clearance' @property def target_url(self): if self.context.state != 'admitted': return '' return self.view.url(self.view.context, self.target) class StartClearancePage(WAeUPPage): grok.context(IStudent) grok.name('start_clearance') grok.require('waeup.handleStudent') grok.template('enterpin') title = 'Start clearance' label = 'Start clearance' ac_prefix = 'CLR' notice = '' pnav = 4 buttonname = 'Start 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) code = get_access_code(pin) if not code: self.flash('Access 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('Access code has already been used.') return else: comment = u"AC invalidated for %s" % self.context.student_id # Here we know that the ac is in state initialized so we do not # expect an exception invalidate_accesscode(pin,comment) 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 StudentClearanceEditActionButton(ManageActionButton): grok.order(1) grok.context(IStudent) grok.view(StudentClearanceDisplayFormPage) grok.require('waeup.handleStudent') text = 'Edit' target = 'cedit' @property def target_url(self): if self.context.clearance_locked: return '' return self.view.url(self.view.context, self.target) class StudentClearanceEditFormPage(StudentClearanceManageFormPage): """ View to edit student clearance data by student """ grok.context(IStudent) grok.name('cedit') grok.require('waeup.handleStudent') form_fields = grok.AutoFields( IStudentClearanceEdit).omit('clearance_locked') label = 'Edit clearance data' title = 'Clearance Data' pnav = 4 form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year') def emitLockMessage(self): self.flash('The requested form is locked (read-only).') self.redirect(self.url(self.context)) return def update(self): if self.context.clearance_locked: self.emitLockMessage() return datepicker.need() return super(StudentClearanceEditFormPage, self).update() @grok.action('Save') def save(self, **data): self.applyData(self.context, **data) self.flash('Clearance form has been saved.') return @grok.action('Save and request clearance') def requestclearance(self, **data): self.applyData(self.context, **data) self.context._p_changed = True #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(WAeUPPage): grok.context(IStudent) grok.name('request_clearance') grok.require('waeup.handleStudent') grok.template('enterpin') title = 'Request clearance' 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 AddStudyLevelActionButton(AddActionButton): grok.order(1) grok.context(IStudentStudyCourse) grok.view(StudyCourseDisplayFormPage) grok.require('waeup.handleStudent') text = 'Add course list' target = 'add' @property def target_url(self): student = self.view.context.getStudent() condition1 = student.state != 'school fee paid' condition2 = str(student['studycourse'].current_level) in \ self.view.context.keys() if condition1 or condition2: return '' return self.view.url(self.view.context, self.target) class AddStudyLevelFormPage(WAeUPEditFormPage): """ 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) title = 'Study Course' pnav = 4 @property def label(self): studylevelsource = StudyLevelSource().factory code = self.context.current_level title = studylevelsource.getTitle(self.context, code) return 'Add current level %s' % title def emitLockMessage(self): self.flash('The requested form is locked (read-only).') self.redirect(self.url(self.context)) return def update(self): if self.context.getStudent().state != 'school fee paid': self.emitLockMessage() return super(AddStudyLevelFormPage, self).update() return @grok.action('Create course list now') 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 StudyLevelEditActionButton(ManageActionButton): grok.order(1) grok.context(IStudentStudyLevel) grok.view(StudyLevelDisplayFormPage) grok.require('waeup.handleStudent') text = 'Add and remove courses' target = 'edit' @property def target_url(self): student = self.view.context.getStudent() condition1 = student.state != 'school fee paid' condition2 = student[ 'studycourse'].current_level != self.view.context.level if condition1 or condition2: return '' return self.view.url(self.view.context, self.target) class StudyLevelEditFormPage(WAeUPEditFormPage): """ 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): super(StudyLevelEditFormPage, self).update() # tabs.need() datatable.need() return @property def title(self): return 'Study Level %s' % self.context.level_title @property def label(self): return 'Add and remove course tickets of study level %s' % self.context.level_title @property def total_credits(self): total_credits = 0 for key, val in self.context.items(): total_credits += val.credits return total_credits @grok.action('Add course ticket') def addCourseTicket(self, **data): self.redirect(self.url(self.context, 'ctadd')) @grok.action('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: # Student are not allowed to remove core tickets if not self.context[id].core_or_elective: try: del self.context[id] deleted.append(id) except: self.flash('Could not delete %s: %s: %s' % ( id, sys.exc_info()[0], sys.exc_info()[1])) if len(deleted): self.flash('Successfully removed: %s' % ', '.join(deleted)) self.redirect(self.url(self.context, u'@@edit')) return @grok.action('Register course list') def register_courses(self, **data): state = IWorkflowState(self.context.getStudent()).getState() 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( 'grade', 'score', 'core_or_elective', 'automatic') @grok.action('Add course ticket') def addCourseTicket(self, **data): ticket = CourseTicket() course = data['course'] ticket.automatic = False ticket.code = course.code ticket.title = course.title ticket.faculty = course.__parent__.__parent__.__parent__.title ticket.department = course.__parent__.__parent__.title 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 %s.' % ticket.code) self.redirect(self.url(self.context, u'@@edit')) return