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

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

Add StudyLevelManageFormPage? and corresponding page template.

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