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

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

Extend ISessionConfiguration and fix utils.py.

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