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

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

Rename functions according to the WAeUP style guide:

functions and methods with property decorator with underscore

methods with CamelCase

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