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

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

Add display and manage pages for course tickets.

  • Property svn:keywords set to Id
File size: 32.5 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
19
20from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
[6760]21from zope.component import createObject
[6621]22from waeup.sirp.accesscodes import invalidate_accesscode, get_access_code
23from waeup.sirp.accesscodes.workflow import USED
24from waeup.sirp.browser import (
25    WAeUPPage, WAeUPEditFormPage, WAeUPAddFormPage, WAeUPDisplayFormPage)
26from waeup.sirp.browser.breadcrumbs import Breadcrumb
[6775]27from waeup.sirp.browser.resources import datepicker, datatable, tabs
[6621]28from waeup.sirp.browser.viewlets import (
[6760]29    ManageActionButton, PrimaryNavTab, AddActionButton)
30from waeup.sirp.interfaces import IWAeUPObject, IUserAccount
[6621]31from waeup.sirp.widgets.datewidget import (
32    FriendlyDateWidget, FriendlyDateDisplayWidget)
33from waeup.sirp.students.interfaces import (
[6756]34    IStudentsContainer, IStudent, IStudentClearance, IStudentPasswordSetting,
[6760]35    IStudentPersonal, IStudentBase, IStudentStudyCourse, IStudentPayments,
[6774]36    IStudentAccommodation, IStudentClearanceEdit, IStudentStudyLevel,
[6795]37    ICourseTicket, ICourseTicketAdd,
[6621]38    )
[6626]39from waeup.sirp.students.catalog import search
[6722]40from waeup.sirp.students.workflow import CLEARANCE
[6795]41from waeup.sirp.students.studylevel import StudentStudyLevel, CourseTicket
[6775]42from waeup.sirp.students.vocabularies import StudyLevelSource
[6621]43
[6762]44# Save function used for save methods in manager pages
45def msave(view, **data):
46    form = view.request.form
47    ob_class = view.__implemented__.__name__.replace('waeup.sirp.','')
48    changed_fields = view.applyData(view.context, **data)
[6771]49    # Turn list of lists into single list
50    if changed_fields:
51        changed_fields = reduce(lambda x,y: x+y, changed_fields.values())
52    fields_string = ' + '.join(changed_fields)
[6762]53    view.context._p_changed = True
54    view.flash('Form has been saved.')
55    if fields_string:
[6794]56        view.context.getStudent().loggerInfo(ob_class, 'saved: % s' % fields_string)
[6762]57    return
58
[6621]59class StudentsTab(PrimaryNavTab):
60    """Students tab in primary navigation.
61    """
62
63    grok.context(IWAeUPObject)
64    grok.order(3)
[6660]65    grok.require('waeup.viewStudent')
[6621]66    grok.template('primarynavtab')
67
68    pnav = 4
69    tab_title = u'Students'
70
71    @property
72    def link_target(self):
73        return self.view.application_url('students')
74
[6629]75class StudentsBreadcrumb(Breadcrumb):
76    """A breadcrumb for the students container.
77    """
78    grok.context(IStudentsContainer)
79    title = u'Students'
80
[6635]81class SudyCourseBreadcrumb(Breadcrumb):
82    """A breadcrumb for the student study course.
83    """
84    grok.context(IStudentStudyCourse)
85    title = u'Study Course'
86
87class PaymentsBreadcrumb(Breadcrumb):
88    """A breadcrumb for the student payments folder.
89    """
90    grok.context(IStudentPayments)
91    title = u'Payments'
92
93class AccommodationBreadcrumb(Breadcrumb):
94    """A breadcrumb for the student accommodation folder.
95    """
96    grok.context(IStudentAccommodation)
97    title = u'Accommodation'
98
[6776]99class StudyLevelBreadcrumb(Breadcrumb):
100    """A breadcrumb for course lists.
101    """
102    grok.context(IStudentStudyLevel)
103
104    @property
105    def title(self):
106        return self.context.level_title
107
[6626]108class StudentsContainerPage(WAeUPPage):
109    """The standard view for student containers.
[6621]110    """
111    grok.context(IStudentsContainer)
112    grok.name('index')
[6660]113    grok.require('waeup.viewStudent')
[6695]114    grok.template('containerpage')
[6654]115    label = 'Student Section'
116    title = 'Students'
[6642]117    pnav = 4
[6621]118
[6626]119    def update(self, *args, **kw):
120        datatable.need()
121        form = self.request.form
122        self.hitlist = []
123        if 'searchterm' in form and form['searchterm']:
124            self.searchterm = form['searchterm']
125            self.searchtype = form['searchtype']
126        elif 'old_searchterm' in form:
127            self.searchterm = form['old_searchterm']
128            self.searchtype = form['old_searchtype']
129        else:
130            if 'search' in form:
131                self.flash('Empty search string.')
132            return
133        self.hitlist = search(query=self.searchterm,
134            searchtype=self.searchtype, view=self)
135        if not self.hitlist:
136            self.flash('No student found.')
137        return
138
[6773]139class SetPasswordPage(WAeUPPage):
[6699]140    grok.context(IWAeUPObject)
141    grok.name('setpassword')
142    grok.require('waeup.Public')
[6774]143    grok.template('setpassword')
[6699]144    title = ''
145    label = 'Set password for first-time login'
[6758]146    ac_prefix = 'PWD'
[6715]147    pnav = 0
[6699]148
149    def update(self, SUBMIT=None):
[6758]150        self.reg_number = self.request.form.get('reg_number', None)
151        self.ac_series = self.request.form.get('ac_series', None)
152        self.ac_number = self.request.form.get('ac_number', None)
[6756]153
[6699]154        if SUBMIT is None:
155            return
156        hitlist = search(query=self.reg_number,
157            searchtype='reg_number', view=self)
158        if not hitlist:
159            self.flash('No student found.')
160            return
161        if len(hitlist) != 1:   # Cannot happen but anyway
162            self.flash('More than one student found.')
163            return
[6704]164        student = hitlist[0].context
165        self.student_id = student.student_id
166        student_pw = student.password
[6758]167        pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
[6699]168        code = get_access_code(pin)
169        if not code:
170            self.flash('Access code is invalid.')
171            return
[6704]172        if student_pw and pin == student.adm_code:
173            self.flash('Password has already been set. Your Student Id is %s'
174                % self.student_id)
175            return
176        elif student_pw:
177            self.flash('Password has already been set.')
178            return
[6699]179        # Mark pin as used (this also fires a pin related transition)
180        # and set student password
181        if code.state == USED:
182            self.flash('Access code has already been used.')
183            return
184        else:
[6704]185            comment = u"AC invalidated for %s" % self.student_id
[6699]186            # Here we know that the ac is in state initialized so we do not
187            # expect an exception
188            #import pdb; pdb.set_trace()
189            invalidate_accesscode(pin,comment)
[6758]190            IUserAccount(student).setPassword(self.ac_number)
[6769]191            student.adm_code = pin
[6704]192        self.flash('Password has been set. Your Student Id is %s'
193            % self.student_id)
[6699]194        return
195
[6626]196class StudentsContainerManageActionButton(ManageActionButton):
[6622]197    grok.order(1)
198    grok.context(IStudentsContainer)
199    grok.view(StudentsContainerPage)
200    grok.require('waeup.manageStudents')
[6647]201    text = 'Manage student section'
[6622]202
[6626]203
204class StudentsContainerManagePage(WAeUPPage):
205    """The manage page for student containers.
[6622]206    """
207    grok.context(IStudentsContainer)
208    grok.name('manage')
209    grok.require('waeup.manageStudents')
[6695]210    grok.template('containermanagepage')
[6642]211    pnav = 4
[6647]212    title = 'Manage student section'
[6622]213
214    @property
215    def label(self):
216        return self.title
217
[6626]218    def update(self, *args, **kw):
219        datatable.need()
220        form = self.request.form
221        self.hitlist = []
222        if 'searchterm' in form and form['searchterm']:
223            self.searchterm = form['searchterm']
224            self.searchtype = form['searchtype']
225        elif 'old_searchterm' in form:
226            self.searchterm = form['old_searchterm']
227            self.searchtype = form['old_searchtype']
228        else:
229            if 'search' in form:
230                self.flash('Empty search string.')
231            return
232        if not 'entries' in form:
233            self.hitlist = search(query=self.searchterm,
234                searchtype=self.searchtype, view=self)
235            if not self.hitlist:
236                self.flash('No student found.')
237            return
238        entries = form['entries']
239        if isinstance(entries, basestring):
240            entries = [entries]
241        deleted = []
242        for entry in entries:
243            if 'remove' in form:
244                del self.context[entry]
245                deleted.append(entry)
246        self.hitlist = search(query=self.searchterm,
247            searchtype=self.searchtype, view=self)
248        if len(deleted):
249            self.flash('Successfully removed: %s' % ', '.join(deleted))
[6622]250        return
251
[6626]252class StudentsContainerAddActionButton(AddActionButton):
253    grok.order(1)
254    grok.context(IStudentsContainer)
255    grok.view(StudentsContainerManagePage)
256    grok.require('waeup.manageStudents')
257    text = 'Add student'
258    target = 'addstudent'
259
[6622]260class StudentAddFormPage(WAeUPAddFormPage):
261    """Add-form to add a student.
262    """
263    grok.context(IStudentsContainer)
264    grok.require('waeup.manageStudents')
265    grok.name('addstudent')
266    grok.template('studentaddpage')
267    form_fields = grok.AutoFields(IStudent)
268    title = 'Students'
269    label = 'Add student'
[6642]270    pnav = 4
[6622]271
272    @grok.action('Create student record')
273    def addStudent(self, **data):
274        student = createObject(u'waeup.Student')
275        self.applyData(student, **data)
[6652]276        self.context.addStudent(student)
[6626]277        self.flash('Student record created.')
[6651]278        self.redirect(self.url(self.context[student.student_id], 'index'))
[6622]279        return
280
[6631]281class StudentBaseDisplayFormPage(WAeUPDisplayFormPage):
282    """ Page to display student base data
283    """
[6622]284    grok.context(IStudent)
285    grok.name('index')
[6660]286    grok.require('waeup.viewStudent')
[6695]287    grok.template('basepage')
[6756]288    form_fields = grok.AutoFields(IStudentBase).omit('password')
[6642]289    pnav = 4
290    title = 'Base Data'
[6622]291
292    @property
293    def label(self):
[6631]294        return '%s: Base Data' % self.context.name
295
[6699]296    @property
297    def hasPassword(self):
298        if self.context.password:
299            return 'set'
300        return 'unset'
301
[6631]302class StudentBaseManageActionButton(ManageActionButton):
303    grok.order(1)
304    grok.context(IStudent)
305    grok.view(StudentBaseDisplayFormPage)
306    grok.require('waeup.manageStudents')
[6695]307    text = 'Manage'
[6631]308    target = 'edit_base'
309
310class StudentBaseManageFormPage(WAeUPEditFormPage):
311    """ View to edit student base data
312    """
313    grok.context(IStudent)
314    grok.name('edit_base')
315    grok.require('waeup.manageStudents')
316    form_fields = grok.AutoFields(IStudentBase).omit('student_id')
[6695]317    grok.template('basemanagepage')
318    label = 'Manage base data'
[6642]319    title = 'Base Data'
320    pnav = 4
[6631]321
[6638]322    def update(self):
323        datepicker.need() # Enable jQuery datepicker in date fields.
324        super(StudentBaseManageFormPage, self).update()
325        self.wf_info = IWorkflowInfo(self.context)
326        return
327
328    def getTransitions(self):
329        """Return a list of dicts of allowed transition ids and titles.
330
331        Each list entry provides keys ``name`` and ``title`` for
332        internal name and (human readable) title of a single
333        transition.
334        """
335        allowed_transitions = self.wf_info.getManualTransitions()
336        return [dict(name='', title='No transition')] +[
337            dict(name=x, title=y) for x, y in allowed_transitions]
338
339    @grok.action('Save')
340    def save(self, **data):
[6701]341        form = self.request.form
342        ob_class = self.__implemented__.__name__.replace('waeup.sirp.','')
[6790]343        password = form.get('password', None)
344        password_ctl = form.get('control_password', None)
345        if password:
346            if (password != password_ctl):
[6701]347                self.flash('Passwords do not match.')
[6790]348            else:
349                # XXX: This is too early. PW should only be saved if there
350                #      are no (other) errors left in form.
351                IUserAccount(self.context).setPassword(password)
352                self.context.loggerInfo(ob_class, 'password changed')
353
[6789]354        #self.reg_number = form.get('form.reg_number', None)
355        #if self.reg_number:
356        #    hitlist = search(query=self.reg_number,searchtype='reg_number', view=self)
357        #    if hitlist and hitlist[0].student_id != self.context.student_id:
358        #        self.flash('Registration number exists.')
359        #        return
360        #self.matric_number = form.get('form.matric_number', None)
361        #if self.matric_number:
362        #    hitlist = search(query=self.matric_number,
363        #        searchtype='matric_number', view=self)
364        #    if hitlist and hitlist[0].student_id != self.context.student_id:
365        #        self.flash('Matriculation number exists.')
366        #        return
367
[6771]368        # Turn list of lists into single list
[6638]369        changed_fields = self.applyData(self.context, **data)
[6771]370        if changed_fields:
371            changed_fields = reduce(lambda x,y: x+y, changed_fields.values())
372        fields_string = ' + '.join(changed_fields)
[6638]373        self.context._p_changed = True
374        if form.has_key('transition') and form['transition']:
375            transition_id = form['transition']
376            self.wf_info.fireTransition(transition_id)
377        self.flash('Form has been saved.')
[6644]378        if fields_string:
379            self.context.loggerInfo(ob_class, 'saved: % s' % fields_string)
[6638]380        return
381
[6631]382class StudentClearanceDisplayFormPage(WAeUPDisplayFormPage):
383    """ Page to display student clearance data
384    """
385    grok.context(IStudent)
386    grok.name('view_clearance')
[6660]387    grok.require('waeup.viewStudent')
[6695]388    form_fields = grok.AutoFields(IStudentClearance).omit('clearance_locked')
[6650]389    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
[6642]390    title = 'Clearance Data'
391    pnav = 4
[6631]392
393    @property
394    def label(self):
395        return '%s: Clearance Data' % self.context.name
396
397class StudentClearanceManageActionButton(ManageActionButton):
398    grok.order(1)
399    grok.context(IStudent)
400    grok.view(StudentClearanceDisplayFormPage)
401    grok.require('waeup.manageStudents')
[6695]402    text = 'Manage'
[6631]403    target = 'edit_clearance'
404
405class StudentClearanceManageFormPage(WAeUPEditFormPage):
406    """ Page to edit student clearance data
407    """
408    grok.context(IStudent)
409    grok.name('edit_clearance')
[6649]410    grok.require('waeup.manageStudents')
[6631]411    form_fields = grok.AutoFields(IStudentClearance)
[6695]412    label = 'Manage clearance data'
[6642]413    title = 'Clearance Data'
414    pnav = 4
[6631]415
[6650]416    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
417
418    def update(self):
419        datepicker.need() # Enable jQuery datepicker in date fields.
420        return super(StudentClearanceManageFormPage, self).update()
421
[6695]422    @grok.action('Save')
423    def save(self, **data):
[6762]424        msave(self, **data)
[6695]425        return
426
[6631]427class StudentPersonalDisplayFormPage(WAeUPDisplayFormPage):
428    """ Page to display student personal data
429    """
430    grok.context(IStudent)
431    grok.name('view_personal')
[6660]432    grok.require('waeup.viewStudent')
[6631]433    form_fields = grok.AutoFields(IStudentPersonal)
[6642]434    title = 'Personal Data'
435    pnav = 4
[6631]436
437    @property
438    def label(self):
439        return '%s: Personal Data' % self.context.name
440
441class StudentPersonalManageActionButton(ManageActionButton):
442    grok.order(1)
443    grok.context(IStudent)
444    grok.view(StudentPersonalDisplayFormPage)
445    grok.require('waeup.manageStudents')
[6695]446    text = 'Manage'
[6631]447    target = 'edit_personal'
448
449class StudentPersonalManageFormPage(WAeUPEditFormPage):
450    """ Page to edit student clearance data
451    """
452    grok.context(IStudent)
453    grok.name('edit_personal')
[6660]454    grok.require('waeup.viewStudent')
[6631]455    form_fields = grok.AutoFields(IStudentPersonal)
[6695]456    label = 'Manage personal data'
[6642]457    title = 'Personal Data'
458    pnav = 4
[6631]459
[6762]460    @grok.action('Save')
461    def save(self, **data):
462        msave(self, **data)
463        return
464
[6635]465class StudyCourseDisplayFormPage(WAeUPDisplayFormPage):
466    """ Page to display the student study course data
467    """
468    grok.context(IStudentStudyCourse)
469    grok.name('index')
[6660]470    grok.require('waeup.viewStudent')
[6635]471    form_fields = grok.AutoFields(IStudentStudyCourse)
[6775]472    grok.template('studycoursepage')
[6642]473    title = 'Study Course'
474    pnav = 4
[6635]475
476    @property
477    def label(self):
[6642]478        return '%s: Study Course' % self.context.__parent__.name
[6635]479
[6649]480class StudyCourseManageActionButton(ManageActionButton):
481    grok.order(1)
482    grok.context(IStudentStudyCourse)
483    grok.view(StudyCourseDisplayFormPage)
484    grok.require('waeup.manageStudents')
[6695]485    text = 'Manage'
[6775]486    target = 'manage'
[6649]487
488class StudyCourseManageFormPage(WAeUPEditFormPage):
489    """ Page to edit the student study course data
490    """
491    grok.context(IStudentStudyCourse)
[6775]492    grok.name('manage')
[6649]493    grok.require('waeup.manageStudents')
[6775]494    grok.template('studycoursemanagepage')
[6649]495    form_fields = grok.AutoFields(IStudentStudyCourse)
496    title = 'Study Course'
[6695]497    label = 'Manage study course'
[6649]498    pnav = 4
[6775]499    taboneactions = ['Save','Cancel']
500    tabtwoactions = ['Remove selected levels','Cancel']
501    tabthreeactions = ['Add study level']
[6649]502
[6775]503    def update(self):
504        super(StudyCourseManageFormPage, self).update()
505        tabs.need()
506        datatable.need()
507        return
508
[6761]509    @grok.action('Save')
510    def save(self, **data):
[6762]511        msave(self, **data)
[6761]512        return
513
[6775]514    @property
515    def level_dict(self):
516        studylevelsource = StudyLevelSource().factory
517        for code in studylevelsource.getValues(self.context):
518            title = studylevelsource.getTitle(self.context, code)
519            yield(dict(code=code, title=title))
520
521    @grok.action('Add study level')
[6774]522    def addStudyLevel(self, **data):
[6775]523        level_code = self.request.form.get('addlevel', None)
[6774]524        studylevel = StudentStudyLevel()
[6775]525        studylevel.level = int(level_code)
526        try:
[6782]527            self.context.addStudentStudyLevel(
528                self.context.certificate,studylevel)
[6775]529        except KeyError:
530            self.flash('This level exists.')
531        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
[6774]532        return
533
[6775]534    @grok.action('Remove selected levels')
535    def delStudyLevels(self, **data):
536        form = self.request.form
537        if form.has_key('val_id'):
538            child_id = form['val_id']
539        else:
540            self.flash('No study level selected.')
541            self.redirect(self.url(self.context, '@@manage')+'#tab-2')
542            return
543        if not isinstance(child_id, list):
544            child_id = [child_id]
545        deleted = []
546        for id in child_id:
547            try:
548                del self.context[id]
549                deleted.append(id)
550            except:
551                self.flash('Could not delete %s: %s: %s' % (
552                        id, sys.exc_info()[0], sys.exc_info()[1]))
553        if len(deleted):
554            self.flash('Successfully removed: %s' % ', '.join(deleted))
555        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
556        return
[6774]557
558class StudyLevelDisplayFormPage(WAeUPDisplayFormPage):
559    """ Page to display student study levels
560    """
561    grok.context(IStudentStudyLevel)
562    grok.name('index')
563    grok.require('waeup.viewStudent')
[6775]564    form_fields = grok.AutoFields(IStudentStudyLevel)
[6783]565    grok.template('studylevelpage')
[6774]566    pnav = 4
567
568    @property
[6792]569    def title(self):
570        return 'Study Level %s' % self.context.level_title
571
572    @property
[6774]573    def label(self):
[6776]574        return '%s: Study Level %s' % (
575            self.context.getStudent().name,self.context.level_title)
[6774]576
[6792]577class StudyLevelManageActionButton(ManageActionButton):
578    grok.order(1)
579    grok.context(IStudentStudyLevel)
580    grok.view(StudyLevelDisplayFormPage)
581    grok.require('waeup.manageStudents')
582    text = 'Manage'
583    target = 'manage'
584
585class StudyLevelManageFormPage(WAeUPEditFormPage):
586    """ Page to edit the student study level data
587    """
588    grok.context(IStudentStudyLevel)
589    grok.name('manage')
590    grok.require('waeup.manageStudents')
591    grok.template('studylevelmanagepage')
592    form_fields = grok.AutoFields(IStudentStudyLevel)
593    pnav = 4
594    taboneactions = ['Save','Cancel']
[6795]595    tabtwoactions = ['Add course ticket','Remove selected tickets','Cancel']
[6792]596
597    def update(self):
598        super(StudyLevelManageFormPage, self).update()
599        tabs.need()
600        datatable.need()
601        return
602
603    @property
604    def title(self):
605        return 'Study Level %s' % self.context.level_title
606
607    @property
608    def label(self):
609        return 'Manage study level %s' % self.context.level_title
610
611    @grok.action('Save')
612    def save(self, **data):
613        msave(self, **data)
614        return
615
616    @grok.action('Add course ticket')
[6795]617    def addCourseTicket(self, **data):
618        self.redirect(self.url(self.context, '@@add'))
[6792]619
620    @grok.action('Remove selected tickets')
621    def delCourseTicket(self, **data):
622        form = self.request.form
623        if form.has_key('val_id'):
624            child_id = form['val_id']
625        else:
626            self.flash('No ticket selected.')
627            self.redirect(self.url(self.context, '@@manage')+'#tab-2')
628            return
629        if not isinstance(child_id, list):
630            child_id = [child_id]
631        deleted = []
632        for id in child_id:
633            try:
634                del self.context[id]
635                deleted.append(id)
636            except:
637                self.flash('Could not delete %s: %s: %s' % (
638                        id, sys.exc_info()[0], sys.exc_info()[1]))
639        if len(deleted):
640            self.flash('Successfully removed: %s' % ', '.join(deleted))
641        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
642        return
643
[6795]644class CourseTicketAddFormPage(WAeUPAddFormPage):
645    """Add a department to a faculty.
646    """
647    grok.context(IStudentStudyLevel)
648    grok.name('add')
649    grok.require('waeup.manageStudents')
650    label = 'Add course ticket'
651    form_fields = grok.AutoFields(ICourseTicketAdd).omit('grade', 'score')
652    pnav = 4
653
654    @property
655    def title(self):
656        return 'Study Level %s' % self.context.level_title
657
658    @grok.action('Add course ticket')
659    def addCourseTicket(self, **data):
660        ticket = CourseTicket()
661        #import pdb; pdb.set_trace()
662        course = data['course']
663        ticket.core_or_elective = data['core_or_elective']
664        ticket.level = self.context.level
665        ticket.code = course.code
666        ticket.title = course.title
667        ticket.faculty = course.__parent__.__parent__.__parent__.title
668        ticket.department = course.__parent__.__parent__.title
669        ticket.credits = course.credits
670        ticket.passmark = course.passmark
671        ticket.semester = course.semester
672        try:
673            self.context.addCourseTicket(ticket)
674        except KeyError:
675            self.flash('The ticket exists.')
676            return
677        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
678        return
679
680    @grok.action('Cancel')
681    def cancel(self, **data):
682        self.redirect(self.url(self.context))
683
[6796]684class CourseTicketDisplayFormPage(WAeUPDisplayFormPage):
685    """ Page to display course tickets
686    """
687    grok.context(ICourseTicket)
688    grok.name('index')
689    grok.require('waeup.viewStudent')
690    form_fields = grok.AutoFields(ICourseTicket)
691    grok.template('courseticketpage')
692    pnav = 4
693
694    @property
695    def title(self):
696        return 'Course Ticket %s' % self.context.code
697
698    @property
699    def label(self):
700        return '%s: Course Ticket %s' % (
701            self.context.getStudent().name,self.context.code)
702
703class CourseTicketManageActionButton(ManageActionButton):
704    grok.order(1)
705    grok.context(ICourseTicket)
706    grok.view(CourseTicketDisplayFormPage)
707    grok.require('waeup.manageStudents')
708    text = 'Manage'
709    target = 'manage'
710
711class CourseTicketManageFormPage(WAeUPEditFormPage):
712    """ Page to manage course tickets
713    """
714    grok.context(ICourseTicket)
715    grok.name('manage')
716    grok.require('waeup.manageStudents')
717    form_fields = grok.AutoFields(ICourseTicket)
718    grok.template('courseticketmanagepage')
719    pnav = 4
720
721    @property
722    def title(self):
723        return 'Course Ticket %s' % self.context.code
724
725    @property
726    def label(self):
727        return 'Manage course ticket %s' % self.context.code
728
729    @grok.action('Save')
730    def save(self, **data):
731        msave(self, **data)
732        return
733
[6635]734class PaymentsDisplayFormPage(WAeUPDisplayFormPage):
735    """ Page to display the student payments
736    """
737    grok.context(IStudentPayments)
738    grok.name('index')
[6660]739    grok.require('waeup.viewStudent')
[6635]740    form_fields = grok.AutoFields(IStudentPayments)
741    #grok.template('paymentspage')
[6642]742    title = 'Payments'
743    pnav = 4
[6635]744
745    @property
746    def label(self):
747        return '%s: Payments' % self.context.__parent__.name
748
749class AccommodationDisplayFormPage(WAeUPDisplayFormPage):
750    """ Page to display the student accommodation data
751    """
752    grok.context(IStudentAccommodation)
753    grok.name('index')
[6660]754    grok.require('waeup.viewStudent')
[6635]755    form_fields = grok.AutoFields(IStudentAccommodation)
756    #grok.template('accommodationpage')
[6642]757    title = 'Accommodation'
758    pnav = 4
[6635]759
760    @property
761    def label(self):
762        return '%s: Accommodation Data' % self.context.__parent__.name
[6637]763
764class StudentHistoryPage(WAeUPPage):
765    """ Page to display student clearance data
766    """
767    grok.context(IStudent)
768    grok.name('history')
[6660]769    grok.require('waeup.viewStudent')
[6637]770    grok.template('studenthistory')
[6642]771    title = 'History'
772    pnav = 4
[6637]773
774    @property
775    def label(self):
776        return '%s: History' % self.context.name
[6694]777
778# Pages for students only
779
780class StudentBaseEditActionButton(ManageActionButton):
781    grok.order(1)
782    grok.context(IStudent)
783    grok.view(StudentBaseDisplayFormPage)
784    grok.require('waeup.handleStudent')
785    text = 'Change password'
786    target = 'bedit'
787
[6756]788class StudentPasswordSetting(grok.Adapter):
789    """Adapt IStudent to data needed for password settings.
790
791    We provide password getters/setters for the attached context (an
792    IStudent object) that cooperate seamless with the usual
793    formlib/form techniques.
794    """
795    grok.context(IStudent)
796    grok.provides(IStudentPasswordSetting)
797
798    def __init__(self, context):
799        self.name = context.name
800        self.password_repeat = context.password
801        self.context = context
802        return
803
804    def getPassword(self):
805        return self.context.password
806
807    def setPassword(self, password):
808        IUserAccount(self.context).setPassword(password)
809        return
810
811    password = property(getPassword, setPassword)
812
[6694]813class StudentBaseEditFormPage(WAeUPEditFormPage):
814    """ View to edit student base data by student
815    """
816    grok.context(IStudent)
817    grok.name('bedit')
818    grok.require('waeup.handleStudent')
[6756]819    #form_fields = grok.AutoFields(IStudentBaseEdit).omit(
820    #    'student_id', 'reg_number', 'matric_number')
821    form_fields = grok.AutoFields(IStudentPasswordSetting)
[6695]822    grok.template('baseeditpage')
[6694]823    label = 'Change password'
824    title = 'Base Data'
825    pnav = 4
826
827    def update(self):
828        super(StudentBaseEditFormPage, self).update()
829        self.wf_info = IWorkflowInfo(self.context)
830        return
831
[6756]832    def onFailure(self, action, data, errors):
833        new_status = []
834        other_errors = False
835        for error in errors:
836            msg = getattr(error, 'message', '')
837            if isinstance(msg, basestring) and msg != '':
838                new_status.append(msg)
839            else:
840                other_errors = True
841        if other_errors:
842            if new_status:
843                new_status.append('see below for further errors')
844            else:
845                new_status.append('See below for details.')
846        if new_status:
847            self.status = u'There were errors: %s' % ', '.join(new_status)
848        return
849
850    @grok.action('Save', failure=onFailure)
[6694]851    def save(self, **data):
[6771]852        self.applyData(self.context, **data)
853        self.flash('Form has been saved.')
[6694]854        return
[6695]855
[6719]856class StudentClearanceStartActionButton(ManageActionButton):
857    grok.order(1)
858    grok.context(IStudent)
859    grok.view(StudentClearanceDisplayFormPage)
860    grok.require('waeup.handleStudent')
861    icon = 'actionicon_start.png'
862    text = 'Start clearance'
863    target = 'start_clearance'
864
865    @property
866    def target_url(self):
867        if self.context.state != 'admitted':
868            return ''
869        return self.view.url(self.view.context, self.target)
870
[6773]871class StartClearancePage(WAeUPPage):
[6770]872    grok.context(IStudent)
873    grok.name('start_clearance')
874    grok.require('waeup.handleStudent')
875    grok.template('enterpin')
876    title = 'Start clearance'
877    label = 'Start clearance'
878    ac_prefix = 'CLR'
879    notice = ''
880    pnav = 4
881    buttonname = 'Start clearance now'
882
883    def update(self, SUBMIT=None):
884        self.ac_series = self.request.form.get('ac_series', None)
885        self.ac_number = self.request.form.get('ac_number', None)
886
887        if SUBMIT is None:
888            return
889        pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
890        code = get_access_code(pin)
891        if not code:
892            self.flash('Access code is invalid.')
893            return
894        # Mark pin as used (this also fires a pin related transition)
895        # and fire transition start_clearance
896        if code.state == USED:
897            self.flash('Access code has already been used.')
898            return
899        else:
900            comment = u"AC invalidated for %s" % self.context.student_id
901            # Here we know that the ac is in state initialized so we do not
902            # expect an exception
903            invalidate_accesscode(pin,comment)
904            self.context.clr_code = pin
905        IWorkflowInfo(self.context).fireTransition('start_clearance')
[6771]906        self.flash('Clearance process has been started.')
[6770]907        self.redirect(self.url(self.context,'cedit'))
908        return
909
[6695]910class StudentClearanceEditActionButton(ManageActionButton):
911    grok.order(1)
912    grok.context(IStudent)
913    grok.view(StudentClearanceDisplayFormPage)
914    grok.require('waeup.handleStudent')
[6722]915    text = 'Edit'
[6695]916    target = 'cedit'
917
[6717]918    @property
919    def target_url(self):
920        if self.context.clearance_locked:
921            return ''
922        return self.view.url(self.view.context, self.target)
923
[6695]924class StudentClearanceEditFormPage(StudentClearanceManageFormPage):
925    """ View to edit student clearance data by student
926    """
927    grok.context(IStudent)
928    grok.name('cedit')
929    grok.require('waeup.handleStudent')
[6756]930    form_fields = grok.AutoFields(
931        IStudentClearanceEdit).omit('clearance_locked')
[6722]932    label = 'Edit clearance data'
[6695]933    title = 'Clearance Data'
934    pnav = 4
935    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
[6718]936
937    def emitLockMessage(self):
938        self.flash('The requested form is locked (read-only).')
939        self.redirect(self.url(self.context))
940        return
941
942    def update(self):
943        if self.context.clearance_locked:
944            self.emitLockMessage()
945            return
946        datepicker.need()
947        return super(StudentClearanceEditFormPage, self).update()
[6719]948
[6722]949    @grok.action('Save')
950    def save(self, **data):
951        self.applyData(self.context, **data)
[6771]952        self.flash('Clearance form has been saved.')
[6722]953        return
954
955    @grok.action('Save and request clearance')
956    def requestclearance(self, **data):
957        self.applyData(self.context, **data)
958        self.context._p_changed = True
959        #if self.dataNotComplete():
960        #    self.flash(self.dataNotComplete())
961        #    return
[6771]962        self.flash('Clearance form has been saved.')
[6769]963        self.redirect(self.url(self.context,'request_clearance'))
[6722]964        return
965
[6773]966class RequestClearancePage(WAeUPPage):
[6769]967    grok.context(IStudent)
968    grok.name('request_clearance')
969    grok.require('waeup.handleStudent')
970    grok.template('enterpin')
971    title = 'Request clearance'
972    label = 'Request clearance'
973    notice = 'Enter the CLR access code used for starting clearance.'
974    ac_prefix = 'CLR'
975    pnav = 4
976    buttonname = 'Request clearance now'
977
978    def update(self, SUBMIT=None):
979        self.ac_series = self.request.form.get('ac_series', None)
980        self.ac_number = self.request.form.get('ac_number', None)
981        if SUBMIT is None:
982            return
983        pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
984        if self.context.clr_code != pin:
985            self.flash("This isn't your CLR access code.")
986            return
987        state = IWorkflowState(self.context).getState()
988        # This shouldn't happen, but the application officer
989        # might have forgotten to lock the form after changing the state
990        if state != CLEARANCE:
991            self.flash('This form cannot be submitted. Wrong state!')
992            return
993        IWorkflowInfo(self.context).fireTransition('request_clearance')
994        self.flash('Clearance has been requested.')
995        self.redirect(self.url(self.context))
[6789]996        return
Note: See TracBrowser for help on using the repository browser.