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

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

academics: Show students in departments.

students: Search for students in department.

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