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

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

Remove maintenance fee attributes. We don't need them in bed tickets.

Add BedTicketDisplayFormPage?.

getPaymentDetails and getAccommodationDetails: Return dictionary instead of tuple.

Callback now provides a HOS pin when applied to IStudentOnlinePayment instances.

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