source: main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py @ 6942

Last change on this file since 6942 was 6940, checked in by Henrik Bettermann, 14 years ago

We only need a PaymentsManageFormPage? which can be used by both students and students officers but students are not allowed to remove payment tickets which have received a valid callback.

  • Property svn:keywords set to Id
File size: 46.1 KB
RevLine 
[6621]1## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
2## This program is free software; you can redistribute it and/or modify
3## it under the terms of the GNU General Public License as published by
4## the Free Software Foundation; either version 2 of the License, or
5## (at your option) any later version.
6##
7## This program is distributed in the hope that it will be useful,
8## but WITHOUT ANY WARRANTY; without even the implied warranty of
9## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10## GNU General Public License for more details.
11##
12## You should have received a copy of the GNU General Public License
13## along with this program; if not, write to the Free Software
14## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15##
16"""UI components for students and related components.
17"""
18import grok
[6869]19from time import time
20from datetime import date, datetime
[6621]21from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
[6760]22from zope.component import createObject
[6936]23from waeup.sirp.accesscodes import (
[6937]24    invalidate_accesscode, get_access_code, create_accesscode)
[6621]25from waeup.sirp.accesscodes.workflow import USED
26from waeup.sirp.browser import (
27    WAeUPPage, WAeUPEditFormPage, WAeUPAddFormPage, WAeUPDisplayFormPage)
28from waeup.sirp.browser.breadcrumbs import Breadcrumb
[6775]29from waeup.sirp.browser.resources import datepicker, datatable, tabs
[6621]30from waeup.sirp.browser.viewlets import (
[6760]31    ManageActionButton, PrimaryNavTab, AddActionButton)
32from waeup.sirp.interfaces import IWAeUPObject, IUserAccount
[6621]33from waeup.sirp.widgets.datewidget import (
[6869]34    FriendlyDateWidget, FriendlyDateDisplayWidget,
35    FriendlyDatetimeDisplayWidget)
[6912]36from waeup.sirp.university.vocabularies import study_modes
[6621]37from waeup.sirp.students.interfaces import (
[6756]38    IStudentsContainer, IStudent, IStudentClearance, IStudentPasswordSetting,
[6859]39    IStudentPersonal, IStudentBase, IStudentStudyCourse,
[6774]40    IStudentAccommodation, IStudentClearanceEdit, IStudentStudyLevel,
[6877]41    ICourseTicket, ICourseTicketAdd, IStudentPaymentsContainer,
42    IStudentOnlinePayment
[6621]43    )
[6626]44from waeup.sirp.students.catalog import search
[6722]45from waeup.sirp.students.workflow import CLEARANCE
[6795]46from waeup.sirp.students.studylevel import StudentStudyLevel, CourseTicket
[6775]47from waeup.sirp.students.vocabularies import StudyLevelSource
[6876]48from waeup.sirp.students.utils import getPaymentDetails
[6820]49from waeup.sirp.browser.resources import toggleall
[6940]50from waeup.sirp.authentication import get_principal_role_manager
[6621]51
[6762]52# Save function used for save methods in manager pages
53def msave(view, **data):
54    form = view.request.form
55    ob_class = view.__implemented__.__name__.replace('waeup.sirp.','')
56    changed_fields = view.applyData(view.context, **data)
[6771]57    # Turn list of lists into single list
58    if changed_fields:
59        changed_fields = reduce(lambda x,y: x+y, changed_fields.values())
60    fields_string = ' + '.join(changed_fields)
[6762]61    view.context._p_changed = True
62    view.flash('Form has been saved.')
63    if fields_string:
[6794]64        view.context.getStudent().loggerInfo(ob_class, 'saved: % s' % fields_string)
[6762]65    return
66
[6621]67class StudentsTab(PrimaryNavTab):
68    """Students tab in primary navigation.
69    """
70
71    grok.context(IWAeUPObject)
72    grok.order(3)
[6660]73    grok.require('waeup.viewStudent')
[6621]74    grok.template('primarynavtab')
75
76    pnav = 4
77    tab_title = u'Students'
78
79    @property
80    def link_target(self):
81        return self.view.application_url('students')
82
[6629]83class StudentsBreadcrumb(Breadcrumb):
84    """A breadcrumb for the students container.
85    """
86    grok.context(IStudentsContainer)
87    title = u'Students'
88
[6818]89class StudentBreadcrumb(Breadcrumb):
90    """A breadcrumb for the student container.
91    """
92    grok.context(IStudent)
93
94    def title(self):
95        return self.context.fullname
96
[6635]97class SudyCourseBreadcrumb(Breadcrumb):
98    """A breadcrumb for the student study course.
99    """
100    grok.context(IStudentStudyCourse)
101    title = u'Study Course'
102
103class PaymentsBreadcrumb(Breadcrumb):
104    """A breadcrumb for the student payments folder.
105    """
[6859]106    grok.context(IStudentPaymentsContainer)
[6635]107    title = u'Payments'
108
[6870]109class OnlinePaymentBreadcrumb(Breadcrumb):
110    """A breadcrumb for course lists.
111    """
[6877]112    grok.context(IStudentOnlinePayment)
[6870]113
114    @property
115    def title(self):
116        return self.context.p_id
117
[6635]118class AccommodationBreadcrumb(Breadcrumb):
119    """A breadcrumb for the student accommodation folder.
120    """
121    grok.context(IStudentAccommodation)
122    title = u'Accommodation'
123
[6776]124class StudyLevelBreadcrumb(Breadcrumb):
125    """A breadcrumb for course lists.
126    """
127    grok.context(IStudentStudyLevel)
128
129    @property
130    def title(self):
131        return self.context.level_title
132
[6626]133class StudentsContainerPage(WAeUPPage):
134    """The standard view for student containers.
[6621]135    """
136    grok.context(IStudentsContainer)
137    grok.name('index')
[6660]138    grok.require('waeup.viewStudent')
[6695]139    grok.template('containerpage')
[6654]140    label = 'Student Section'
141    title = 'Students'
[6642]142    pnav = 4
[6621]143
[6626]144    def update(self, *args, **kw):
145        datatable.need()
146        form = self.request.form
147        self.hitlist = []
148        if 'searchterm' in form and form['searchterm']:
149            self.searchterm = form['searchterm']
150            self.searchtype = form['searchtype']
151        elif 'old_searchterm' in form:
152            self.searchterm = form['old_searchterm']
153            self.searchtype = form['old_searchtype']
154        else:
155            if 'search' in form:
156                self.flash('Empty search string.')
157            return
158        self.hitlist = search(query=self.searchterm,
159            searchtype=self.searchtype, view=self)
160        if not self.hitlist:
161            self.flash('No student found.')
162        return
163
[6773]164class SetPasswordPage(WAeUPPage):
[6699]165    grok.context(IWAeUPObject)
166    grok.name('setpassword')
167    grok.require('waeup.Public')
[6774]168    grok.template('setpassword')
[6699]169    title = ''
170    label = 'Set password for first-time login'
[6758]171    ac_prefix = 'PWD'
[6715]172    pnav = 0
[6699]173
174    def update(self, SUBMIT=None):
[6758]175        self.reg_number = self.request.form.get('reg_number', None)
176        self.ac_series = self.request.form.get('ac_series', None)
177        self.ac_number = self.request.form.get('ac_number', None)
[6756]178
[6699]179        if SUBMIT is None:
180            return
181        hitlist = search(query=self.reg_number,
182            searchtype='reg_number', view=self)
183        if not hitlist:
184            self.flash('No student found.')
185            return
186        if len(hitlist) != 1:   # Cannot happen but anyway
187            self.flash('More than one student found.')
188            return
[6704]189        student = hitlist[0].context
190        self.student_id = student.student_id
191        student_pw = student.password
[6758]192        pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
[6699]193        code = get_access_code(pin)
194        if not code:
195            self.flash('Access code is invalid.')
196            return
[6704]197        if student_pw and pin == student.adm_code:
198            self.flash('Password has already been set. Your Student Id is %s'
199                % self.student_id)
200            return
201        elif student_pw:
[6800]202            self.flash('Password has already been set. You are using the wrong Access Code.')
[6704]203            return
[6699]204        # Mark pin as used (this also fires a pin related transition)
205        # and set student password
206        if code.state == USED:
207            self.flash('Access code has already been used.')
208            return
209        else:
[6704]210            comment = u"AC invalidated for %s" % self.student_id
[6699]211            # Here we know that the ac is in state initialized so we do not
212            # expect an exception
213            #import pdb; pdb.set_trace()
214            invalidate_accesscode(pin,comment)
[6758]215            IUserAccount(student).setPassword(self.ac_number)
[6769]216            student.adm_code = pin
[6704]217        self.flash('Password has been set. Your Student Id is %s'
218            % self.student_id)
[6699]219        return
220
[6626]221class StudentsContainerManageActionButton(ManageActionButton):
[6622]222    grok.order(1)
223    grok.context(IStudentsContainer)
224    grok.view(StudentsContainerPage)
225    grok.require('waeup.manageStudents')
[6647]226    text = 'Manage student section'
[6622]227
[6626]228
229class StudentsContainerManagePage(WAeUPPage):
230    """The manage page for student containers.
[6622]231    """
232    grok.context(IStudentsContainer)
233    grok.name('manage')
234    grok.require('waeup.manageStudents')
[6695]235    grok.template('containermanagepage')
[6642]236    pnav = 4
[6647]237    title = 'Manage student section'
[6622]238
239    @property
240    def label(self):
241        return self.title
242
[6626]243    def update(self, *args, **kw):
244        datatable.need()
[6820]245        toggleall.need()
[6626]246        form = self.request.form
247        self.hitlist = []
248        if 'searchterm' in form and form['searchterm']:
249            self.searchterm = form['searchterm']
250            self.searchtype = form['searchtype']
251        elif 'old_searchterm' in form:
252            self.searchterm = form['old_searchterm']
253            self.searchtype = form['old_searchtype']
254        else:
255            if 'search' in form:
256                self.flash('Empty search string.')
257            return
258        if not 'entries' in form:
259            self.hitlist = search(query=self.searchterm,
260                searchtype=self.searchtype, view=self)
261            if not self.hitlist:
262                self.flash('No student found.')
263            return
264        entries = form['entries']
265        if isinstance(entries, basestring):
266            entries = [entries]
267        deleted = []
268        for entry in entries:
269            if 'remove' in form:
270                del self.context[entry]
271                deleted.append(entry)
272        self.hitlist = search(query=self.searchterm,
273            searchtype=self.searchtype, view=self)
274        if len(deleted):
275            self.flash('Successfully removed: %s' % ', '.join(deleted))
[6622]276        return
277
[6626]278class StudentsContainerAddActionButton(AddActionButton):
279    grok.order(1)
280    grok.context(IStudentsContainer)
281    grok.view(StudentsContainerManagePage)
282    grok.require('waeup.manageStudents')
283    text = 'Add student'
284    target = 'addstudent'
285
[6622]286class StudentAddFormPage(WAeUPAddFormPage):
287    """Add-form to add a student.
288    """
289    grok.context(IStudentsContainer)
290    grok.require('waeup.manageStudents')
291    grok.name('addstudent')
292    grok.template('studentaddpage')
293    form_fields = grok.AutoFields(IStudent)
294    title = 'Students'
295    label = 'Add student'
[6642]296    pnav = 4
[6622]297
298    @grok.action('Create student record')
299    def addStudent(self, **data):
300        student = createObject(u'waeup.Student')
301        self.applyData(student, **data)
[6652]302        self.context.addStudent(student)
[6626]303        self.flash('Student record created.')
[6651]304        self.redirect(self.url(self.context[student.student_id], 'index'))
[6622]305        return
306
[6631]307class StudentBaseDisplayFormPage(WAeUPDisplayFormPage):
308    """ Page to display student base data
309    """
[6622]310    grok.context(IStudent)
311    grok.name('index')
[6660]312    grok.require('waeup.viewStudent')
[6695]313    grok.template('basepage')
[6756]314    form_fields = grok.AutoFields(IStudentBase).omit('password')
[6642]315    pnav = 4
316    title = 'Base Data'
[6622]317
318    @property
319    def label(self):
[6818]320        return '%s: Base Data' % self.context.fullname
[6631]321
[6699]322    @property
323    def hasPassword(self):
324        if self.context.password:
325            return 'set'
326        return 'unset'
327
[6631]328class StudentBaseManageActionButton(ManageActionButton):
329    grok.order(1)
330    grok.context(IStudent)
331    grok.view(StudentBaseDisplayFormPage)
332    grok.require('waeup.manageStudents')
[6695]333    text = 'Manage'
[6631]334    target = 'edit_base'
335
336class StudentBaseManageFormPage(WAeUPEditFormPage):
337    """ View to edit student base data
338    """
339    grok.context(IStudent)
340    grok.name('edit_base')
341    grok.require('waeup.manageStudents')
342    form_fields = grok.AutoFields(IStudentBase).omit('student_id')
[6695]343    grok.template('basemanagepage')
344    label = 'Manage base data'
[6642]345    title = 'Base Data'
346    pnav = 4
[6631]347
[6638]348    def update(self):
349        datepicker.need() # Enable jQuery datepicker in date fields.
350        super(StudentBaseManageFormPage, self).update()
351        self.wf_info = IWorkflowInfo(self.context)
352        return
353
354    def getTransitions(self):
355        """Return a list of dicts of allowed transition ids and titles.
356
357        Each list entry provides keys ``name`` and ``title`` for
358        internal name and (human readable) title of a single
359        transition.
360        """
361        allowed_transitions = self.wf_info.getManualTransitions()
362        return [dict(name='', title='No transition')] +[
363            dict(name=x, title=y) for x, y in allowed_transitions]
364
365    @grok.action('Save')
366    def save(self, **data):
[6701]367        form = self.request.form
368        ob_class = self.__implemented__.__name__.replace('waeup.sirp.','')
[6790]369        password = form.get('password', None)
370        password_ctl = form.get('control_password', None)
371        if password:
372            if (password != password_ctl):
[6701]373                self.flash('Passwords do not match.')
[6790]374            else:
375                # XXX: This is too early. PW should only be saved if there
376                #      are no (other) errors left in form.
377                IUserAccount(self.context).setPassword(password)
378                self.context.loggerInfo(ob_class, 'password changed')
379
[6789]380        #self.reg_number = form.get('form.reg_number', None)
381        #if self.reg_number:
382        #    hitlist = search(query=self.reg_number,searchtype='reg_number', view=self)
383        #    if hitlist and hitlist[0].student_id != self.context.student_id:
384        #        self.flash('Registration number exists.')
385        #        return
386        #self.matric_number = form.get('form.matric_number', None)
387        #if self.matric_number:
388        #    hitlist = search(query=self.matric_number,
389        #        searchtype='matric_number', view=self)
390        #    if hitlist and hitlist[0].student_id != self.context.student_id:
391        #        self.flash('Matriculation number exists.')
392        #        return
393
[6771]394        # Turn list of lists into single list
[6638]395        changed_fields = self.applyData(self.context, **data)
[6771]396        if changed_fields:
397            changed_fields = reduce(lambda x,y: x+y, changed_fields.values())
398        fields_string = ' + '.join(changed_fields)
[6638]399        self.context._p_changed = True
400        if form.has_key('transition') and form['transition']:
401            transition_id = form['transition']
402            self.wf_info.fireTransition(transition_id)
403        self.flash('Form has been saved.')
[6644]404        if fields_string:
405            self.context.loggerInfo(ob_class, 'saved: % s' % fields_string)
[6638]406        return
407
[6631]408class StudentClearanceDisplayFormPage(WAeUPDisplayFormPage):
409    """ Page to display student clearance data
410    """
411    grok.context(IStudent)
412    grok.name('view_clearance')
[6660]413    grok.require('waeup.viewStudent')
[6695]414    form_fields = grok.AutoFields(IStudentClearance).omit('clearance_locked')
[6650]415    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
[6642]416    title = 'Clearance Data'
417    pnav = 4
[6631]418
419    @property
420    def label(self):
[6818]421        return '%s: Clearance Data' % self.context.fullname
[6631]422
423class StudentClearanceManageActionButton(ManageActionButton):
424    grok.order(1)
425    grok.context(IStudent)
426    grok.view(StudentClearanceDisplayFormPage)
427    grok.require('waeup.manageStudents')
[6695]428    text = 'Manage'
[6631]429    target = 'edit_clearance'
430
431class StudentClearanceManageFormPage(WAeUPEditFormPage):
432    """ Page to edit student clearance data
433    """
434    grok.context(IStudent)
435    grok.name('edit_clearance')
[6649]436    grok.require('waeup.manageStudents')
[6631]437    form_fields = grok.AutoFields(IStudentClearance)
[6695]438    label = 'Manage clearance data'
[6642]439    title = 'Clearance Data'
440    pnav = 4
[6631]441
[6650]442    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
443
444    def update(self):
445        datepicker.need() # Enable jQuery datepicker in date fields.
446        return super(StudentClearanceManageFormPage, self).update()
447
[6695]448    @grok.action('Save')
449    def save(self, **data):
[6762]450        msave(self, **data)
[6695]451        return
452
[6631]453class StudentPersonalDisplayFormPage(WAeUPDisplayFormPage):
454    """ Page to display student personal data
455    """
456    grok.context(IStudent)
457    grok.name('view_personal')
[6660]458    grok.require('waeup.viewStudent')
[6631]459    form_fields = grok.AutoFields(IStudentPersonal)
[6642]460    title = 'Personal Data'
461    pnav = 4
[6631]462
463    @property
464    def label(self):
[6818]465        return '%s: Personal Data' % self.context.fullname
[6631]466
467class StudentPersonalManageActionButton(ManageActionButton):
468    grok.order(1)
469    grok.context(IStudent)
470    grok.view(StudentPersonalDisplayFormPage)
471    grok.require('waeup.manageStudents')
[6695]472    text = 'Manage'
[6631]473    target = 'edit_personal'
474
475class StudentPersonalManageFormPage(WAeUPEditFormPage):
476    """ Page to edit student clearance data
477    """
478    grok.context(IStudent)
479    grok.name('edit_personal')
[6660]480    grok.require('waeup.viewStudent')
[6631]481    form_fields = grok.AutoFields(IStudentPersonal)
[6695]482    label = 'Manage personal data'
[6642]483    title = 'Personal Data'
484    pnav = 4
[6631]485
[6762]486    @grok.action('Save')
487    def save(self, **data):
488        msave(self, **data)
489        return
490
[6635]491class StudyCourseDisplayFormPage(WAeUPDisplayFormPage):
492    """ Page to display the student study course data
493    """
494    grok.context(IStudentStudyCourse)
495    grok.name('index')
[6660]496    grok.require('waeup.viewStudent')
[6635]497    form_fields = grok.AutoFields(IStudentStudyCourse)
[6775]498    grok.template('studycoursepage')
[6642]499    title = 'Study Course'
500    pnav = 4
[6635]501
502    @property
503    def label(self):
[6818]504        return '%s: Study Course' % self.context.__parent__.fullname
[6635]505
[6912]506    @property
507    def current_mode(self):
508        return study_modes.getTermByToken(
509            self.context.certificate.study_mode).title
510
[6649]511class StudyCourseManageActionButton(ManageActionButton):
512    grok.order(1)
513    grok.context(IStudentStudyCourse)
514    grok.view(StudyCourseDisplayFormPage)
515    grok.require('waeup.manageStudents')
[6695]516    text = 'Manage'
[6775]517    target = 'manage'
[6649]518
519class StudyCourseManageFormPage(WAeUPEditFormPage):
520    """ Page to edit the student study course data
521    """
522    grok.context(IStudentStudyCourse)
[6775]523    grok.name('manage')
[6649]524    grok.require('waeup.manageStudents')
[6775]525    grok.template('studycoursemanagepage')
[6649]526    form_fields = grok.AutoFields(IStudentStudyCourse)
527    title = 'Study Course'
[6695]528    label = 'Manage study course'
[6649]529    pnav = 4
[6775]530    taboneactions = ['Save','Cancel']
531    tabtwoactions = ['Remove selected levels','Cancel']
532    tabthreeactions = ['Add study level']
[6649]533
[6775]534    def update(self):
535        super(StudyCourseManageFormPage, self).update()
536        tabs.need()
537        datatable.need()
538        return
539
[6761]540    @grok.action('Save')
541    def save(self, **data):
[6762]542        msave(self, **data)
[6761]543        return
544
[6775]545    @property
546    def level_dict(self):
547        studylevelsource = StudyLevelSource().factory
548        for code in studylevelsource.getValues(self.context):
549            title = studylevelsource.getTitle(self.context, code)
550            yield(dict(code=code, title=title))
551
552    @grok.action('Add study level')
[6774]553    def addStudyLevel(self, **data):
[6775]554        level_code = self.request.form.get('addlevel', None)
[6774]555        studylevel = StudentStudyLevel()
[6775]556        studylevel.level = int(level_code)
557        try:
[6782]558            self.context.addStudentStudyLevel(
559                self.context.certificate,studylevel)
[6775]560        except KeyError:
561            self.flash('This level exists.')
562        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
[6774]563        return
564
[6775]565    @grok.action('Remove selected levels')
566    def delStudyLevels(self, **data):
567        form = self.request.form
568        if form.has_key('val_id'):
569            child_id = form['val_id']
570        else:
571            self.flash('No study level selected.')
572            self.redirect(self.url(self.context, '@@manage')+'#tab-2')
573            return
574        if not isinstance(child_id, list):
575            child_id = [child_id]
576        deleted = []
577        for id in child_id:
578            try:
579                del self.context[id]
580                deleted.append(id)
581            except:
582                self.flash('Could not delete %s: %s: %s' % (
583                        id, sys.exc_info()[0], sys.exc_info()[1]))
584        if len(deleted):
585            self.flash('Successfully removed: %s' % ', '.join(deleted))
586        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
587        return
[6774]588
589class StudyLevelDisplayFormPage(WAeUPDisplayFormPage):
590    """ Page to display student study levels
591    """
592    grok.context(IStudentStudyLevel)
593    grok.name('index')
594    grok.require('waeup.viewStudent')
[6775]595    form_fields = grok.AutoFields(IStudentStudyLevel)
[6783]596    grok.template('studylevelpage')
[6774]597    pnav = 4
598
599    @property
[6792]600    def title(self):
601        return 'Study Level %s' % self.context.level_title
602
603    @property
[6774]604    def label(self):
[6776]605        return '%s: Study Level %s' % (
[6818]606            self.context.getStudent().fullname,self.context.level_title)
[6774]607
[6803]608    @property
609    def total_credits(self):
610        total_credits = 0
611        for key, val in self.context.items():
612            total_credits += val.credits
613        return total_credits
614
[6792]615class StudyLevelManageActionButton(ManageActionButton):
616    grok.order(1)
617    grok.context(IStudentStudyLevel)
618    grok.view(StudyLevelDisplayFormPage)
619    grok.require('waeup.manageStudents')
620    text = 'Manage'
621    target = 'manage'
622
623class StudyLevelManageFormPage(WAeUPEditFormPage):
624    """ Page to edit the student study level data
625    """
626    grok.context(IStudentStudyLevel)
627    grok.name('manage')
628    grok.require('waeup.manageStudents')
629    grok.template('studylevelmanagepage')
630    form_fields = grok.AutoFields(IStudentStudyLevel)
631    pnav = 4
632    taboneactions = ['Save','Cancel']
[6795]633    tabtwoactions = ['Add course ticket','Remove selected tickets','Cancel']
[6792]634
635    def update(self):
636        super(StudyLevelManageFormPage, self).update()
637        tabs.need()
638        datatable.need()
639        return
640
641    @property
642    def title(self):
643        return 'Study Level %s' % self.context.level_title
644
645    @property
646    def label(self):
647        return 'Manage study level %s' % self.context.level_title
648
649    @grok.action('Save')
650    def save(self, **data):
651        msave(self, **data)
652        return
653
654    @grok.action('Add course ticket')
[6795]655    def addCourseTicket(self, **data):
656        self.redirect(self.url(self.context, '@@add'))
[6792]657
658    @grok.action('Remove selected tickets')
659    def delCourseTicket(self, **data):
660        form = self.request.form
661        if form.has_key('val_id'):
662            child_id = form['val_id']
663        else:
664            self.flash('No ticket selected.')
665            self.redirect(self.url(self.context, '@@manage')+'#tab-2')
666            return
667        if not isinstance(child_id, list):
668            child_id = [child_id]
669        deleted = []
670        for id in child_id:
671            try:
672                del self.context[id]
673                deleted.append(id)
674            except:
675                self.flash('Could not delete %s: %s: %s' % (
676                        id, sys.exc_info()[0], sys.exc_info()[1]))
677        if len(deleted):
678            self.flash('Successfully removed: %s' % ', '.join(deleted))
679        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
680        return
681
[6795]682class CourseTicketAddFormPage(WAeUPAddFormPage):
[6808]683    """Add a course ticket.
[6795]684    """
685    grok.context(IStudentStudyLevel)
686    grok.name('add')
687    grok.require('waeup.manageStudents')
688    label = 'Add course ticket'
[6808]689    form_fields = grok.AutoFields(ICourseTicketAdd).omit(
690        'grade', 'score', 'automatic')
[6795]691    pnav = 4
692
693    @property
694    def title(self):
695        return 'Study Level %s' % self.context.level_title
696
697    @grok.action('Add course ticket')
698    def addCourseTicket(self, **data):
699        ticket = CourseTicket()
700        course = data['course']
701        ticket.core_or_elective = data['core_or_elective']
[6802]702        ticket.automatic = False
[6795]703        ticket.code = course.code
704        ticket.title = course.title
705        ticket.faculty = course.__parent__.__parent__.__parent__.title
706        ticket.department = course.__parent__.__parent__.title
707        ticket.credits = course.credits
708        ticket.passmark = course.passmark
709        ticket.semester = course.semester
710        try:
711            self.context.addCourseTicket(ticket)
712        except KeyError:
713            self.flash('The ticket exists.')
714            return
[6799]715        self.flash('Successfully added %s.' % ticket.code)
[6795]716        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
717        return
718
719    @grok.action('Cancel')
720    def cancel(self, **data):
721        self.redirect(self.url(self.context))
722
[6796]723class CourseTicketDisplayFormPage(WAeUPDisplayFormPage):
724    """ Page to display course tickets
725    """
726    grok.context(ICourseTicket)
727    grok.name('index')
728    grok.require('waeup.viewStudent')
729    form_fields = grok.AutoFields(ICourseTicket)
730    grok.template('courseticketpage')
731    pnav = 4
732
733    @property
734    def title(self):
735        return 'Course Ticket %s' % self.context.code
736
737    @property
738    def label(self):
739        return '%s: Course Ticket %s' % (
[6818]740            self.context.getStudent().fullname,self.context.code)
[6796]741
742class CourseTicketManageActionButton(ManageActionButton):
743    grok.order(1)
744    grok.context(ICourseTicket)
745    grok.view(CourseTicketDisplayFormPage)
746    grok.require('waeup.manageStudents')
747    text = 'Manage'
748    target = 'manage'
749
750class CourseTicketManageFormPage(WAeUPEditFormPage):
751    """ Page to manage course tickets
752    """
753    grok.context(ICourseTicket)
754    grok.name('manage')
755    grok.require('waeup.manageStudents')
756    form_fields = grok.AutoFields(ICourseTicket)
757    grok.template('courseticketmanagepage')
758    pnav = 4
759
760    @property
761    def title(self):
762        return 'Course Ticket %s' % self.context.code
763
764    @property
765    def label(self):
766        return 'Manage course ticket %s' % self.context.code
767
768    @grok.action('Save')
769    def save(self, **data):
770        msave(self, **data)
771        return
772
[6940]773# We don't need the display form page yet
[6635]774class PaymentsDisplayFormPage(WAeUPDisplayFormPage):
775    """ Page to display the student payments
776    """
[6859]777    grok.context(IStudentPaymentsContainer)
[6940]778    grok.name('view')
[6660]779    grok.require('waeup.viewStudent')
[6859]780    form_fields = grok.AutoFields(IStudentPaymentsContainer)
[6869]781    grok.template('paymentspage')
[6642]782    title = 'Payments'
783    pnav = 4
[6635]784
[6869]785    def formatDatetime(self,datetimeobj):
786        if isinstance(datetimeobj, datetime):
787            return datetimeobj.strftime("%Y-%m-%d %H:%M:%S")
788        else:
789            return None
790
[6635]791    @property
792    def label(self):
[6818]793        return '%s: Payments' % self.context.__parent__.fullname
[6635]794
[6869]795    def update(self):
796        super(PaymentsDisplayFormPage, self).update()
797        datatable.need()
798        return
799
[6940]800# This manage form page is for both students and students officers.
[6869]801class PaymentsManageFormPage(WAeUPEditFormPage):
802    """ Page to manage the student payments
803    """
804    grok.context(IStudentPaymentsContainer)
[6940]805    grok.name('index')
806    grok.require('waeup.handleStudent')
[6869]807    form_fields = grok.AutoFields(IStudentPaymentsContainer)
808    grok.template('paymentsmanagepage')
809    title = 'Payments'
810    pnav = 4
811
[6940]812    def unremovable(self, ticket):
813        prm = get_principal_role_manager()
814        roles = [x[0] for x in prm.getRolesForPrincipal(self.request.principal.id)]
815        return ('waeup.Student' in roles and ticket.r_code)
816
[6869]817    def formatDatetime(self,datetimeobj):
818        if isinstance(datetimeobj, datetime):
819            return datetimeobj.strftime("%Y-%m-%d %H:%M:%S")
820        else:
821            return None
822
823    @property
824    def label(self):
825        return '%s: Payments' % self.context.__parent__.fullname
826
827    def update(self):
828        super(PaymentsManageFormPage, self).update()
829        datatable.need()
830        return
831
832    @grok.action('Remove selected tickets')
833    def delPaymentTicket(self, **data):
834        form = self.request.form
835        if form.has_key('val_id'):
836            child_id = form['val_id']
837        else:
838            self.flash('No payment selected.')
[6940]839            self.redirect(self.url(self.context))
[6869]840            return
841        if not isinstance(child_id, list):
842            child_id = [child_id]
843        deleted = []
844        for id in child_id:
[6940]845            # Students are not allowed to remove payment used tickets
846            if not self.unremovable(self.context[id]):
847                try:
848                    del self.context[id]
849                    deleted.append(id)
850                except:
851                    self.flash('Could not delete %s: %s: %s' % (
852                            id, sys.exc_info()[0], sys.exc_info()[1]))
[6869]853        if len(deleted):
854            self.flash('Successfully removed: %s' % ', '.join(deleted))
[6940]855        self.redirect(self.url(self.context))
[6869]856        return
857
858    @grok.action('Add online payment ticket')
859    def addPaymentTicket(self, **data):
860        self.redirect(self.url(self.context, '@@addop'))
861
[6940]862#class OnlinePaymentManageActionButton(ManageActionButton):
863#    grok.order(1)
864#    grok.context(IStudentPaymentsContainer)
865#    grok.view(PaymentsDisplayFormPage)
866#    grok.require('waeup.manageStudents')
867#    text = 'Manage payments'
868#    target = 'manage'
[6869]869
870class OnlinePaymentAddFormPage(WAeUPAddFormPage):
871    """ Page to add an online payment ticket
872    """
873    grok.context(IStudentPaymentsContainer)
874    grok.name('addop')
875    grok.require('waeup.handleStudent')
[6877]876    form_fields = grok.AutoFields(IStudentOnlinePayment).select(
[6869]877        'p_category')
[6906]878    #zzgrok.template('addpaymentpage')
[6869]879    label = 'Add online payment'
880    title = 'Payments'
881    pnav = 4
882
883    @grok.action('Create ticket')
884    def createTicket(self, **data):
[6876]885        payment = createObject(u'waeup.StudentOnlinePayment')
[6869]886        self.applyData(payment, **data)
887        timestamp = "%d" % int(time()*1000)
888        #order_id = "%s%s" % (student_id[1:],timestamp)
889        payment.p_id = "p%s" % timestamp
[6876]890        (payment.amount_auth,
[6877]891            payment.p_item, payment.p_session,
[6876]892            payment.surcharge_1,
893            payment.surcharge_2,
[6920]894            payment.surcharge_3,
895            error)  = getPaymentDetails(
[6876]896            data['p_category'],self.context.__parent__)
[6920]897        if error:
898            self.flash(error)
[6940]899            self.redirect(self.url(self.context))
[6920]900            return
[6869]901        self.context[payment.p_id] = payment
902        self.flash('Payment ticket created.')
[6940]903        self.redirect(self.url(self.context))
[6869]904        return
905
906class OnlinePaymentDisplayFormPage(WAeUPDisplayFormPage):
907    """ Page to view an online payment ticket
908    """
[6877]909    grok.context(IStudentOnlinePayment)
[6869]910    grok.name('index')
911    grok.require('waeup.viewStudent')
[6877]912    form_fields = grok.AutoFields(IStudentOnlinePayment)
[6869]913    form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
914    form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
915    pnav = 4
916
917    @property
918    def title(self):
919        return 'Online Payment Ticket %s' % self.context.p_id
920
921    @property
922    def label(self):
923        return '%s: Online Payment Ticket %s' % (
924            self.context.__parent__.__parent__.fullname,self.context.p_id)
925
[6930]926class OnlinePaymentCallbackPage(grok.View):
927    """ Callback view
928    """
929    grok.context(IStudentOnlinePayment)
930    grok.name('callback')
931    grok.require('waeup.payStudent')
932
933    # This update method simulates a valid callback und must be
934    # specified in the customization package. The parameters must be taken
935    # from the incoming request.
936    def update(self):
[6936]937        student = self.context.getStudent()
[6930]938        self.context.r_amount_approved = self.context.amount_auth
939        self.context.r_card_num = u'0000'
940        self.context.r_code = u'00'
941        self.context.p_state = 'paid'
942        self.context.payment_date = datetime.now()
943        if self.context.p_category == 'clearance':
[6936]944            # Create CLR access code
[6937]945            pin, error = create_accesscode('CLR',0,student.student_id)
[6936]946            if error:
[6940]947                self.flash('Valid callback received. ' + error)
[6936]948                return
949            self.context.ac = pin
[6930]950        elif self.context.p_category == 'schoolfee':
[6936]951            # Create SFE access code
[6940]952            pin, error = create_accesscode('SFE',0,student.student_id)
[6936]953            if error:
[6940]954                self.flash('Valid callback received. ' + error)
[6936]955                return
956            self.context.ac = pin
[6940]957        self.flash('Valid callback received.')
958        return
[6930]959
960    def render(self):
[6940]961        self.redirect(self.url(self.context, '@@index'))
[6930]962        return
963
[6635]964class AccommodationDisplayFormPage(WAeUPDisplayFormPage):
965    """ Page to display the student accommodation data
966    """
967    grok.context(IStudentAccommodation)
968    grok.name('index')
[6660]969    grok.require('waeup.viewStudent')
[6635]970    form_fields = grok.AutoFields(IStudentAccommodation)
971    #grok.template('accommodationpage')
[6642]972    title = 'Accommodation'
973    pnav = 4
[6635]974
975    @property
976    def label(self):
[6818]977        return '%s: Accommodation Data' % self.context.__parent__.fullname
[6637]978
979class StudentHistoryPage(WAeUPPage):
980    """ Page to display student clearance data
981    """
982    grok.context(IStudent)
983    grok.name('history')
[6660]984    grok.require('waeup.viewStudent')
[6637]985    grok.template('studenthistory')
[6642]986    title = 'History'
987    pnav = 4
[6637]988
989    @property
990    def label(self):
[6818]991        return '%s: History' % self.context.fullname
[6694]992
993# Pages for students only
994
995class StudentBaseEditActionButton(ManageActionButton):
996    grok.order(1)
997    grok.context(IStudent)
998    grok.view(StudentBaseDisplayFormPage)
999    grok.require('waeup.handleStudent')
1000    text = 'Change password'
1001    target = 'bedit'
1002
[6756]1003class StudentPasswordSetting(grok.Adapter):
1004    """Adapt IStudent to data needed for password settings.
1005
1006    We provide password getters/setters for the attached context (an
1007    IStudent object) that cooperate seamless with the usual
1008    formlib/form techniques.
1009    """
1010    grok.context(IStudent)
1011    grok.provides(IStudentPasswordSetting)
1012
1013    def __init__(self, context):
[6818]1014        self.name = context.fullname
[6756]1015        self.password_repeat = context.password
1016        self.context = context
1017        return
1018
1019    def getPassword(self):
1020        return self.context.password
1021
1022    def setPassword(self, password):
1023        IUserAccount(self.context).setPassword(password)
1024        return
1025
1026    password = property(getPassword, setPassword)
1027
[6694]1028class StudentBaseEditFormPage(WAeUPEditFormPage):
1029    """ View to edit student base data by student
1030    """
1031    grok.context(IStudent)
1032    grok.name('bedit')
1033    grok.require('waeup.handleStudent')
[6756]1034    #form_fields = grok.AutoFields(IStudentBaseEdit).omit(
1035    #    'student_id', 'reg_number', 'matric_number')
1036    form_fields = grok.AutoFields(IStudentPasswordSetting)
[6695]1037    grok.template('baseeditpage')
[6694]1038    label = 'Change password'
1039    title = 'Base Data'
1040    pnav = 4
1041
1042    def update(self):
1043        super(StudentBaseEditFormPage, self).update()
1044        self.wf_info = IWorkflowInfo(self.context)
1045        return
1046
[6756]1047    def onFailure(self, action, data, errors):
1048        new_status = []
1049        other_errors = False
1050        for error in errors:
1051            msg = getattr(error, 'message', '')
1052            if isinstance(msg, basestring) and msg != '':
1053                new_status.append(msg)
1054            else:
1055                other_errors = True
1056        if other_errors:
1057            if new_status:
1058                new_status.append('see below for further errors')
1059            else:
1060                new_status.append('See below for details.')
1061        if new_status:
1062            self.status = u'There were errors: %s' % ', '.join(new_status)
1063        return
1064
1065    @grok.action('Save', failure=onFailure)
[6694]1066    def save(self, **data):
[6771]1067        self.applyData(self.context, **data)
1068        self.flash('Form has been saved.')
[6694]1069        return
[6695]1070
[6719]1071class StudentClearanceStartActionButton(ManageActionButton):
1072    grok.order(1)
1073    grok.context(IStudent)
1074    grok.view(StudentClearanceDisplayFormPage)
1075    grok.require('waeup.handleStudent')
1076    icon = 'actionicon_start.png'
1077    text = 'Start clearance'
1078    target = 'start_clearance'
1079
1080    @property
1081    def target_url(self):
1082        if self.context.state != 'admitted':
1083            return ''
1084        return self.view.url(self.view.context, self.target)
1085
[6773]1086class StartClearancePage(WAeUPPage):
[6770]1087    grok.context(IStudent)
1088    grok.name('start_clearance')
1089    grok.require('waeup.handleStudent')
1090    grok.template('enterpin')
1091    title = 'Start clearance'
1092    label = 'Start clearance'
1093    ac_prefix = 'CLR'
1094    notice = ''
1095    pnav = 4
1096    buttonname = 'Start clearance now'
1097
1098    def update(self, SUBMIT=None):
[6936]1099        if not self.context.state == 'admitted':
1100            self.flash("Wrong state.")
1101            self.redirect(self.url(self.context))
1102            return
[6770]1103        self.ac_series = self.request.form.get('ac_series', None)
1104        self.ac_number = self.request.form.get('ac_number', None)
1105
1106        if SUBMIT is None:
1107            return
1108        pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
1109        code = get_access_code(pin)
1110        if not code:
[6936]1111            self.flash('Activation code is invalid.')
[6770]1112            return
1113        # Mark pin as used (this also fires a pin related transition)
1114        # and fire transition start_clearance
1115        if code.state == USED:
[6936]1116            self.flash('Activation code has already been used.')
[6770]1117            return
1118        else:
1119            comment = u"AC invalidated for %s" % self.context.student_id
1120            # Here we know that the ac is in state initialized so we do not
[6927]1121            # expect an exception, but the owner might be different
1122            if not invalidate_accesscode(pin,comment,self.context.student_id):
1123                self.flash('You are not the owner of this access code.')
1124                return
[6770]1125            self.context.clr_code = pin
1126        IWorkflowInfo(self.context).fireTransition('start_clearance')
[6771]1127        self.flash('Clearance process has been started.')
[6770]1128        self.redirect(self.url(self.context,'cedit'))
1129        return
1130
[6695]1131class StudentClearanceEditActionButton(ManageActionButton):
1132    grok.order(1)
1133    grok.context(IStudent)
1134    grok.view(StudentClearanceDisplayFormPage)
1135    grok.require('waeup.handleStudent')
[6722]1136    text = 'Edit'
[6695]1137    target = 'cedit'
1138
[6717]1139    @property
1140    def target_url(self):
1141        if self.context.clearance_locked:
1142            return ''
1143        return self.view.url(self.view.context, self.target)
1144
[6695]1145class StudentClearanceEditFormPage(StudentClearanceManageFormPage):
1146    """ View to edit student clearance data by student
1147    """
1148    grok.context(IStudent)
1149    grok.name('cedit')
1150    grok.require('waeup.handleStudent')
[6756]1151    form_fields = grok.AutoFields(
1152        IStudentClearanceEdit).omit('clearance_locked')
[6722]1153    label = 'Edit clearance data'
[6695]1154    title = 'Clearance Data'
1155    pnav = 4
1156    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
[6718]1157
1158    def emitLockMessage(self):
1159        self.flash('The requested form is locked (read-only).')
1160        self.redirect(self.url(self.context))
1161        return
1162
1163    def update(self):
1164        if self.context.clearance_locked:
1165            self.emitLockMessage()
1166            return
1167        datepicker.need()
1168        return super(StudentClearanceEditFormPage, self).update()
[6719]1169
[6722]1170    @grok.action('Save')
1171    def save(self, **data):
1172        self.applyData(self.context, **data)
[6771]1173        self.flash('Clearance form has been saved.')
[6722]1174        return
1175
1176    @grok.action('Save and request clearance')
1177    def requestclearance(self, **data):
1178        self.applyData(self.context, **data)
1179        self.context._p_changed = True
1180        #if self.dataNotComplete():
1181        #    self.flash(self.dataNotComplete())
1182        #    return
[6771]1183        self.flash('Clearance form has been saved.')
[6769]1184        self.redirect(self.url(self.context,'request_clearance'))
[6722]1185        return
1186
[6773]1187class RequestClearancePage(WAeUPPage):
[6769]1188    grok.context(IStudent)
1189    grok.name('request_clearance')
1190    grok.require('waeup.handleStudent')
1191    grok.template('enterpin')
1192    title = 'Request clearance'
1193    label = 'Request clearance'
1194    notice = 'Enter the CLR access code used for starting clearance.'
1195    ac_prefix = 'CLR'
1196    pnav = 4
1197    buttonname = 'Request clearance now'
1198
1199    def update(self, SUBMIT=None):
1200        self.ac_series = self.request.form.get('ac_series', None)
1201        self.ac_number = self.request.form.get('ac_number', None)
1202        if SUBMIT is None:
1203            return
1204        pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
1205        if self.context.clr_code != pin:
1206            self.flash("This isn't your CLR access code.")
1207            return
1208        state = IWorkflowState(self.context).getState()
1209        # This shouldn't happen, but the application officer
1210        # might have forgotten to lock the form after changing the state
1211        if state != CLEARANCE:
1212            self.flash('This form cannot be submitted. Wrong state!')
1213            return
1214        IWorkflowInfo(self.context).fireTransition('request_clearance')
1215        self.flash('Clearance has been requested.')
1216        self.redirect(self.url(self.context))
[6789]1217        return
[6806]1218
[6808]1219class AddStudyLevelActionButton(AddActionButton):
1220    grok.order(1)
1221    grok.context(IStudentStudyCourse)
1222    grok.view(StudyCourseDisplayFormPage)
1223    grok.require('waeup.handleStudent')
1224    text = 'Add course list'
1225    target = 'add'
1226
1227    @property
1228    def target_url(self):
1229        student = self.view.context.getStudent()
1230        condition1 = student.state != 'school fee paid'
1231        condition2 = str(student['studycourse'].current_level) in \
1232            self.view.context.keys()
1233        if condition1 or condition2:
1234            return ''
1235        return self.view.url(self.view.context, self.target)
1236
[6806]1237class AddStudyLevelFormPage(WAeUPEditFormPage):
1238    """ Page for students to add current study levels
1239    """
1240    grok.context(IStudentStudyCourse)
1241    grok.name('add')
1242    grok.require('waeup.handleStudent')
1243    grok.template('studyleveladdpage')
1244    form_fields = grok.AutoFields(IStudentStudyCourse)
1245    title = 'Study Course'
1246    pnav = 4
1247
1248    @property
1249    def label(self):
1250        studylevelsource = StudyLevelSource().factory
1251        code = self.context.current_level
1252        title = studylevelsource.getTitle(self.context, code)
1253        return 'Add current level %s' % title
1254
1255    def emitLockMessage(self):
1256        self.flash('The requested form is locked (read-only).')
1257        self.redirect(self.url(self.context))
1258        return
1259
1260    def update(self):
1261        if self.context.getStudent().state != 'school fee paid':
1262            self.emitLockMessage()
1263            return
1264        super(AddStudyLevelFormPage, self).update()
1265        return
1266
1267    @grok.action('Create course list now')
1268    def addStudyLevel(self, **data):
1269        studylevel = StudentStudyLevel()
1270        studylevel.level = self.context.current_level
1271        studylevel.level_session = self.context.current_session
1272        try:
1273            self.context.addStudentStudyLevel(
1274                self.context.certificate,studylevel)
1275        except KeyError:
1276            self.flash('This level exists.')
1277        self.redirect(self.url(self.context))
1278        return
[6808]1279
1280class StudyLevelEditActionButton(ManageActionButton):
1281    grok.order(1)
1282    grok.context(IStudentStudyLevel)
1283    grok.view(StudyLevelDisplayFormPage)
1284    grok.require('waeup.handleStudent')
1285    text = 'Add and remove courses'
1286    target = 'edit'
1287
1288    @property
1289    def target_url(self):
1290        student = self.view.context.getStudent()
1291        condition1 = student.state != 'school fee paid'
1292        condition2 = student[
1293            'studycourse'].current_level != self.view.context.level
1294        if condition1 or condition2:
1295            return ''
1296        return self.view.url(self.view.context, self.target)
1297
1298class StudyLevelEditFormPage(WAeUPEditFormPage):
1299    """ Page to edit the student study level data by students
1300    """
1301    grok.context(IStudentStudyLevel)
1302    grok.name('edit')
1303    grok.require('waeup.handleStudent')
1304    grok.template('studyleveleditpage')
1305    form_fields = grok.AutoFields(IStudentStudyLevel).omit(
1306        'level_session', 'level_verdict')
1307    pnav = 4
1308
1309    def update(self):
1310        super(StudyLevelEditFormPage, self).update()
1311    #    tabs.need()
1312        datatable.need()
1313        return
1314
1315    @property
1316    def title(self):
1317        return 'Study Level %s' % self.context.level_title
1318
1319    @property
1320    def label(self):
1321        return 'Add and remove course tickets of study level %s' % self.context.level_title
1322
1323    @property
1324    def total_credits(self):
1325        total_credits = 0
1326        for key, val in self.context.items():
1327            total_credits += val.credits
1328        return total_credits
1329
1330    @grok.action('Add course ticket')
1331    def addCourseTicket(self, **data):
1332        self.redirect(self.url(self.context, 'ctadd'))
1333
1334    @grok.action('Remove selected tickets')
1335    def delCourseTicket(self, **data):
1336        form = self.request.form
1337        if form.has_key('val_id'):
1338            child_id = form['val_id']
1339        else:
1340            self.flash('No ticket selected.')
1341            self.redirect(self.url(self.context, '@@edit'))
1342            return
1343        if not isinstance(child_id, list):
1344            child_id = [child_id]
1345        deleted = []
1346        for id in child_id:
[6940]1347            # Students are not allowed to remove core tickets
[6808]1348            if not self.context[id].core_or_elective:
1349                try:
1350                    del self.context[id]
1351                    deleted.append(id)
1352                except:
1353                    self.flash('Could not delete %s: %s: %s' % (
1354                            id, sys.exc_info()[0], sys.exc_info()[1]))
1355        if len(deleted):
1356            self.flash('Successfully removed: %s' % ', '.join(deleted))
1357        self.redirect(self.url(self.context, u'@@edit'))
1358        return
1359
[6810]1360    @grok.action('Register course list')
1361    def register_courses(self, **data):
1362        state = IWorkflowState(self.context.getStudent()).getState()
1363        IWorkflowInfo(self.context.getStudent()).fireTransition('register_courses')
1364        self.flash('Course list has been registered.')
1365        self.redirect(self.url(self.context))
1366        return
1367
[6808]1368class CourseTicketAddFormPage2(CourseTicketAddFormPage):
1369    """Add a course ticket by student.
1370    """
1371    grok.name('ctadd')
1372    grok.require('waeup.handleStudent')
1373    form_fields = grok.AutoFields(ICourseTicketAdd).omit(
1374        'grade', 'score', 'core_or_elective', 'automatic')
1375
1376    @grok.action('Add course ticket')
1377    def addCourseTicket(self, **data):
1378        ticket = CourseTicket()
1379        course = data['course']
1380        ticket.automatic = False
1381        ticket.code = course.code
1382        ticket.title = course.title
1383        ticket.faculty = course.__parent__.__parent__.__parent__.title
1384        ticket.department = course.__parent__.__parent__.title
1385        ticket.credits = course.credits
1386        ticket.passmark = course.passmark
1387        ticket.semester = course.semester
1388        try:
1389            self.context.addCourseTicket(ticket)
1390        except KeyError:
1391            self.flash('The ticket exists.')
1392            return
1393        self.flash('Successfully added %s.' % ticket.code)
1394        self.redirect(self.url(self.context, u'@@edit'))
1395        return
Note: See TracBrowser for help on using the repository browser.