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

Last change on this file since 7167 was 7160, checked in by Henrik Bettermann, 13 years ago

Use proper action icons (hopefully we can use them also with the bootstrap toolkit).

  • Property svn:keywords set to Id
File size: 67.9 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"""
[7006]18import sys
[6621]19import grok
[6869]20from time import time
21from datetime import date, datetime
[7015]22from zope.event import notify
[6996]23from zope.catalog.interfaces import ICatalog
[7133]24from zope.component import queryUtility, getUtility
[6621]25from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
[6760]26from zope.component import createObject
[6936]27from waeup.sirp.accesscodes import (
[6937]28    invalidate_accesscode, get_access_code, create_accesscode)
[6621]29from waeup.sirp.accesscodes.workflow import USED
30from waeup.sirp.browser import (
31    WAeUPPage, WAeUPEditFormPage, WAeUPAddFormPage, WAeUPDisplayFormPage)
32from waeup.sirp.browser.breadcrumbs import Breadcrumb
[6775]33from waeup.sirp.browser.resources import datepicker, datatable, tabs
[6621]34from waeup.sirp.browser.viewlets import (
[6760]35    ManageActionButton, PrimaryNavTab, AddActionButton)
[7147]36from waeup.sirp.interfaces import (
37    IWAeUPObject, IUserAccount, IExtFileStore, IPasswordValidator)
[6621]38from waeup.sirp.widgets.datewidget import (
[6869]39    FriendlyDateWidget, FriendlyDateDisplayWidget,
40    FriendlyDatetimeDisplayWidget)
[6912]41from waeup.sirp.university.vocabularies import study_modes
[6621]42from waeup.sirp.students.interfaces import (
[7144]43    IStudentsContainer, IStudent, IStudentClearance,
[6859]44    IStudentPersonal, IStudentBase, IStudentStudyCourse,
[6774]45    IStudentAccommodation, IStudentClearanceEdit, IStudentStudyLevel,
[6877]46    ICourseTicket, ICourseTicketAdd, IStudentPaymentsContainer,
[7150]47    IStudentOnlinePayment, IBedTicket, IStudentsUtils
[6621]48    )
[6626]49from waeup.sirp.students.catalog import search
[6992]50from waeup.sirp.students.workflow import (
[7158]51    CLEARANCE, REQUESTED, RETURNING, CLEARED, REGISTERED, VALIDATED)
[6795]52from waeup.sirp.students.studylevel import StudentStudyLevel, CourseTicket
[6775]53from waeup.sirp.students.vocabularies import StudyLevelSource
[6820]54from waeup.sirp.browser.resources import toggleall
[6940]55from waeup.sirp.authentication import get_principal_role_manager
[6997]56from waeup.sirp.hostels.hostel import NOT_OCCUPIED
[6621]57
[6943]58def write_log_message(view, message):
59    ob_class = view.__implemented__.__name__.replace('waeup.sirp.','')
60    view.context.getStudent().loggerInfo(ob_class, message)
61    return
62
[7133]63# Save function used for save methods in pages
[6762]64def msave(view, **data):
65    form = view.request.form
66    changed_fields = view.applyData(view.context, **data)
[6771]67    # Turn list of lists into single list
68    if changed_fields:
69        changed_fields = reduce(lambda x,y: x+y, changed_fields.values())
70    fields_string = ' + '.join(changed_fields)
[7108]71    #view.context._p_changed = True
[6762]72    view.flash('Form has been saved.')
73    if fields_string:
[7108]74        write_log_message(view, 'saved: %s' % fields_string)
[6762]75    return
76
[7145]77def emit_lock_message(view):
[7133]78    view.flash('The requested form is locked (read-only).')
79    view.redirect(view.url(view.context))
80    return
81
[6621]82class StudentsTab(PrimaryNavTab):
83    """Students tab in primary navigation.
84    """
85
86    grok.context(IWAeUPObject)
[6953]87    grok.order(4)
[6660]88    grok.require('waeup.viewStudent')
[6621]89    grok.template('primarynavtab')
90
91    pnav = 4
92    tab_title = u'Students'
93
94    @property
95    def link_target(self):
96        return self.view.application_url('students')
97
[6629]98class StudentsBreadcrumb(Breadcrumb):
99    """A breadcrumb for the students container.
100    """
101    grok.context(IStudentsContainer)
102    title = u'Students'
103
[6818]104class StudentBreadcrumb(Breadcrumb):
105    """A breadcrumb for the student container.
106    """
107    grok.context(IStudent)
108
109    def title(self):
110        return self.context.fullname
111
[6635]112class SudyCourseBreadcrumb(Breadcrumb):
113    """A breadcrumb for the student study course.
114    """
115    grok.context(IStudentStudyCourse)
116    title = u'Study Course'
117
118class PaymentsBreadcrumb(Breadcrumb):
119    """A breadcrumb for the student payments folder.
120    """
[6859]121    grok.context(IStudentPaymentsContainer)
[6635]122    title = u'Payments'
123
[6870]124class OnlinePaymentBreadcrumb(Breadcrumb):
125    """A breadcrumb for course lists.
126    """
[6877]127    grok.context(IStudentOnlinePayment)
[6870]128
129    @property
130    def title(self):
131        return self.context.p_id
132
[6635]133class AccommodationBreadcrumb(Breadcrumb):
134    """A breadcrumb for the student accommodation folder.
135    """
136    grok.context(IStudentAccommodation)
137    title = u'Accommodation'
138
[7009]139    #@property
140    #def target(self):
141    #    prm = get_principal_role_manager()
142    #    principal = get_current_principal()
143    #    roles = [x[0] for x in prm.getRolesForPrincipal(principal.id)]
144    #    if 'waeup.Student' in roles:
145    #        return 'index'
146    #    else:
147    #        return 'manage'
148
[6994]149class BedTicketBreadcrumb(Breadcrumb):
150    """A breadcrumb for bed tickets.
151    """
152    grok.context(IBedTicket)
[7009]153
[6994]154    @property
155    def title(self):
156        return 'Bed Ticket %s' % self.context.getSessionString()
157
[6776]158class StudyLevelBreadcrumb(Breadcrumb):
159    """A breadcrumb for course lists.
160    """
161    grok.context(IStudentStudyLevel)
162
163    @property
164    def title(self):
165        return self.context.level_title
166
[6626]167class StudentsContainerPage(WAeUPPage):
168    """The standard view for student containers.
[6621]169    """
170    grok.context(IStudentsContainer)
171    grok.name('index')
[6660]172    grok.require('waeup.viewStudent')
[6695]173    grok.template('containerpage')
[6654]174    label = 'Student Section'
175    title = 'Students'
[6642]176    pnav = 4
[6621]177
[6626]178    def update(self, *args, **kw):
179        datatable.need()
180        form = self.request.form
181        self.hitlist = []
182        if 'searchterm' in form and form['searchterm']:
183            self.searchterm = form['searchterm']
184            self.searchtype = form['searchtype']
185        elif 'old_searchterm' in form:
186            self.searchterm = form['old_searchterm']
187            self.searchtype = form['old_searchtype']
188        else:
189            if 'search' in form:
190                self.flash('Empty search string.')
191            return
[7068]192        if self.searchtype == 'current_session':
193            self.searchterm = int(self.searchterm)
[6626]194        self.hitlist = search(query=self.searchterm,
195            searchtype=self.searchtype, view=self)
196        if not self.hitlist:
197            self.flash('No student found.')
198        return
199
[6773]200class SetPasswordPage(WAeUPPage):
[6699]201    grok.context(IWAeUPObject)
202    grok.name('setpassword')
203    grok.require('waeup.Public')
[6774]204    grok.template('setpassword')
[6699]205    title = ''
206    label = 'Set password for first-time login'
[6758]207    ac_prefix = 'PWD'
[6715]208    pnav = 0
[6699]209
210    def update(self, SUBMIT=None):
[6758]211        self.reg_number = self.request.form.get('reg_number', None)
212        self.ac_series = self.request.form.get('ac_series', None)
213        self.ac_number = self.request.form.get('ac_number', None)
[6756]214
[6699]215        if SUBMIT is None:
216            return
217        hitlist = search(query=self.reg_number,
218            searchtype='reg_number', view=self)
219        if not hitlist:
220            self.flash('No student found.')
221            return
222        if len(hitlist) != 1:   # Cannot happen but anyway
223            self.flash('More than one student found.')
224            return
[6704]225        student = hitlist[0].context
226        self.student_id = student.student_id
227        student_pw = student.password
[6758]228        pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
[6699]229        code = get_access_code(pin)
230        if not code:
231            self.flash('Access code is invalid.')
232            return
[6704]233        if student_pw and pin == student.adm_code:
234            self.flash('Password has already been set. Your Student Id is %s'
235                % self.student_id)
236            return
237        elif student_pw:
[6800]238            self.flash('Password has already been set. You are using the wrong Access Code.')
[6704]239            return
[6699]240        # Mark pin as used (this also fires a pin related transition)
241        # and set student password
242        if code.state == USED:
243            self.flash('Access code has already been used.')
244            return
245        else:
[6704]246            comment = u"AC invalidated for %s" % self.student_id
[6699]247            # Here we know that the ac is in state initialized so we do not
248            # expect an exception
249            #import pdb; pdb.set_trace()
250            invalidate_accesscode(pin,comment)
[6758]251            IUserAccount(student).setPassword(self.ac_number)
[6769]252            student.adm_code = pin
[6704]253        self.flash('Password has been set. Your Student Id is %s'
254            % self.student_id)
[6699]255        return
256
[6626]257class StudentsContainerManageActionButton(ManageActionButton):
[6622]258    grok.order(1)
259    grok.context(IStudentsContainer)
260    grok.view(StudentsContainerPage)
[7136]261    grok.require('waeup.manageStudent')
[6647]262    text = 'Manage student section'
[6622]263
[6626]264
265class StudentsContainerManagePage(WAeUPPage):
266    """The manage page for student containers.
[6622]267    """
268    grok.context(IStudentsContainer)
269    grok.name('manage')
[7136]270    grok.require('waeup.manageStudent')
[6695]271    grok.template('containermanagepage')
[6642]272    pnav = 4
[6647]273    title = 'Manage student section'
[6622]274
275    @property
276    def label(self):
277        return self.title
278
[6626]279    def update(self, *args, **kw):
280        datatable.need()
[6820]281        toggleall.need()
[6626]282        form = self.request.form
283        self.hitlist = []
284        if 'searchterm' in form and form['searchterm']:
285            self.searchterm = form['searchterm']
286            self.searchtype = form['searchtype']
287        elif 'old_searchterm' in form:
288            self.searchterm = form['old_searchterm']
289            self.searchtype = form['old_searchtype']
290        else:
291            if 'search' in form:
292                self.flash('Empty search string.')
293            return
294        if not 'entries' in form:
295            self.hitlist = search(query=self.searchterm,
296                searchtype=self.searchtype, view=self)
297            if not self.hitlist:
298                self.flash('No student found.')
299            return
300        entries = form['entries']
301        if isinstance(entries, basestring):
302            entries = [entries]
303        deleted = []
304        for entry in entries:
305            if 'remove' in form:
306                del self.context[entry]
307                deleted.append(entry)
308        self.hitlist = search(query=self.searchterm,
309            searchtype=self.searchtype, view=self)
310        if len(deleted):
311            self.flash('Successfully removed: %s' % ', '.join(deleted))
[6622]312        return
313
[6626]314class StudentsContainerAddActionButton(AddActionButton):
315    grok.order(1)
316    grok.context(IStudentsContainer)
317    grok.view(StudentsContainerManagePage)
[7136]318    grok.require('waeup.manageStudent')
[6626]319    text = 'Add student'
320    target = 'addstudent'
321
[6622]322class StudentAddFormPage(WAeUPAddFormPage):
323    """Add-form to add a student.
324    """
325    grok.context(IStudentsContainer)
[7136]326    grok.require('waeup.manageStudent')
[6622]327    grok.name('addstudent')
328    grok.template('studentaddpage')
329    form_fields = grok.AutoFields(IStudent)
330    title = 'Students'
331    label = 'Add student'
[6642]332    pnav = 4
[6622]333
334    @grok.action('Create student record')
335    def addStudent(self, **data):
336        student = createObject(u'waeup.Student')
337        self.applyData(student, **data)
[6652]338        self.context.addStudent(student)
[6626]339        self.flash('Student record created.')
[6651]340        self.redirect(self.url(self.context[student.student_id], 'index'))
[6622]341        return
342
[6631]343class StudentBaseDisplayFormPage(WAeUPDisplayFormPage):
344    """ Page to display student base data
345    """
[6622]346    grok.context(IStudent)
347    grok.name('index')
[6660]348    grok.require('waeup.viewStudent')
[6695]349    grok.template('basepage')
[6756]350    form_fields = grok.AutoFields(IStudentBase).omit('password')
[6642]351    pnav = 4
352    title = 'Base Data'
[6622]353
354    @property
355    def label(self):
[6818]356        return '%s: Base Data' % self.context.fullname
[6631]357
[6699]358    @property
359    def hasPassword(self):
360        if self.context.password:
361            return 'set'
362        return 'unset'
363
[6631]364class StudentBaseManageActionButton(ManageActionButton):
365    grok.order(1)
366    grok.context(IStudent)
367    grok.view(StudentBaseDisplayFormPage)
[7136]368    grok.require('waeup.manageStudent')
[6695]369    text = 'Manage'
[7133]370    target = 'manage_base'
[6631]371
372class StudentBaseManageFormPage(WAeUPEditFormPage):
[7133]373    """ View to manage student base data
[6631]374    """
375    grok.context(IStudent)
[7133]376    grok.name('manage_base')
[7136]377    grok.require('waeup.manageStudent')
[6631]378    form_fields = grok.AutoFields(IStudentBase).omit('student_id')
[6695]379    grok.template('basemanagepage')
380    label = 'Manage base data'
[6642]381    title = 'Base Data'
382    pnav = 4
[6631]383
[6638]384    def update(self):
385        datepicker.need() # Enable jQuery datepicker in date fields.
[7134]386        tabs.need()
[6638]387        super(StudentBaseManageFormPage, self).update()
388        self.wf_info = IWorkflowInfo(self.context)
389        return
390
391    def getTransitions(self):
392        """Return a list of dicts of allowed transition ids and titles.
393
394        Each list entry provides keys ``name`` and ``title`` for
395        internal name and (human readable) title of a single
396        transition.
397        """
398        allowed_transitions = self.wf_info.getManualTransitions()
399        return [dict(name='', title='No transition')] +[
400            dict(name=x, title=y) for x, y in allowed_transitions]
401
402    @grok.action('Save')
403    def save(self, **data):
[6701]404        form = self.request.form
[6790]405        password = form.get('password', None)
406        password_ctl = form.get('control_password', None)
407        if password:
[7147]408            validator = getUtility(IPasswordValidator)
409            errors = validator.validate_password(password, password_ctl)
410            if errors:
411                self.flash( ' '.join(errors))
412                return
413        changed_fields = self.applyData(self.context, **data)
[6771]414        # Turn list of lists into single list
415        if changed_fields:
416            changed_fields = reduce(lambda x,y: x+y, changed_fields.values())
[7147]417        else:
418            changed_fields = []
419        if password:
420            # Now we know that the form has no errors and can set password ...
421            IUserAccount(self.context).setPassword(password)
422            changed_fields.append('password')
423        # ... and execute transition
[6638]424        if form.has_key('transition') and form['transition']:
425            transition_id = form['transition']
426            self.wf_info.fireTransition(transition_id)
[7147]427        fields_string = ' + '.join(changed_fields)
[6638]428        self.flash('Form has been saved.')
[6644]429        if fields_string:
[6943]430            write_log_message(self, 'saved: % s' % fields_string)
[6638]431        return
432
[6631]433class StudentClearanceDisplayFormPage(WAeUPDisplayFormPage):
434    """ Page to display student clearance data
435    """
436    grok.context(IStudent)
437    grok.name('view_clearance')
[6660]438    grok.require('waeup.viewStudent')
[6695]439    form_fields = grok.AutoFields(IStudentClearance).omit('clearance_locked')
[6650]440    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
[6642]441    title = 'Clearance Data'
442    pnav = 4
[6631]443
444    @property
445    def label(self):
[6818]446        return '%s: Clearance Data' % self.context.fullname
[6631]447
448class StudentClearanceManageActionButton(ManageActionButton):
449    grok.order(1)
450    grok.context(IStudent)
451    grok.view(StudentClearanceDisplayFormPage)
[7136]452    grok.require('waeup.manageStudent')
[6695]453    text = 'Manage'
[6631]454    target = 'edit_clearance'
455
[7158]456class StudentClearActionButton(ManageActionButton):
457    grok.order(2)
458    grok.context(IStudent)
459    grok.view(StudentClearanceDisplayFormPage)
460    grok.require('waeup.clearStudent')
461    text = 'Clear student'
462    target = 'clear'
[7160]463    icon = 'actionicon_accept.png'
[7158]464
465    @property
466    def target_url(self):
467        if self.context.state != REQUESTED:
468            return ''
469        return self.view.url(self.view.context, self.target)
470
471class StudentRejectClearanceActionButton(ManageActionButton):
472    grok.order(2)
473    grok.context(IStudent)
474    grok.view(StudentClearanceDisplayFormPage)
475    grok.require('waeup.clearStudent')
476    text = 'Reject clearance'
477    target = 'reject_clearance'
[7160]478    icon = 'actionicon_reject.png'
[7158]479
480    @property
481    def target_url(self):
482        if self.context.state not in (REQUESTED, CLEARED):
483            return ''
484        return self.view.url(self.view.context, self.target)
485
[6631]486class StudentClearanceManageFormPage(WAeUPEditFormPage):
487    """ Page to edit student clearance data
488    """
489    grok.context(IStudent)
490    grok.name('edit_clearance')
[7136]491    grok.require('waeup.manageStudent')
[7134]492    grok.template('clearanceeditpage')
[6631]493    form_fields = grok.AutoFields(IStudentClearance)
[6695]494    label = 'Manage clearance data'
[6642]495    title = 'Clearance Data'
496    pnav = 4
[6650]497    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
498
499    def update(self):
500        datepicker.need() # Enable jQuery datepicker in date fields.
[7134]501        tabs.need()
[6650]502        return super(StudentClearanceManageFormPage, self).update()
503
[7134]504    @grok.action('Save')
[6695]505    def save(self, **data):
[6762]506        msave(self, **data)
[6695]507        return
508
[7158]509class StudentClearPage(grok.View):
510    """ Clear student by clearance officer
511    """
512    grok.context(IStudent)
513    grok.name('clear')
514    grok.require('waeup.clearStudent')
515
516    def update(self):
517        if self.context.state == REQUESTED:
518            IWorkflowInfo(self.context).fireTransition('clear')
519            self.flash('Student has been cleared.')
520        else:
521            self.flash('Student is in the wrong state.')
522        self.redirect(self.url(self.context,'view_clearance'))
523        return
524
525    def render(self):
526        self.redirect(self.url(self.context, 'view_clearance'))
527        return
528
529class StudentRejectClearancePage(grok.View):
530    """ Reject clearance by clearance officers
531    """
532    grok.context(IStudent)
533    grok.name('reject_clearance')
534    grok.require('waeup.clearStudent')
535
536    def update(self):
537        if self.context.state == CLEARED:
538            IWorkflowInfo(self.context).fireTransition('reset4')
539            self.flash('Clearance has been annulled.')
540        elif self.context.state == REQUESTED:
541            IWorkflowInfo(self.context).fireTransition('reset3')
542            self.flash('Clearance request has been rejected.')
543        else:
544            self.flash('Student is in the wrong state.')
545        self.redirect(self.url(self.context,'view_clearance'))
546        return
547
548    def render(self):
549        self.redirect(self.url(self.context, 'view_clearance'))
550        return
551
[6631]552class StudentPersonalDisplayFormPage(WAeUPDisplayFormPage):
553    """ Page to display student personal data
554    """
555    grok.context(IStudent)
556    grok.name('view_personal')
[6660]557    grok.require('waeup.viewStudent')
[6631]558    form_fields = grok.AutoFields(IStudentPersonal)
[6642]559    title = 'Personal Data'
560    pnav = 4
[6631]561
562    @property
563    def label(self):
[6818]564        return '%s: Personal Data' % self.context.fullname
[6631]565
566class StudentPersonalManageActionButton(ManageActionButton):
567    grok.order(1)
568    grok.context(IStudent)
569    grok.view(StudentPersonalDisplayFormPage)
[7136]570    grok.require('waeup.manageStudent')
[6695]571    text = 'Manage'
[6631]572    target = 'edit_personal'
573
574class StudentPersonalManageFormPage(WAeUPEditFormPage):
575    """ Page to edit student clearance data
576    """
577    grok.context(IStudent)
578    grok.name('edit_personal')
[6660]579    grok.require('waeup.viewStudent')
[6631]580    form_fields = grok.AutoFields(IStudentPersonal)
[6695]581    label = 'Manage personal data'
[6642]582    title = 'Personal Data'
583    pnav = 4
[6631]584
[6762]585    @grok.action('Save')
586    def save(self, **data):
587        msave(self, **data)
588        return
589
[6635]590class StudyCourseDisplayFormPage(WAeUPDisplayFormPage):
591    """ Page to display the student study course data
592    """
593    grok.context(IStudentStudyCourse)
594    grok.name('index')
[6660]595    grok.require('waeup.viewStudent')
[6635]596    form_fields = grok.AutoFields(IStudentStudyCourse)
[6775]597    grok.template('studycoursepage')
[6642]598    title = 'Study Course'
599    pnav = 4
[6635]600
601    @property
602    def label(self):
[6818]603        return '%s: Study Course' % self.context.__parent__.fullname
[6635]604
[6912]605    @property
606    def current_mode(self):
607        return study_modes.getTermByToken(
608            self.context.certificate.study_mode).title
609
[6649]610class StudyCourseManageActionButton(ManageActionButton):
611    grok.order(1)
612    grok.context(IStudentStudyCourse)
613    grok.view(StudyCourseDisplayFormPage)
[7136]614    grok.require('waeup.manageStudent')
[6695]615    text = 'Manage'
[6775]616    target = 'manage'
[6649]617
618class StudyCourseManageFormPage(WAeUPEditFormPage):
619    """ Page to edit the student study course data
620    """
621    grok.context(IStudentStudyCourse)
[6775]622    grok.name('manage')
[7136]623    grok.require('waeup.manageStudent')
[6775]624    grok.template('studycoursemanagepage')
[6649]625    form_fields = grok.AutoFields(IStudentStudyCourse)
626    title = 'Study Course'
[6695]627    label = 'Manage study course'
[6649]628    pnav = 4
[6775]629    taboneactions = ['Save','Cancel']
630    tabtwoactions = ['Remove selected levels','Cancel']
631    tabthreeactions = ['Add study level']
[6649]632
[6775]633    def update(self):
634        super(StudyCourseManageFormPage, self).update()
635        tabs.need()
636        datatable.need()
637        return
638
[6761]639    @grok.action('Save')
640    def save(self, **data):
[6762]641        msave(self, **data)
[6761]642        return
643
[6775]644    @property
645    def level_dict(self):
646        studylevelsource = StudyLevelSource().factory
647        for code in studylevelsource.getValues(self.context):
648            title = studylevelsource.getTitle(self.context, code)
649            yield(dict(code=code, title=title))
650
651    @grok.action('Add study level')
[6774]652    def addStudyLevel(self, **data):
[6775]653        level_code = self.request.form.get('addlevel', None)
[6774]654        studylevel = StudentStudyLevel()
[6775]655        studylevel.level = int(level_code)
656        try:
[6782]657            self.context.addStudentStudyLevel(
658                self.context.certificate,studylevel)
[6775]659        except KeyError:
660            self.flash('This level exists.')
661        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
[6774]662        return
663
[6775]664    @grok.action('Remove selected levels')
665    def delStudyLevels(self, **data):
666        form = self.request.form
667        if form.has_key('val_id'):
668            child_id = form['val_id']
669        else:
670            self.flash('No study level selected.')
671            self.redirect(self.url(self.context, '@@manage')+'#tab-2')
672            return
673        if not isinstance(child_id, list):
674            child_id = [child_id]
675        deleted = []
676        for id in child_id:
677            try:
678                del self.context[id]
679                deleted.append(id)
680            except:
681                self.flash('Could not delete %s: %s: %s' % (
682                        id, sys.exc_info()[0], sys.exc_info()[1]))
683        if len(deleted):
684            self.flash('Successfully removed: %s' % ', '.join(deleted))
685        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
686        return
[6774]687
688class StudyLevelDisplayFormPage(WAeUPDisplayFormPage):
689    """ Page to display student study levels
690    """
691    grok.context(IStudentStudyLevel)
692    grok.name('index')
693    grok.require('waeup.viewStudent')
[6775]694    form_fields = grok.AutoFields(IStudentStudyLevel)
[6783]695    grok.template('studylevelpage')
[6774]696    pnav = 4
697
698    @property
[6792]699    def title(self):
700        return 'Study Level %s' % self.context.level_title
701
702    @property
[6774]703    def label(self):
[6776]704        return '%s: Study Level %s' % (
[6818]705            self.context.getStudent().fullname,self.context.level_title)
[6774]706
[6803]707    @property
708    def total_credits(self):
709        total_credits = 0
710        for key, val in self.context.items():
711            total_credits += val.credits
712        return total_credits
713
[7028]714class CourseRegistrationSlipActionButton(ManageActionButton):
[6792]715    grok.order(1)
716    grok.context(IStudentStudyLevel)
[7029]717    grok.view(StudyLevelDisplayFormPage)
[7028]718    grok.require('waeup.viewStudent')
719    icon = 'actionicon_pdf.png'
720    text = 'Download course registration slip'
721    target = 'course_registration.pdf'
722
723class ExportPDFCourseRegistrationSlipPage(grok.View):
724    """Deliver a PDF slip of the context.
725    """
726    grok.context(IStudentStudyLevel)
727    grok.name('course_registration.pdf')
728    grok.require('waeup.viewStudent')
729    form_fields = grok.AutoFields(IStudentStudyLevel)
730    prefix = 'form'
731
732    @property
733    def label(self):
734        return 'Course Registration Slip %s' % self.context.level_title
735
736    def render(self):
737        studentview = StudentBaseDisplayFormPage(self.context.getStudent(),
738            self.request)
[7150]739        students_utils = getUtility(IStudentsUtils)
740        return students_utils.render_pdf(
741            self,'Course Registration', 'course_registration.pdf',
[7028]742            self.context.getStudent, studentview)
743
744class StudyLevelManageActionButton(ManageActionButton):
745    grok.order(2)
746    grok.context(IStudentStudyLevel)
[6792]747    grok.view(StudyLevelDisplayFormPage)
[7136]748    grok.require('waeup.manageStudent')
[6792]749    text = 'Manage'
750    target = 'manage'
751
752class StudyLevelManageFormPage(WAeUPEditFormPage):
753    """ Page to edit the student study level data
754    """
755    grok.context(IStudentStudyLevel)
756    grok.name('manage')
[7136]757    grok.require('waeup.manageStudent')
[6792]758    grok.template('studylevelmanagepage')
759    form_fields = grok.AutoFields(IStudentStudyLevel)
760    pnav = 4
761    taboneactions = ['Save','Cancel']
[6795]762    tabtwoactions = ['Add course ticket','Remove selected tickets','Cancel']
[6792]763
764    def update(self):
765        super(StudyLevelManageFormPage, self).update()
766        tabs.need()
767        datatable.need()
768        return
769
770    @property
771    def title(self):
772        return 'Study Level %s' % self.context.level_title
773
774    @property
775    def label(self):
776        return 'Manage study level %s' % self.context.level_title
777
778    @grok.action('Save')
779    def save(self, **data):
780        msave(self, **data)
781        return
782
783    @grok.action('Add course ticket')
[6795]784    def addCourseTicket(self, **data):
785        self.redirect(self.url(self.context, '@@add'))
[6792]786
787    @grok.action('Remove selected tickets')
788    def delCourseTicket(self, **data):
789        form = self.request.form
790        if form.has_key('val_id'):
791            child_id = form['val_id']
792        else:
793            self.flash('No ticket selected.')
794            self.redirect(self.url(self.context, '@@manage')+'#tab-2')
795            return
796        if not isinstance(child_id, list):
797            child_id = [child_id]
798        deleted = []
799        for id in child_id:
800            try:
801                del self.context[id]
802                deleted.append(id)
803            except:
804                self.flash('Could not delete %s: %s: %s' % (
805                        id, sys.exc_info()[0], sys.exc_info()[1]))
806        if len(deleted):
807            self.flash('Successfully removed: %s' % ', '.join(deleted))
808        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
809        return
810
[6795]811class CourseTicketAddFormPage(WAeUPAddFormPage):
[6808]812    """Add a course ticket.
[6795]813    """
814    grok.context(IStudentStudyLevel)
815    grok.name('add')
[7136]816    grok.require('waeup.manageStudent')
[6795]817    label = 'Add course ticket'
[6808]818    form_fields = grok.AutoFields(ICourseTicketAdd).omit(
819        'grade', 'score', 'automatic')
[6795]820    pnav = 4
821
822    @property
823    def title(self):
824        return 'Study Level %s' % self.context.level_title
825
826    @grok.action('Add course ticket')
827    def addCourseTicket(self, **data):
828        ticket = CourseTicket()
829        course = data['course']
830        ticket.core_or_elective = data['core_or_elective']
[6802]831        ticket.automatic = False
[6795]832        ticket.code = course.code
833        ticket.title = course.title
834        ticket.faculty = course.__parent__.__parent__.__parent__.title
835        ticket.department = course.__parent__.__parent__.title
836        ticket.credits = course.credits
837        ticket.passmark = course.passmark
838        ticket.semester = course.semester
839        try:
840            self.context.addCourseTicket(ticket)
841        except KeyError:
842            self.flash('The ticket exists.')
843            return
[6799]844        self.flash('Successfully added %s.' % ticket.code)
[6795]845        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
846        return
847
848    @grok.action('Cancel')
849    def cancel(self, **data):
850        self.redirect(self.url(self.context))
851
[6796]852class CourseTicketDisplayFormPage(WAeUPDisplayFormPage):
853    """ Page to display course tickets
854    """
855    grok.context(ICourseTicket)
856    grok.name('index')
857    grok.require('waeup.viewStudent')
858    form_fields = grok.AutoFields(ICourseTicket)
859    grok.template('courseticketpage')
860    pnav = 4
861
862    @property
863    def title(self):
864        return 'Course Ticket %s' % self.context.code
865
866    @property
867    def label(self):
868        return '%s: Course Ticket %s' % (
[6818]869            self.context.getStudent().fullname,self.context.code)
[6796]870
871class CourseTicketManageActionButton(ManageActionButton):
872    grok.order(1)
873    grok.context(ICourseTicket)
874    grok.view(CourseTicketDisplayFormPage)
[7136]875    grok.require('waeup.manageStudent')
[6796]876    text = 'Manage'
877    target = 'manage'
878
879class CourseTicketManageFormPage(WAeUPEditFormPage):
880    """ Page to manage course tickets
881    """
882    grok.context(ICourseTicket)
883    grok.name('manage')
[7136]884    grok.require('waeup.manageStudent')
[6796]885    form_fields = grok.AutoFields(ICourseTicket)
886    grok.template('courseticketmanagepage')
887    pnav = 4
888
889    @property
890    def title(self):
891        return 'Course Ticket %s' % self.context.code
892
893    @property
894    def label(self):
895        return 'Manage course ticket %s' % self.context.code
896
897    @grok.action('Save')
898    def save(self, **data):
899        msave(self, **data)
900        return
901
[6940]902# We don't need the display form page yet
[6943]903#class PaymentsDisplayFormPage(WAeUPDisplayFormPage):
904#    """ Page to display the student payments
905#    """
906#    grok.context(IStudentPaymentsContainer)
907#    grok.name('view')
908#    grok.require('waeup.viewStudent')
909#    form_fields = grok.AutoFields(IStudentPaymentsContainer)
910#    grok.template('paymentspage')
911#    title = 'Payments'
912#    pnav = 4
[6635]913
[6943]914#    def formatDatetime(self,datetimeobj):
915#        if isinstance(datetimeobj, datetime):
916#            return datetimeobj.strftime("%Y-%m-%d %H:%M:%S")
917#        else:
918#            return None
[6869]919
[6943]920#    @property
921#    def label(self):
922#        return '%s: Payments' % self.context.__parent__.fullname
[6635]923
[6943]924#    def update(self):
925#        super(PaymentsDisplayFormPage, self).update()
926#        datatable.need()
927#        return
[6869]928
[6940]929# This manage form page is for both students and students officers.
[6869]930class PaymentsManageFormPage(WAeUPEditFormPage):
931    """ Page to manage the student payments
932    """
933    grok.context(IStudentPaymentsContainer)
[6940]934    grok.name('index')
935    grok.require('waeup.handleStudent')
[6869]936    form_fields = grok.AutoFields(IStudentPaymentsContainer)
937    grok.template('paymentsmanagepage')
938    title = 'Payments'
939    pnav = 4
940
[6940]941    def unremovable(self, ticket):
942        prm = get_principal_role_manager()
943        roles = [x[0] for x in prm.getRolesForPrincipal(self.request.principal.id)]
944        return ('waeup.Student' in roles and ticket.r_code)
945
[6869]946    def formatDatetime(self,datetimeobj):
947        if isinstance(datetimeobj, datetime):
948            return datetimeobj.strftime("%Y-%m-%d %H:%M:%S")
949        else:
950            return None
951
952    @property
953    def label(self):
954        return '%s: Payments' % self.context.__parent__.fullname
955
956    def update(self):
957        super(PaymentsManageFormPage, self).update()
958        datatable.need()
959        return
960
961    @grok.action('Remove selected tickets')
962    def delPaymentTicket(self, **data):
963        form = self.request.form
964        if form.has_key('val_id'):
965            child_id = form['val_id']
966        else:
967            self.flash('No payment selected.')
[6940]968            self.redirect(self.url(self.context))
[6869]969            return
970        if not isinstance(child_id, list):
971            child_id = [child_id]
972        deleted = []
973        for id in child_id:
[6992]974            # Students are not allowed to remove used payment tickets
[6940]975            if not self.unremovable(self.context[id]):
976                try:
977                    del self.context[id]
978                    deleted.append(id)
979                except:
980                    self.flash('Could not delete %s: %s: %s' % (
981                            id, sys.exc_info()[0], sys.exc_info()[1]))
[6869]982        if len(deleted):
983            self.flash('Successfully removed: %s' % ', '.join(deleted))
[6943]984            write_log_message(self,'removed: % s' % ', '.join(deleted))
[6940]985        self.redirect(self.url(self.context))
[6869]986        return
987
988    @grok.action('Add online payment ticket')
989    def addPaymentTicket(self, **data):
990        self.redirect(self.url(self.context, '@@addop'))
991
[6940]992#class OnlinePaymentManageActionButton(ManageActionButton):
993#    grok.order(1)
994#    grok.context(IStudentPaymentsContainer)
995#    grok.view(PaymentsDisplayFormPage)
[7136]996#    grok.require('waeup.manageStudent')
[6940]997#    text = 'Manage payments'
998#    target = 'manage'
[6869]999
1000class OnlinePaymentAddFormPage(WAeUPAddFormPage):
1001    """ Page to add an online payment ticket
1002    """
1003    grok.context(IStudentPaymentsContainer)
1004    grok.name('addop')
1005    grok.require('waeup.handleStudent')
[6877]1006    form_fields = grok.AutoFields(IStudentOnlinePayment).select(
[6869]1007        'p_category')
[6906]1008    #zzgrok.template('addpaymentpage')
[6869]1009    label = 'Add online payment'
1010    title = 'Payments'
1011    pnav = 4
[6947]1012   
[6869]1013    @grok.action('Create ticket')
1014    def createTicket(self, **data):
[7024]1015        p_category = data['p_category']
1016        student = self.context.__parent__
1017        if p_category == 'bed_allocation' and student[
1018            'studycourse'].current_session != grok.getSite()[
1019            'configuration'].accommodation_session:
1020                self.flash(
1021                    'Your current session does not match accommodation session.')
1022                self.redirect(self.url(self.context))
1023                return
[7150]1024        students_utils = getUtility(IStudentsUtils)
1025        pay_details  = students_utils.get_payment_details(
[7024]1026            p_category,student)
[6994]1027        if pay_details['error']:
1028            self.flash(pay_details['error'])
[6940]1029            self.redirect(self.url(self.context))
[6920]1030            return
[7025]1031        p_item = pay_details['p_item']
1032        p_session = pay_details['p_session']
1033        for key in self.context.keys():
1034            ticket = self.context[key]
1035            if ticket.p_category == p_category and \
1036               ticket.p_item == p_item and \
1037               ticket.p_session == p_session:
1038                  self.flash(
1039                      'This payment ticket already exists.')
1040                  self.redirect(self.url(self.context))
1041                  return
1042        payment = createObject(u'waeup.StudentOnlinePayment')
1043        self.applyData(payment, **data)
1044        timestamp = "%d" % int(time()*1000)
1045        #order_id = "%s%s" % (student_id[1:],timestamp)
1046        payment.p_id = "p%s" % timestamp
1047        payment.p_item = p_item
1048        payment.p_session = p_session
[6994]1049        payment.amount_auth = pay_details['amount']
1050        payment.surcharge_1 = pay_details['surcharge_1']
1051        payment.surcharge_2 = pay_details['surcharge_2']
1052        payment.surcharge_3 = pay_details['surcharge_3']
[6869]1053        self.context[payment.p_id] = payment
1054        self.flash('Payment ticket created.')
[6940]1055        self.redirect(self.url(self.context))
[6869]1056        return
1057
1058class OnlinePaymentDisplayFormPage(WAeUPDisplayFormPage):
1059    """ Page to view an online payment ticket
1060    """
[6877]1061    grok.context(IStudentOnlinePayment)
[6869]1062    grok.name('index')
1063    grok.require('waeup.viewStudent')
[6877]1064    form_fields = grok.AutoFields(IStudentOnlinePayment)
[6869]1065    form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
1066    form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
1067    pnav = 4
1068
1069    @property
1070    def title(self):
1071        return 'Online Payment Ticket %s' % self.context.p_id
1072
1073    @property
1074    def label(self):
1075        return '%s: Online Payment Ticket %s' % (
[7019]1076            self.context.getStudent().fullname,self.context.p_id)
[6869]1077
[7019]1078class PaymentReceiptActionButton(ManageActionButton):
[7028]1079    grok.order(1)
[7019]1080    grok.context(IStudentOnlinePayment)
[7029]1081    grok.view(OnlinePaymentDisplayFormPage)
[7019]1082    grok.require('waeup.viewStudent')
1083    icon = 'actionicon_pdf.png'
1084    text = 'Download payment receipt'
1085    target = 'payment_receipt.pdf'
1086
[7028]1087    @property
1088    def target_url(self):
1089        if self.context.p_state != 'paid':
1090            return ''
1091        return self.view.url(self.view.context, self.target)
1092
[7056]1093class RequestCallbackActionButton(ManageActionButton):
1094    grok.order(2)
1095    grok.context(IStudentOnlinePayment)
1096    grok.view(OnlinePaymentDisplayFormPage)
1097    grok.require('waeup.handleStudent')
1098    icon = 'actionicon_call.png'
1099    text = 'Request callback'
1100    target = 'callback'
1101
1102    @property
1103    def target_url(self):
1104        if self.context.p_state != 'unpaid':
1105            return ''
1106        return self.view.url(self.view.context, self.target)
1107
[6930]1108class OnlinePaymentCallbackPage(grok.View):
1109    """ Callback view
1110    """
1111    grok.context(IStudentOnlinePayment)
1112    grok.name('callback')
1113    grok.require('waeup.payStudent')
1114
1115    # This update method simulates a valid callback und must be
1116    # specified in the customization package. The parameters must be taken
1117    # from the incoming request.
1118    def update(self):
[7026]1119        if self.context.p_state == 'paid':
1120            self.flash('This ticket has already been paid.')
1121            return
[6936]1122        student = self.context.getStudent()
[6943]1123        write_log_message(self,'valid callback: %s' % self.context.p_id)
[6930]1124        self.context.r_amount_approved = self.context.amount_auth
1125        self.context.r_card_num = u'0000'
1126        self.context.r_code = u'00'
1127        self.context.p_state = 'paid'
1128        self.context.payment_date = datetime.now()
1129        if self.context.p_category == 'clearance':
[6936]1130            # Create CLR access code
[6937]1131            pin, error = create_accesscode('CLR',0,student.student_id)
[6936]1132            if error:
[6940]1133                self.flash('Valid callback received. ' + error)
[6936]1134                return
1135            self.context.ac = pin
[6930]1136        elif self.context.p_category == 'schoolfee':
[6936]1137            # Create SFE access code
[6940]1138            pin, error = create_accesscode('SFE',0,student.student_id)
[6936]1139            if error:
[6940]1140                self.flash('Valid callback received. ' + error)
[6936]1141                return
1142            self.context.ac = pin
[6994]1143        elif self.context.p_category == 'bed_allocation':
1144            # Create HOS access code
1145            pin, error = create_accesscode('HOS',0,student.student_id)
1146            if error:
1147                self.flash('Valid callback received. ' + error)
1148                return
1149            self.context.ac = pin
[6940]1150        self.flash('Valid callback received.')
1151        return
[6930]1152
1153    def render(self):
[6940]1154        self.redirect(self.url(self.context, '@@index'))
[6930]1155        return
1156
[7019]1157class ExportPDFPaymentSlipPage(grok.View):
1158    """Deliver a PDF slip of the context.
1159    """
1160    grok.context(IStudentOnlinePayment)
1161    grok.name('payment_receipt.pdf')
1162    grok.require('waeup.viewStudent')
1163    form_fields = grok.AutoFields(IStudentOnlinePayment)
1164    form_fields['creation_date'].custom_widget = FriendlyDateDisplayWidget('le')
1165    form_fields['payment_date'].custom_widget = FriendlyDateDisplayWidget('le')
1166    prefix = 'form'
1167
1168    @property
1169    def label(self):
1170        return 'Online Payment Receipt %s' % self.context.p_id
1171
1172    def render(self):
[7028]1173        if self.context.p_state != 'paid':
1174            self.flash('Ticket not yet paid.')
1175            self.redirect(self.url(self.context))
1176            return
[7019]1177        studentview = StudentBaseDisplayFormPage(self.context.getStudent(),
1178            self.request)
[7150]1179        students_utils = getUtility(IStudentsUtils)
1180        return students_utils.render_pdf(self,'Payment', 'payment_receipt.pdf',
[7019]1181            self.context.getStudent, studentview)
1182
[6992]1183# We don't need the display form page yet
1184#class AccommodationDisplayFormPage(WAeUPDisplayFormPage):
1185#    """ Page to display the student accommodation data
1186#    """
1187#    grok.context(IStudentAccommodation)
1188#    grok.name('xxx')
1189#    grok.require('waeup.viewStudent')
1190#    form_fields = grok.AutoFields(IStudentAccommodation)
1191#    #grok.template('accommodationpage')
1192#    title = 'Accommodation'
1193#    pnav = 4
1194
1195#    @property
1196#    def label(self):
1197#        return '%s: Accommodation Data' % self.context.__parent__.fullname
1198
1199# This manage form page is for both students and students officers.
1200class AccommodationManageFormPage(WAeUPEditFormPage):
[7009]1201    """ Page to manage bed tickets.
[6635]1202    """
1203    grok.context(IStudentAccommodation)
1204    grok.name('index')
[6992]1205    grok.require('waeup.handleStudent')
[6635]1206    form_fields = grok.AutoFields(IStudentAccommodation)
[6992]1207    grok.template('accommodationmanagepage')
[6642]1208    title = 'Accommodation'
1209    pnav = 4
[7017]1210    officers_only_actions = ['Remove selected']
[6635]1211
[6992]1212    def formatDatetime(self,datetimeobj):
1213        if isinstance(datetimeobj, datetime):
1214            return datetimeobj.strftime("%Y-%m-%d %H:%M:%S")
1215        else:
1216            return None
1217
[6635]1218    @property
1219    def label(self):
[6992]1220        return '%s: Accommodation' % self.context.__parent__.fullname
[6637]1221
[6992]1222    def update(self):
1223        super(AccommodationManageFormPage, self).update()
1224        datatable.need()
1225        return
1226
[7017]1227    @property
1228    def is_student(self):
1229        prm = get_principal_role_manager()
1230        roles = [x[0] for x in prm.getRolesForPrincipal(self.request.principal.id)]
1231        return 'waeup.Student' in roles
1232
[7009]1233    @grok.action('Remove selected')
1234    def delBedTickets(self, **data):
[7017]1235        if self.is_student:
1236            self.flash('You are not allowed to remove bed tickets.')
1237            self.redirect(self.url(self.context))
1238            return
[6992]1239        form = self.request.form
1240        if form.has_key('val_id'):
1241            child_id = form['val_id']
1242        else:
1243            self.flash('No bed ticket selected.')
1244            self.redirect(self.url(self.context))
1245            return
1246        if not isinstance(child_id, list):
1247            child_id = [child_id]
1248        deleted = []
1249        for id in child_id:
[7068]1250            del self.context[id]
1251            deleted.append(id)
[6992]1252        if len(deleted):
1253            self.flash('Successfully removed: %s' % ', '.join(deleted))
1254            write_log_message(self,'removed: % s' % ', '.join(deleted))
1255        self.redirect(self.url(self.context))
1256        return
1257
[7009]1258    @property
1259    def selected_actions(self):
1260        sa = self.actions
[7017]1261        if self.is_student:
[7009]1262            sa = [action for action in self.actions
[7017]1263                  if not action.label in self.officers_only_actions]
[7009]1264        return sa
1265
[7015]1266class AddBedTicketActionButton(ManageActionButton):
1267    grok.order(1)
1268    grok.context(IStudentAccommodation)
1269    grok.view(AccommodationManageFormPage)
1270    grok.require('waeup.handleStudent')
1271    icon = 'actionicon_home.png'
1272    text = 'Book accommodation'
1273    target = 'add'
1274
[6992]1275class BedTicketAddPage(WAeUPPage):
1276    """ Page to add an online payment ticket
1277    """
1278    grok.context(IStudentAccommodation)
1279    grok.name('add')
1280    grok.require('waeup.handleStudent')
1281    grok.template('enterpin')
[6993]1282    ac_prefix = 'HOS'
[6992]1283    label = 'Add bed ticket'
1284    title = 'Add bed ticket'
1285    pnav = 4
1286    buttonname = 'Create bed ticket'
[6993]1287    notice = ''
[6992]1288
1289    def update(self, SUBMIT=None):
[6996]1290        student = self.context.getStudent()
[7150]1291        students_utils = getUtility(IStudentsUtils)
1292        acc_details  = students_utils.get_accommodation_details(student)
[6996]1293        if not student.state in acc_details['allowed_states']:
[7015]1294            self.flash("You are in the wrong registration state.")
[6992]1295            self.redirect(self.url(self.context))
1296            return
[7061]1297        if student['studycourse'].current_session != acc_details['booking_session']:
1298            self.flash(
1299                'Your current session does not match accommodation session.')
1300            self.redirect(self.url(self.context))
1301            return
1302        if str(acc_details['booking_session']) in self.context.keys():
[7060]1303            self.flash('You already booked a bed space in current accommodation session.')
[7004]1304            self.redirect(self.url(self.context))
1305            return
[6992]1306        self.ac_series = self.request.form.get('ac_series', None)
1307        self.ac_number = self.request.form.get('ac_number', None)
1308        if SUBMIT is None:
1309            return
1310        pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
1311        code = get_access_code(pin)
1312        if not code:
1313            self.flash('Activation code is invalid.')
1314            return
[7060]1315        # Search and book bed
[6997]1316        cat = queryUtility(ICatalog, name='beds_catalog', default=None)
1317        entries = cat.searchResults(
[7003]1318            owner=(student.student_id,student.student_id))
1319        if len(entries):
[7060]1320            # If bed space has bee manually allocated use this bed
[7003]1321            bed = [entry for entry in entries][0]
[7060]1322        else:
1323            # else search for other available beds
1324            entries = cat.searchResults(
1325                bed_type=(acc_details['bt'],acc_details['bt']))
1326            available_beds = [
1327                entry for entry in entries if entry.owner == NOT_OCCUPIED]
1328            if available_beds:
[7150]1329                students_utils = getUtility(IStudentsUtils)
1330                bed = students_utils.select_bed(available_beds)
[7060]1331                bed.bookBed(student.student_id)
1332            else:
1333                self.flash('There is no free bed in your category %s.'
1334                            % acc_details['bt'])
1335                return
[6992]1336        # Mark pin as used (this also fires a pin related transition)
1337        if code.state == USED:
1338            self.flash('Activation code has already been used.')
1339            return
1340        else:
1341            comment = u"AC invalidated for %s" % self.context.getStudent().student_id
1342            # Here we know that the ac is in state initialized so we do not
1343            # expect an exception, but the owner might be different
1344            if not invalidate_accesscode(
1345                pin,comment,self.context.getStudent().student_id):
1346                self.flash('You are not the owner of this access code.')
1347                return
[7060]1348        # Create bed ticket
[6992]1349        bedticket = createObject(u'waeup.BedTicket')
1350        bedticket.booking_code = pin
[6994]1351        bedticket.booking_session = acc_details['booking_session']
[6996]1352        bedticket.bed_type = acc_details['bt']
[7006]1353        bedticket.bed = bed
[6996]1354        hall_title = bed.__parent__.hostel_name
1355        coordinates = bed.getBedCoordinates()[1:]
1356        block, room_nr, bed_nr = coordinates
[7068]1357        bedticket.bed_coordinates = '%s, Block %s, Room %s, Bed %s (%s)' % (
1358            hall_title, block, room_nr, bed_nr, bed.bed_type)
[6996]1359        key = str(acc_details['booking_session'])
1360        self.context[key] = bedticket
1361        self.flash('Bed ticket created and bed booked: %s'
1362            % bedticket.bed_coordinates)
[6992]1363        self.redirect(self.url(self.context))
1364        return
1365
[6994]1366class BedTicketDisplayFormPage(WAeUPDisplayFormPage):
1367    """ Page to display bed tickets
1368    """
1369    grok.context(IBedTicket)
1370    grok.name('index')
1371    grok.require('waeup.viewStudent')
1372    form_fields = grok.AutoFields(IBedTicket)
1373    form_fields[
1374        'booking_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
1375    pnav = 4
1376
1377    @property
1378    def label(self):
1379        return 'Bed Ticket for Session %s' % self.context.getSessionString()
1380
1381    @property
1382    def title(self):
1383        return 'Bed Ticket %s' % self.context.getSessionString()
1384
[7027]1385class BedTicketSlipActionButton(ManageActionButton):
[7028]1386    grok.order(1)
[7027]1387    grok.context(IBedTicket)
[7029]1388    grok.view(BedTicketDisplayFormPage)
[7027]1389    grok.require('waeup.viewStudent')
1390    icon = 'actionicon_pdf.png'
1391    text = 'Download bed allocation slip'
1392    target = 'bed_allocation.pdf'
1393
1394class ExportPDFBedTicketSlipPage(grok.View):
1395    """Deliver a PDF slip of the context.
1396    """
1397    grok.context(IBedTicket)
1398    grok.name('bed_allocation.pdf')
1399    grok.require('waeup.viewStudent')
1400    form_fields = grok.AutoFields(IBedTicket)
1401    form_fields['booking_date'].custom_widget = FriendlyDateDisplayWidget('le')
1402    prefix = 'form'
1403
1404    @property
1405    def label(self):
1406        return 'Bed Allocation %s' % self.context.bed_coordinates
1407
1408    def render(self):
1409        studentview = StudentBaseDisplayFormPage(self.context.getStudent(),
1410            self.request)
[7150]1411        students_utils = getUtility(IStudentsUtils)
1412        return students_utils.render_pdf(
1413            self,'Bed Allocation', 'bed_allocation.pdf',
[7027]1414            self.context.getStudent, studentview)
1415
[7015]1416class RelocateStudentActionButton(ManageActionButton):
[7027]1417    grok.order(2)
[7015]1418    grok.context(IBedTicket)
1419    grok.view(BedTicketDisplayFormPage)
1420    grok.require('waeup.manageHostels')
1421    icon = 'actionicon_reload.png'
1422    text = 'Relocate student'
1423    target = 'relocate'
1424
1425class BedTicketRelocationPage(grok.View):
1426    """ Callback view
1427    """
1428    grok.context(IBedTicket)
1429    grok.name('relocate')
1430    grok.require('waeup.manageHostels')
1431
[7059]1432    # Relocate student if student parameters have changed or the bed_type
1433    # of the bed has changed
[7015]1434    def update(self):
1435        student = self.context.getStudent()
[7150]1436        students_utils = getUtility(IStudentsUtils)
1437        acc_details  = students_utils.get_accommodation_details(student)
[7068]1438        if self.context.bed != None and \
1439              'reserved' in self.context.bed.bed_type:
1440            self.flash("Students in reserved beds can't be relocated.")
1441            self.redirect(self.url(self.context))
1442            return
[7059]1443        if acc_details['bt'] == self.context.bed_type and \
[7068]1444                self.context.bed != None and \
[7059]1445                self.context.bed.bed_type == self.context.bed_type:
[7068]1446            self.flash("Student can't be relocated.")
1447            self.redirect(self.url(self.context))
[7015]1448            return
[7068]1449        # Search a bed
[7015]1450        cat = queryUtility(ICatalog, name='beds_catalog', default=None)
1451        entries = cat.searchResults(
[7068]1452            owner=(student.student_id,student.student_id))
1453        if len(entries) and self.context.bed == None:
1454            # If booking has been cancelled but other bed space has been
1455            # manually allocated after cancellation use this bed
1456            new_bed = [entry for entry in entries][0]
1457        else:
1458            # Search for other available beds
1459            entries = cat.searchResults(
1460                bed_type=(acc_details['bt'],acc_details['bt']))
1461            available_beds = [
1462                entry for entry in entries if entry.owner == NOT_OCCUPIED]
1463            if available_beds:
[7150]1464                students_utils = getUtility(IStudentsUtils)
1465                new_bed = students_utils.select_bed(available_beds)
[7068]1466                new_bed.bookBed(student.student_id)
1467            else:
1468                self.flash('There is no free bed in your category %s.'
1469                            % acc_details['bt'])
1470                self.redirect(self.url(self.context))
1471                return
1472        # Rlease old bed if exists
1473        if self.context.bed != None:
1474            self.context.bed.owner = NOT_OCCUPIED
1475            notify(grok.ObjectModifiedEvent(self.context.bed))
[7015]1476        # Alocate new bed
1477        self.context.bed_type = acc_details['bt']
[7068]1478        self.context.bed = new_bed
1479        hall_title = new_bed.__parent__.hostel_name
1480        coordinates = new_bed.getBedCoordinates()[1:]
[7015]1481        block, room_nr, bed_nr = coordinates
[7068]1482        self.context.bed_coordinates = '%s, Block %s, Room %s, Bed %s (%s)' % (
1483            hall_title, block, room_nr, bed_nr, new_bed.bed_type)
1484        self.flash('Student relocated: %s' % self.context.bed_coordinates)
[7015]1485        self.redirect(self.url(self.context))
1486        return
1487
1488    def render(self):
[7068]1489        #self.redirect(self.url(self.context, '@@index'))
[7015]1490        return
1491
[6637]1492class StudentHistoryPage(WAeUPPage):
1493    """ Page to display student clearance data
1494    """
1495    grok.context(IStudent)
1496    grok.name('history')
[6660]1497    grok.require('waeup.viewStudent')
[6637]1498    grok.template('studenthistory')
[6642]1499    title = 'History'
1500    pnav = 4
[6637]1501
1502    @property
1503    def label(self):
[6818]1504        return '%s: History' % self.context.fullname
[6694]1505
1506# Pages for students only
1507
[7133]1508class StudentBaseActionButton(ManageActionButton):
[6694]1509    grok.order(1)
1510    grok.context(IStudent)
1511    grok.view(StudentBaseDisplayFormPage)
1512    grok.require('waeup.handleStudent')
[7133]1513    text = 'Edit base data'
1514    target = 'edit_base'
1515
1516class StudentPasswordActionButton(ManageActionButton):
1517    grok.order(2)
1518    grok.context(IStudent)
1519    grok.view(StudentBaseDisplayFormPage)
1520    grok.require('waeup.handleStudent')
[7114]1521    icon = 'actionicon_key.png'
[6694]1522    text = 'Change password'
[7114]1523    target = 'change_password'
[6694]1524
[7114]1525class StudentPassportActionButton(ManageActionButton):
[7133]1526    grok.order(3)
[7114]1527    grok.context(IStudent)
1528    grok.view(StudentBaseDisplayFormPage)
1529    grok.require('waeup.handleStudent')
1530    icon = 'actionicon_portrait.png'
1531    text = 'Change portrait'
1532    target = 'change_portrait'
1533
[7133]1534    @property
1535    def target_url(self):
1536        if self.context.state != 'admitted':
1537            return ''
1538        return self.view.url(self.view.context, self.target)
1539
1540class StudentBaseEditFormPage(WAeUPEditFormPage):
1541    """ View to edit student base data
1542    """
1543    grok.context(IStudent)
1544    grok.name('edit_base')
1545    grok.require('waeup.handleStudent')
1546    form_fields = grok.AutoFields(IStudentBase).select(
1547        'email', 'phone')
1548    label = 'Edit base data'
1549    title = 'Base Data'
1550    pnav = 4
1551
1552    @grok.action('Save')
1553    def save(self, **data):
1554        msave(self, **data)
1555        return
1556
[7144]1557class StudentChangePasswordPage(WAeUPEditFormPage):
1558    """ View to manage student base data
[6756]1559    """
1560    grok.context(IStudent)
[7114]1561    grok.name('change_password')
[6694]1562    grok.require('waeup.handleStudent')
[7144]1563    grok.template('change_password')
[6694]1564    label = 'Change password'
1565    title = 'Base Data'
1566    pnav = 4
1567
[7144]1568    @grok.action('Save')
1569    def save(self, **data):
1570        form = self.request.form
1571        password = form.get('change_password', None)
1572        password_ctl = form.get('change_password_repeat', None)
1573        if password:
[7147]1574            validator = getUtility(IPasswordValidator)
1575            errors = validator.validate_password(password, password_ctl)
1576            if not errors:
1577                IUserAccount(self.context).setPassword(password)
1578                write_log_message(self, 'saved: password')
1579                self.flash('Password changed.')
[6756]1580            else:
[7147]1581                self.flash( ' '.join(errors))
[6756]1582        return
1583
[7114]1584class StudentFilesUploadPage(WAeUPPage):
1585    """ View to upload files by student
1586    """
1587    grok.context(IStudent)
1588    grok.name('change_portrait')
[7127]1589    grok.require('waeup.uploadStudentFile')
[7114]1590    grok.template('filesuploadpage')
1591    label = 'Upload portrait'
1592    title = 'Base Data'
1593    pnav = 4
1594
[7133]1595    def update(self):
1596        if self.context.getStudent().state != 'admitted':
[7145]1597            emit_lock_message(self)
[7133]1598            return
1599        super(StudentFilesUploadPage, self).update()
1600        return
1601
[6719]1602class StudentClearanceStartActionButton(ManageActionButton):
1603    grok.order(1)
1604    grok.context(IStudent)
1605    grok.view(StudentClearanceDisplayFormPage)
1606    grok.require('waeup.handleStudent')
[7116]1607    icon = 'actionicon_start.gif'
[6719]1608    text = 'Start clearance'
1609    target = 'start_clearance'
1610
1611    @property
1612    def target_url(self):
1613        if self.context.state != 'admitted':
1614            return ''
1615        return self.view.url(self.view.context, self.target)
1616
[6773]1617class StartClearancePage(WAeUPPage):
[6770]1618    grok.context(IStudent)
1619    grok.name('start_clearance')
1620    grok.require('waeup.handleStudent')
1621    grok.template('enterpin')
1622    title = 'Start clearance'
1623    label = 'Start clearance'
1624    ac_prefix = 'CLR'
1625    notice = ''
1626    pnav = 4
1627    buttonname = 'Start clearance now'
1628
[7133]1629    @property
1630    def all_required_fields_filled(self):
1631        if self.context.email and self.context.phone:
1632            return True
1633        return False
1634
1635    @property
1636    def portrait_uploaded(self):
1637        store = getUtility(IExtFileStore)
1638        if store.getFileByContext(self.context, attr=u'passport.jpg'):
1639            return True
1640        return False
1641
[6770]1642    def update(self, SUBMIT=None):
[6936]1643        if not self.context.state == 'admitted':
1644            self.flash("Wrong state.")
1645            self.redirect(self.url(self.context))
1646            return
[7133]1647        if not self.portrait_uploaded:
1648            self.flash("No portrait uploaded.")
1649            self.redirect(self.url(self.context, 'change_portrait'))
1650            return
1651        if not self.all_required_fields_filled:
1652            self.flash("Not all required fields filled.")
1653            self.redirect(self.url(self.context, 'edit_base'))
1654            return
[6770]1655        self.ac_series = self.request.form.get('ac_series', None)
1656        self.ac_number = self.request.form.get('ac_number', None)
1657
1658        if SUBMIT is None:
1659            return
1660        pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
1661        code = get_access_code(pin)
1662        if not code:
[6936]1663            self.flash('Activation code is invalid.')
[6770]1664            return
1665        # Mark pin as used (this also fires a pin related transition)
1666        # and fire transition start_clearance
1667        if code.state == USED:
[6936]1668            self.flash('Activation code has already been used.')
[6770]1669            return
1670        else:
1671            comment = u"AC invalidated for %s" % self.context.student_id
1672            # Here we know that the ac is in state initialized so we do not
[6927]1673            # expect an exception, but the owner might be different
1674            if not invalidate_accesscode(pin,comment,self.context.student_id):
1675                self.flash('You are not the owner of this access code.')
1676                return
[6770]1677            self.context.clr_code = pin
1678        IWorkflowInfo(self.context).fireTransition('start_clearance')
[6771]1679        self.flash('Clearance process has been started.')
[6770]1680        self.redirect(self.url(self.context,'cedit'))
1681        return
1682
[6695]1683class StudentClearanceEditActionButton(ManageActionButton):
1684    grok.order(1)
1685    grok.context(IStudent)
1686    grok.view(StudentClearanceDisplayFormPage)
1687    grok.require('waeup.handleStudent')
[6722]1688    text = 'Edit'
[6695]1689    target = 'cedit'
1690
[6717]1691    @property
1692    def target_url(self):
1693        if self.context.clearance_locked:
1694            return ''
1695        return self.view.url(self.view.context, self.target)
1696
[6695]1697class StudentClearanceEditFormPage(StudentClearanceManageFormPage):
1698    """ View to edit student clearance data by student
1699    """
1700    grok.context(IStudent)
1701    grok.name('cedit')
1702    grok.require('waeup.handleStudent')
[6756]1703    form_fields = grok.AutoFields(
1704        IStudentClearanceEdit).omit('clearance_locked')
[6722]1705    label = 'Edit clearance data'
[6695]1706    title = 'Clearance Data'
1707    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
[6718]1708
1709    def update(self):
1710        if self.context.clearance_locked:
[7145]1711            emit_lock_message(self)
[6718]1712            return
1713        return super(StudentClearanceEditFormPage, self).update()
[6719]1714
[6722]1715    @grok.action('Save')
1716    def save(self, **data):
1717        self.applyData(self.context, **data)
[6771]1718        self.flash('Clearance form has been saved.')
[6722]1719        return
1720
1721    @grok.action('Save and request clearance')
1722    def requestclearance(self, **data):
1723        self.applyData(self.context, **data)
1724        self.context._p_changed = True
1725        #if self.dataNotComplete():
1726        #    self.flash(self.dataNotComplete())
1727        #    return
[6771]1728        self.flash('Clearance form has been saved.')
[6769]1729        self.redirect(self.url(self.context,'request_clearance'))
[6722]1730        return
1731
[6773]1732class RequestClearancePage(WAeUPPage):
[6769]1733    grok.context(IStudent)
1734    grok.name('request_clearance')
1735    grok.require('waeup.handleStudent')
1736    grok.template('enterpin')
1737    title = 'Request clearance'
1738    label = 'Request clearance'
1739    notice = 'Enter the CLR access code used for starting clearance.'
1740    ac_prefix = 'CLR'
1741    pnav = 4
1742    buttonname = 'Request clearance now'
1743
1744    def update(self, SUBMIT=None):
1745        self.ac_series = self.request.form.get('ac_series', None)
1746        self.ac_number = self.request.form.get('ac_number', None)
1747        if SUBMIT is None:
1748            return
1749        pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
1750        if self.context.clr_code != pin:
1751            self.flash("This isn't your CLR access code.")
1752            return
1753        state = IWorkflowState(self.context).getState()
1754        # This shouldn't happen, but the application officer
1755        # might have forgotten to lock the form after changing the state
1756        if state != CLEARANCE:
1757            self.flash('This form cannot be submitted. Wrong state!')
1758            return
1759        IWorkflowInfo(self.context).fireTransition('request_clearance')
1760        self.flash('Clearance has been requested.')
1761        self.redirect(self.url(self.context))
[6789]1762        return
[6806]1763
[6944]1764class CourseRegistrationStartActionButton(ManageActionButton):
1765    grok.order(1)
1766    grok.context(IStudentStudyCourse)
1767    grok.view(StudyCourseDisplayFormPage)
1768    grok.require('waeup.handleStudent')
[7116]1769    icon = 'actionicon_start.gif'
[6944]1770    text = 'Start course registration'
1771    target = 'start_course_registration'
1772
1773    @property
1774    def target_url(self):
1775        if not self.context.getStudent().state in (CLEARED,RETURNING):
1776            return ''
1777        return self.view.url(self.view.context, self.target)
1778
1779class StartCourseRegistrationPage(WAeUPPage):
1780    grok.context(IStudentStudyCourse)
1781    grok.name('start_course_registration')
1782    grok.require('waeup.handleStudent')
1783    grok.template('enterpin')
1784    title = 'Start course registration'
1785    label = 'Start course registration'
1786    ac_prefix = 'SFE'
1787    notice = ''
1788    pnav = 4
1789    buttonname = 'Start course registration now'
1790
1791    def update(self, SUBMIT=None):
1792        if not self.context.getStudent().state in (CLEARED,RETURNING):
1793            self.flash("Wrong state.")
1794            self.redirect(self.url(self.context))
1795            return
1796        self.ac_series = self.request.form.get('ac_series', None)
1797        self.ac_number = self.request.form.get('ac_number', None)
1798
1799        if SUBMIT is None:
1800            return
1801        pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
1802        code = get_access_code(pin)
1803        if not code:
1804            self.flash('Activation code is invalid.')
1805            return
1806        # Mark pin as used (this also fires a pin related transition)
1807        # and fire transition start_clearance
1808        if code.state == USED:
1809            self.flash('Activation code has already been used.')
1810            return
1811        else:
1812            comment = u"AC invalidated for %s" % self.context.getStudent().student_id
1813            # Here we know that the ac is in state initialized so we do not
1814            # expect an exception, but the owner might be different
1815            if not invalidate_accesscode(
1816                pin,comment,self.context.getStudent().student_id):
1817                self.flash('You are not the owner of this access code.')
1818                return
1819        if self.context.getStudent().state == CLEARED:
1820            IWorkflowInfo(self.context.getStudent()).fireTransition(
1821                'pay_first_school_fee')
1822        elif self.context.getStudent().state == RETURNING:
1823            IWorkflowInfo(self.context.getStudent()).fireTransition(
1824                'pay_school_fee')
1825        self.flash('Course registration has been started.')
1826        self.redirect(self.url(self.context))
1827        return
1828
1829
[6808]1830class AddStudyLevelActionButton(AddActionButton):
1831    grok.order(1)
1832    grok.context(IStudentStudyCourse)
1833    grok.view(StudyCourseDisplayFormPage)
1834    grok.require('waeup.handleStudent')
1835    text = 'Add course list'
1836    target = 'add'
1837
1838    @property
1839    def target_url(self):
1840        student = self.view.context.getStudent()
1841        condition1 = student.state != 'school fee paid'
1842        condition2 = str(student['studycourse'].current_level) in \
1843            self.view.context.keys()
1844        if condition1 or condition2:
1845            return ''
1846        return self.view.url(self.view.context, self.target)
1847
[6806]1848class AddStudyLevelFormPage(WAeUPEditFormPage):
1849    """ Page for students to add current study levels
1850    """
1851    grok.context(IStudentStudyCourse)
1852    grok.name('add')
1853    grok.require('waeup.handleStudent')
1854    grok.template('studyleveladdpage')
1855    form_fields = grok.AutoFields(IStudentStudyCourse)
1856    title = 'Study Course'
1857    pnav = 4
1858
1859    @property
1860    def label(self):
1861        studylevelsource = StudyLevelSource().factory
1862        code = self.context.current_level
1863        title = studylevelsource.getTitle(self.context, code)
1864        return 'Add current level %s' % title
1865
1866    def update(self):
1867        if self.context.getStudent().state != 'school fee paid':
[7145]1868            emit_lock_message(self)
[6806]1869            return
1870        super(AddStudyLevelFormPage, self).update()
1871        return
1872
1873    @grok.action('Create course list now')
1874    def addStudyLevel(self, **data):
1875        studylevel = StudentStudyLevel()
1876        studylevel.level = self.context.current_level
1877        studylevel.level_session = self.context.current_session
1878        try:
1879            self.context.addStudentStudyLevel(
1880                self.context.certificate,studylevel)
1881        except KeyError:
1882            self.flash('This level exists.')
1883        self.redirect(self.url(self.context))
1884        return
[6808]1885
1886class StudyLevelEditActionButton(ManageActionButton):
1887    grok.order(1)
1888    grok.context(IStudentStudyLevel)
1889    grok.view(StudyLevelDisplayFormPage)
1890    grok.require('waeup.handleStudent')
1891    text = 'Add and remove courses'
1892    target = 'edit'
1893
1894    @property
1895    def target_url(self):
1896        student = self.view.context.getStudent()
1897        condition1 = student.state != 'school fee paid'
1898        condition2 = student[
1899            'studycourse'].current_level != self.view.context.level
1900        if condition1 or condition2:
1901            return ''
1902        return self.view.url(self.view.context, self.target)
1903
1904class StudyLevelEditFormPage(WAeUPEditFormPage):
1905    """ Page to edit the student study level data by students
1906    """
1907    grok.context(IStudentStudyLevel)
1908    grok.name('edit')
1909    grok.require('waeup.handleStudent')
1910    grok.template('studyleveleditpage')
1911    form_fields = grok.AutoFields(IStudentStudyLevel).omit(
1912        'level_session', 'level_verdict')
1913    pnav = 4
1914
1915    def update(self):
1916        super(StudyLevelEditFormPage, self).update()
1917    #    tabs.need()
1918        datatable.need()
1919        return
1920
1921    @property
1922    def title(self):
1923        return 'Study Level %s' % self.context.level_title
1924
1925    @property
1926    def label(self):
1927        return 'Add and remove course tickets of study level %s' % self.context.level_title
1928
1929    @property
1930    def total_credits(self):
1931        total_credits = 0
1932        for key, val in self.context.items():
1933            total_credits += val.credits
1934        return total_credits
1935
1936    @grok.action('Add course ticket')
1937    def addCourseTicket(self, **data):
1938        self.redirect(self.url(self.context, 'ctadd'))
1939
1940    @grok.action('Remove selected tickets')
1941    def delCourseTicket(self, **data):
1942        form = self.request.form
1943        if form.has_key('val_id'):
1944            child_id = form['val_id']
1945        else:
1946            self.flash('No ticket selected.')
1947            self.redirect(self.url(self.context, '@@edit'))
1948            return
1949        if not isinstance(child_id, list):
1950            child_id = [child_id]
1951        deleted = []
1952        for id in child_id:
[6940]1953            # Students are not allowed to remove core tickets
[6808]1954            if not self.context[id].core_or_elective:
1955                try:
1956                    del self.context[id]
1957                    deleted.append(id)
1958                except:
1959                    self.flash('Could not delete %s: %s: %s' % (
1960                            id, sys.exc_info()[0], sys.exc_info()[1]))
1961        if len(deleted):
1962            self.flash('Successfully removed: %s' % ', '.join(deleted))
1963        self.redirect(self.url(self.context, u'@@edit'))
1964        return
1965
[6810]1966    @grok.action('Register course list')
1967    def register_courses(self, **data):
1968        state = IWorkflowState(self.context.getStudent()).getState()
1969        IWorkflowInfo(self.context.getStudent()).fireTransition('register_courses')
1970        self.flash('Course list has been registered.')
1971        self.redirect(self.url(self.context))
1972        return
1973
[6808]1974class CourseTicketAddFormPage2(CourseTicketAddFormPage):
1975    """Add a course ticket by student.
1976    """
1977    grok.name('ctadd')
1978    grok.require('waeup.handleStudent')
1979    form_fields = grok.AutoFields(ICourseTicketAdd).omit(
1980        'grade', 'score', 'core_or_elective', 'automatic')
1981
1982    @grok.action('Add course ticket')
1983    def addCourseTicket(self, **data):
1984        ticket = CourseTicket()
1985        course = data['course']
1986        ticket.automatic = False
1987        ticket.code = course.code
1988        ticket.title = course.title
1989        ticket.faculty = course.__parent__.__parent__.__parent__.title
1990        ticket.department = course.__parent__.__parent__.title
1991        ticket.credits = course.credits
1992        ticket.passmark = course.passmark
1993        ticket.semester = course.semester
1994        try:
1995            self.context.addCourseTicket(ticket)
1996        except KeyError:
1997            self.flash('The ticket exists.')
1998            return
1999        self.flash('Successfully added %s.' % ticket.code)
2000        self.redirect(self.url(self.context, u'@@edit'))
2001        return
Note: See TracBrowser for help on using the repository browser.