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

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

Implement StartCourseRegistrationPage? (tests will follow).

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