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

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

Implement AddStudyLevelFormPage? for students which add the current study level including course tickets. The page can only be called in state 'school fee paid'.

  • Property svn:keywords set to Id
File size: 34.8 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    ICourseTicket, ICourseTicketAdd,
38    )
39from waeup.sirp.students.catalog import search
40from waeup.sirp.students.workflow import CLEARANCE
41from waeup.sirp.students.studylevel import StudentStudyLevel, CourseTicket
42from waeup.sirp.students.vocabularies import StudyLevelSource
43
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)
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)
53    view.context._p_changed = True
54    view.flash('Form has been saved.')
55    if fields_string:
56        view.context.getStudent().loggerInfo(ob_class, 'saved: % s' % fields_string)
57    return
58
59class StudentsTab(PrimaryNavTab):
60    """Students tab in primary navigation.
61    """
62
63    grok.context(IWAeUPObject)
64    grok.order(3)
65    grok.require('waeup.viewStudent')
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
75class StudentsBreadcrumb(Breadcrumb):
76    """A breadcrumb for the students container.
77    """
78    grok.context(IStudentsContainer)
79    title = u'Students'
80
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
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
108class StudentsContainerPage(WAeUPPage):
109    """The standard view for student containers.
110    """
111    grok.context(IStudentsContainer)
112    grok.name('index')
113    grok.require('waeup.viewStudent')
114    grok.template('containerpage')
115    label = 'Student Section'
116    title = 'Students'
117    pnav = 4
118
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
139class SetPasswordPage(WAeUPPage):
140    grok.context(IWAeUPObject)
141    grok.name('setpassword')
142    grok.require('waeup.Public')
143    grok.template('setpassword')
144    title = ''
145    label = 'Set password for first-time login'
146    ac_prefix = 'PWD'
147    pnav = 0
148
149    def update(self, SUBMIT=None):
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)
153
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
164        student = hitlist[0].context
165        self.student_id = student.student_id
166        student_pw = student.password
167        pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
168        code = get_access_code(pin)
169        if not code:
170            self.flash('Access code is invalid.')
171            return
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. You are using the wrong Access Code.')
178            return
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:
185            comment = u"AC invalidated for %s" % self.student_id
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)
190            IUserAccount(student).setPassword(self.ac_number)
191            student.adm_code = pin
192        self.flash('Password has been set. Your Student Id is %s'
193            % self.student_id)
194        return
195
196class StudentsContainerManageActionButton(ManageActionButton):
197    grok.order(1)
198    grok.context(IStudentsContainer)
199    grok.view(StudentsContainerPage)
200    grok.require('waeup.manageStudents')
201    text = 'Manage student section'
202
203
204class StudentsContainerManagePage(WAeUPPage):
205    """The manage page for student containers.
206    """
207    grok.context(IStudentsContainer)
208    grok.name('manage')
209    grok.require('waeup.manageStudents')
210    grok.template('containermanagepage')
211    pnav = 4
212    title = 'Manage student section'
213
214    @property
215    def label(self):
216        return self.title
217
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))
250        return
251
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
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'
270    pnav = 4
271
272    @grok.action('Create student record')
273    def addStudent(self, **data):
274        student = createObject(u'waeup.Student')
275        self.applyData(student, **data)
276        self.context.addStudent(student)
277        self.flash('Student record created.')
278        self.redirect(self.url(self.context[student.student_id], 'index'))
279        return
280
281class StudentBaseDisplayFormPage(WAeUPDisplayFormPage):
282    """ Page to display student base data
283    """
284    grok.context(IStudent)
285    grok.name('index')
286    grok.require('waeup.viewStudent')
287    grok.template('basepage')
288    form_fields = grok.AutoFields(IStudentBase).omit('password')
289    pnav = 4
290    title = 'Base Data'
291
292    @property
293    def label(self):
294        return '%s: Base Data' % self.context.name
295
296    @property
297    def hasPassword(self):
298        if self.context.password:
299            return 'set'
300        return 'unset'
301
302class StudentBaseManageActionButton(ManageActionButton):
303    grok.order(1)
304    grok.context(IStudent)
305    grok.view(StudentBaseDisplayFormPage)
306    grok.require('waeup.manageStudents')
307    text = 'Manage'
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')
317    grok.template('basemanagepage')
318    label = 'Manage base data'
319    title = 'Base Data'
320    pnav = 4
321
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):
341        form = self.request.form
342        ob_class = self.__implemented__.__name__.replace('waeup.sirp.','')
343        password = form.get('password', None)
344        password_ctl = form.get('control_password', None)
345        if password:
346            if (password != password_ctl):
347                self.flash('Passwords do not match.')
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
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
368        # Turn list of lists into single list
369        changed_fields = self.applyData(self.context, **data)
370        if changed_fields:
371            changed_fields = reduce(lambda x,y: x+y, changed_fields.values())
372        fields_string = ' + '.join(changed_fields)
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.')
378        if fields_string:
379            self.context.loggerInfo(ob_class, 'saved: % s' % fields_string)
380        return
381
382class StudentClearanceDisplayFormPage(WAeUPDisplayFormPage):
383    """ Page to display student clearance data
384    """
385    grok.context(IStudent)
386    grok.name('view_clearance')
387    grok.require('waeup.viewStudent')
388    form_fields = grok.AutoFields(IStudentClearance).omit('clearance_locked')
389    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
390    title = 'Clearance Data'
391    pnav = 4
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')
402    text = 'Manage'
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')
410    grok.require('waeup.manageStudents')
411    form_fields = grok.AutoFields(IStudentClearance)
412    label = 'Manage clearance data'
413    title = 'Clearance Data'
414    pnav = 4
415
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
422    @grok.action('Save')
423    def save(self, **data):
424        msave(self, **data)
425        return
426
427class StudentPersonalDisplayFormPage(WAeUPDisplayFormPage):
428    """ Page to display student personal data
429    """
430    grok.context(IStudent)
431    grok.name('view_personal')
432    grok.require('waeup.viewStudent')
433    form_fields = grok.AutoFields(IStudentPersonal)
434    title = 'Personal Data'
435    pnav = 4
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')
446    text = 'Manage'
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')
454    grok.require('waeup.viewStudent')
455    form_fields = grok.AutoFields(IStudentPersonal)
456    label = 'Manage personal data'
457    title = 'Personal Data'
458    pnav = 4
459
460    @grok.action('Save')
461    def save(self, **data):
462        msave(self, **data)
463        return
464
465class StudyCourseDisplayFormPage(WAeUPDisplayFormPage):
466    """ Page to display the student study course data
467    """
468    grok.context(IStudentStudyCourse)
469    grok.name('index')
470    grok.require('waeup.viewStudent')
471    form_fields = grok.AutoFields(IStudentStudyCourse)
472    grok.template('studycoursepage')
473    title = 'Study Course'
474    pnav = 4
475
476    @property
477    def label(self):
478        return '%s: Study Course' % self.context.__parent__.name
479
480class StudyCourseManageActionButton(ManageActionButton):
481    grok.order(1)
482    grok.context(IStudentStudyCourse)
483    grok.view(StudyCourseDisplayFormPage)
484    grok.require('waeup.manageStudents')
485    text = 'Manage'
486    target = 'manage'
487
488class AddStudyLevelActionButton(AddActionButton):
489    grok.order(1)
490    grok.context(IStudentStudyCourse)
491    grok.view(StudyCourseDisplayFormPage)
492    grok.require('waeup.handleStudent')
493    text = 'Add course list'
494    target = 'add'
495
496    @property
497    def target_url(self):
498        student = self.view.context.getStudent()
499        condition1 = student.state != 'school fee paid'
500        condition2 = str(student['studycourse'].current_level) in \
501            self.view.context.keys()
502        if condition1 or condition2:
503            return ''
504        return self.view.url(self.view.context, self.target)
505
506class StudyCourseManageFormPage(WAeUPEditFormPage):
507    """ Page to edit the student study course data
508    """
509    grok.context(IStudentStudyCourse)
510    grok.name('manage')
511    grok.require('waeup.manageStudents')
512    grok.template('studycoursemanagepage')
513    form_fields = grok.AutoFields(IStudentStudyCourse)
514    title = 'Study Course'
515    label = 'Manage study course'
516    pnav = 4
517    taboneactions = ['Save','Cancel']
518    tabtwoactions = ['Remove selected levels','Cancel']
519    tabthreeactions = ['Add study level']
520
521    def update(self):
522        super(StudyCourseManageFormPage, self).update()
523        tabs.need()
524        datatable.need()
525        return
526
527    @grok.action('Save')
528    def save(self, **data):
529        msave(self, **data)
530        return
531
532    @property
533    def level_dict(self):
534        studylevelsource = StudyLevelSource().factory
535        for code in studylevelsource.getValues(self.context):
536            title = studylevelsource.getTitle(self.context, code)
537            yield(dict(code=code, title=title))
538
539    @grok.action('Add study level')
540    def addStudyLevel(self, **data):
541        level_code = self.request.form.get('addlevel', None)
542        studylevel = StudentStudyLevel()
543        studylevel.level = int(level_code)
544        try:
545            self.context.addStudentStudyLevel(
546                self.context.certificate,studylevel)
547        except KeyError:
548            self.flash('This level exists.')
549        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
550        return
551
552    @grok.action('Remove selected levels')
553    def delStudyLevels(self, **data):
554        form = self.request.form
555        if form.has_key('val_id'):
556            child_id = form['val_id']
557        else:
558            self.flash('No study level selected.')
559            self.redirect(self.url(self.context, '@@manage')+'#tab-2')
560            return
561        if not isinstance(child_id, list):
562            child_id = [child_id]
563        deleted = []
564        for id in child_id:
565            try:
566                del self.context[id]
567                deleted.append(id)
568            except:
569                self.flash('Could not delete %s: %s: %s' % (
570                        id, sys.exc_info()[0], sys.exc_info()[1]))
571        if len(deleted):
572            self.flash('Successfully removed: %s' % ', '.join(deleted))
573        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
574        return
575
576class StudyLevelDisplayFormPage(WAeUPDisplayFormPage):
577    """ Page to display student study levels
578    """
579    grok.context(IStudentStudyLevel)
580    grok.name('index')
581    grok.require('waeup.viewStudent')
582    form_fields = grok.AutoFields(IStudentStudyLevel)
583    grok.template('studylevelpage')
584    pnav = 4
585
586    @property
587    def title(self):
588        return 'Study Level %s' % self.context.level_title
589
590    @property
591    def label(self):
592        return '%s: Study Level %s' % (
593            self.context.getStudent().name,self.context.level_title)
594
595    @property
596    def total_credits(self):
597        total_credits = 0
598        for key, val in self.context.items():
599            total_credits += val.credits
600        return total_credits
601
602class StudyLevelManageActionButton(ManageActionButton):
603    grok.order(1)
604    grok.context(IStudentStudyLevel)
605    grok.view(StudyLevelDisplayFormPage)
606    grok.require('waeup.manageStudents')
607    text = 'Manage'
608    target = 'manage'
609
610class StudyLevelManageFormPage(WAeUPEditFormPage):
611    """ Page to edit the student study level data
612    """
613    grok.context(IStudentStudyLevel)
614    grok.name('manage')
615    grok.require('waeup.manageStudents')
616    grok.template('studylevelmanagepage')
617    form_fields = grok.AutoFields(IStudentStudyLevel)
618    pnav = 4
619    taboneactions = ['Save','Cancel']
620    tabtwoactions = ['Add course ticket','Remove selected tickets','Cancel']
621
622    def update(self):
623        super(StudyLevelManageFormPage, self).update()
624        tabs.need()
625        datatable.need()
626        return
627
628    @property
629    def title(self):
630        return 'Study Level %s' % self.context.level_title
631
632    @property
633    def label(self):
634        return 'Manage study level %s' % self.context.level_title
635
636    @grok.action('Save')
637    def save(self, **data):
638        msave(self, **data)
639        return
640
641    @grok.action('Add course ticket')
642    def addCourseTicket(self, **data):
643        self.redirect(self.url(self.context, '@@add'))
644
645    @grok.action('Remove selected tickets')
646    def delCourseTicket(self, **data):
647        form = self.request.form
648        if form.has_key('val_id'):
649            child_id = form['val_id']
650        else:
651            self.flash('No ticket selected.')
652            self.redirect(self.url(self.context, '@@manage')+'#tab-2')
653            return
654        if not isinstance(child_id, list):
655            child_id = [child_id]
656        deleted = []
657        for id in child_id:
658            try:
659                del self.context[id]
660                deleted.append(id)
661            except:
662                self.flash('Could not delete %s: %s: %s' % (
663                        id, sys.exc_info()[0], sys.exc_info()[1]))
664        if len(deleted):
665            self.flash('Successfully removed: %s' % ', '.join(deleted))
666        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
667        return
668
669class CourseTicketAddFormPage(WAeUPAddFormPage):
670    """Add a department to a faculty.
671    """
672    grok.context(IStudentStudyLevel)
673    grok.name('add')
674    grok.require('waeup.manageStudents')
675    label = 'Add course ticket'
676    form_fields = grok.AutoFields(ICourseTicketAdd).omit('grade', 'score')
677    pnav = 4
678
679    @property
680    def title(self):
681        return 'Study Level %s' % self.context.level_title
682
683    @grok.action('Add course ticket')
684    def addCourseTicket(self, **data):
685        ticket = CourseTicket()
686        course = data['course']
687        ticket.core_or_elective = data['core_or_elective']
688        ticket.automatic = False
689        ticket.code = course.code
690        ticket.title = course.title
691        ticket.faculty = course.__parent__.__parent__.__parent__.title
692        ticket.department = course.__parent__.__parent__.title
693        ticket.credits = course.credits
694        ticket.passmark = course.passmark
695        ticket.semester = course.semester
696        try:
697            self.context.addCourseTicket(ticket)
698        except KeyError:
699            self.flash('The ticket exists.')
700            return
701        self.flash('Successfully added %s.' % ticket.code)
702        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
703        return
704
705    @grok.action('Cancel')
706    def cancel(self, **data):
707        self.redirect(self.url(self.context))
708
709class CourseTicketDisplayFormPage(WAeUPDisplayFormPage):
710    """ Page to display course tickets
711    """
712    grok.context(ICourseTicket)
713    grok.name('index')
714    grok.require('waeup.viewStudent')
715    form_fields = grok.AutoFields(ICourseTicket)
716    grok.template('courseticketpage')
717    pnav = 4
718
719    @property
720    def title(self):
721        return 'Course Ticket %s' % self.context.code
722
723    @property
724    def label(self):
725        return '%s: Course Ticket %s' % (
726            self.context.getStudent().name,self.context.code)
727
728class CourseTicketManageActionButton(ManageActionButton):
729    grok.order(1)
730    grok.context(ICourseTicket)
731    grok.view(CourseTicketDisplayFormPage)
732    grok.require('waeup.manageStudents')
733    text = 'Manage'
734    target = 'manage'
735
736class CourseTicketManageFormPage(WAeUPEditFormPage):
737    """ Page to manage course tickets
738    """
739    grok.context(ICourseTicket)
740    grok.name('manage')
741    grok.require('waeup.manageStudents')
742    form_fields = grok.AutoFields(ICourseTicket)
743    grok.template('courseticketmanagepage')
744    pnav = 4
745
746    @property
747    def title(self):
748        return 'Course Ticket %s' % self.context.code
749
750    @property
751    def label(self):
752        return 'Manage course ticket %s' % self.context.code
753
754    @grok.action('Save')
755    def save(self, **data):
756        msave(self, **data)
757        return
758
759class PaymentsDisplayFormPage(WAeUPDisplayFormPage):
760    """ Page to display the student payments
761    """
762    grok.context(IStudentPayments)
763    grok.name('index')
764    grok.require('waeup.viewStudent')
765    form_fields = grok.AutoFields(IStudentPayments)
766    #grok.template('paymentspage')
767    title = 'Payments'
768    pnav = 4
769
770    @property
771    def label(self):
772        return '%s: Payments' % self.context.__parent__.name
773
774class AccommodationDisplayFormPage(WAeUPDisplayFormPage):
775    """ Page to display the student accommodation data
776    """
777    grok.context(IStudentAccommodation)
778    grok.name('index')
779    grok.require('waeup.viewStudent')
780    form_fields = grok.AutoFields(IStudentAccommodation)
781    #grok.template('accommodationpage')
782    title = 'Accommodation'
783    pnav = 4
784
785    @property
786    def label(self):
787        return '%s: Accommodation Data' % self.context.__parent__.name
788
789class StudentHistoryPage(WAeUPPage):
790    """ Page to display student clearance data
791    """
792    grok.context(IStudent)
793    grok.name('history')
794    grok.require('waeup.viewStudent')
795    grok.template('studenthistory')
796    title = 'History'
797    pnav = 4
798
799    @property
800    def label(self):
801        return '%s: History' % self.context.name
802
803# Pages for students only
804
805class StudentBaseEditActionButton(ManageActionButton):
806    grok.order(1)
807    grok.context(IStudent)
808    grok.view(StudentBaseDisplayFormPage)
809    grok.require('waeup.handleStudent')
810    text = 'Change password'
811    target = 'bedit'
812
813class StudentPasswordSetting(grok.Adapter):
814    """Adapt IStudent to data needed for password settings.
815
816    We provide password getters/setters for the attached context (an
817    IStudent object) that cooperate seamless with the usual
818    formlib/form techniques.
819    """
820    grok.context(IStudent)
821    grok.provides(IStudentPasswordSetting)
822
823    def __init__(self, context):
824        self.name = context.name
825        self.password_repeat = context.password
826        self.context = context
827        return
828
829    def getPassword(self):
830        return self.context.password
831
832    def setPassword(self, password):
833        IUserAccount(self.context).setPassword(password)
834        return
835
836    password = property(getPassword, setPassword)
837
838class StudentBaseEditFormPage(WAeUPEditFormPage):
839    """ View to edit student base data by student
840    """
841    grok.context(IStudent)
842    grok.name('bedit')
843    grok.require('waeup.handleStudent')
844    #form_fields = grok.AutoFields(IStudentBaseEdit).omit(
845    #    'student_id', 'reg_number', 'matric_number')
846    form_fields = grok.AutoFields(IStudentPasswordSetting)
847    grok.template('baseeditpage')
848    label = 'Change password'
849    title = 'Base Data'
850    pnav = 4
851
852    def update(self):
853        super(StudentBaseEditFormPage, self).update()
854        self.wf_info = IWorkflowInfo(self.context)
855        return
856
857    def onFailure(self, action, data, errors):
858        new_status = []
859        other_errors = False
860        for error in errors:
861            msg = getattr(error, 'message', '')
862            if isinstance(msg, basestring) and msg != '':
863                new_status.append(msg)
864            else:
865                other_errors = True
866        if other_errors:
867            if new_status:
868                new_status.append('see below for further errors')
869            else:
870                new_status.append('See below for details.')
871        if new_status:
872            self.status = u'There were errors: %s' % ', '.join(new_status)
873        return
874
875    @grok.action('Save', failure=onFailure)
876    def save(self, **data):
877        self.applyData(self.context, **data)
878        self.flash('Form has been saved.')
879        return
880
881class StudentClearanceStartActionButton(ManageActionButton):
882    grok.order(1)
883    grok.context(IStudent)
884    grok.view(StudentClearanceDisplayFormPage)
885    grok.require('waeup.handleStudent')
886    icon = 'actionicon_start.png'
887    text = 'Start clearance'
888    target = 'start_clearance'
889
890    @property
891    def target_url(self):
892        if self.context.state != 'admitted':
893            return ''
894        return self.view.url(self.view.context, self.target)
895
896class StartClearancePage(WAeUPPage):
897    grok.context(IStudent)
898    grok.name('start_clearance')
899    grok.require('waeup.handleStudent')
900    grok.template('enterpin')
901    title = 'Start clearance'
902    label = 'Start clearance'
903    ac_prefix = 'CLR'
904    notice = ''
905    pnav = 4
906    buttonname = 'Start clearance now'
907
908    def update(self, SUBMIT=None):
909        self.ac_series = self.request.form.get('ac_series', None)
910        self.ac_number = self.request.form.get('ac_number', None)
911
912        if SUBMIT is None:
913            return
914        pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
915        code = get_access_code(pin)
916        if not code:
917            self.flash('Access code is invalid.')
918            return
919        # Mark pin as used (this also fires a pin related transition)
920        # and fire transition start_clearance
921        if code.state == USED:
922            self.flash('Access code has already been used.')
923            return
924        else:
925            comment = u"AC invalidated for %s" % self.context.student_id
926            # Here we know that the ac is in state initialized so we do not
927            # expect an exception
928            invalidate_accesscode(pin,comment)
929            self.context.clr_code = pin
930        IWorkflowInfo(self.context).fireTransition('start_clearance')
931        self.flash('Clearance process has been started.')
932        self.redirect(self.url(self.context,'cedit'))
933        return
934
935class StudentClearanceEditActionButton(ManageActionButton):
936    grok.order(1)
937    grok.context(IStudent)
938    grok.view(StudentClearanceDisplayFormPage)
939    grok.require('waeup.handleStudent')
940    text = 'Edit'
941    target = 'cedit'
942
943    @property
944    def target_url(self):
945        if self.context.clearance_locked:
946            return ''
947        return self.view.url(self.view.context, self.target)
948
949class StudentClearanceEditFormPage(StudentClearanceManageFormPage):
950    """ View to edit student clearance data by student
951    """
952    grok.context(IStudent)
953    grok.name('cedit')
954    grok.require('waeup.handleStudent')
955    form_fields = grok.AutoFields(
956        IStudentClearanceEdit).omit('clearance_locked')
957    label = 'Edit clearance data'
958    title = 'Clearance Data'
959    pnav = 4
960    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
961
962    def emitLockMessage(self):
963        self.flash('The requested form is locked (read-only).')
964        self.redirect(self.url(self.context))
965        return
966
967    def update(self):
968        if self.context.clearance_locked:
969            self.emitLockMessage()
970            return
971        datepicker.need()
972        return super(StudentClearanceEditFormPage, self).update()
973
974    @grok.action('Save')
975    def save(self, **data):
976        self.applyData(self.context, **data)
977        self.flash('Clearance form has been saved.')
978        return
979
980    @grok.action('Save and request clearance')
981    def requestclearance(self, **data):
982        self.applyData(self.context, **data)
983        self.context._p_changed = True
984        #if self.dataNotComplete():
985        #    self.flash(self.dataNotComplete())
986        #    return
987        self.flash('Clearance form has been saved.')
988        self.redirect(self.url(self.context,'request_clearance'))
989        return
990
991class RequestClearancePage(WAeUPPage):
992    grok.context(IStudent)
993    grok.name('request_clearance')
994    grok.require('waeup.handleStudent')
995    grok.template('enterpin')
996    title = 'Request clearance'
997    label = 'Request clearance'
998    notice = 'Enter the CLR access code used for starting clearance.'
999    ac_prefix = 'CLR'
1000    pnav = 4
1001    buttonname = 'Request clearance now'
1002
1003    def update(self, SUBMIT=None):
1004        self.ac_series = self.request.form.get('ac_series', None)
1005        self.ac_number = self.request.form.get('ac_number', None)
1006        if SUBMIT is None:
1007            return
1008        pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
1009        if self.context.clr_code != pin:
1010            self.flash("This isn't your CLR access code.")
1011            return
1012        state = IWorkflowState(self.context).getState()
1013        # This shouldn't happen, but the application officer
1014        # might have forgotten to lock the form after changing the state
1015        if state != CLEARANCE:
1016            self.flash('This form cannot be submitted. Wrong state!')
1017            return
1018        IWorkflowInfo(self.context).fireTransition('request_clearance')
1019        self.flash('Clearance has been requested.')
1020        self.redirect(self.url(self.context))
1021        return
1022
1023class AddStudyLevelFormPage(WAeUPEditFormPage):
1024    """ Page for students to add current study levels
1025    """
1026    grok.context(IStudentStudyCourse)
1027    grok.name('add')
1028    grok.require('waeup.handleStudent')
1029    grok.template('studyleveladdpage')
1030    form_fields = grok.AutoFields(IStudentStudyCourse)
1031    title = 'Study Course'
1032    pnav = 4
1033
1034    @property
1035    def label(self):
1036        studylevelsource = StudyLevelSource().factory
1037        code = self.context.current_level
1038        title = studylevelsource.getTitle(self.context, code)
1039        return 'Add current level %s' % title
1040
1041    def emitLockMessage(self):
1042        self.flash('The requested form is locked (read-only).')
1043        self.redirect(self.url(self.context))
1044        return
1045
1046    def update(self):
1047        if self.context.getStudent().state != 'school fee paid':
1048            self.emitLockMessage()
1049            return
1050        super(AddStudyLevelFormPage, self).update()
1051        return
1052
1053    @grok.action('Create course list now')
1054    def addStudyLevel(self, **data):
1055        studylevel = StudentStudyLevel()
1056        studylevel.level = self.context.current_level
1057        studylevel.level_session = self.context.current_session
1058        try:
1059            self.context.addStudentStudyLevel(
1060                self.context.certificate,studylevel)
1061        except KeyError:
1062            self.flash('This level exists.')
1063        self.redirect(self.url(self.context))
1064        return
Note: See TracBrowser for help on using the repository browser.