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

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

Use getStudent instead of traversing the ancestral line with try/except.

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