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

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

Add register course list action.

  • Property svn:keywords set to Id
File size: 38.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 StudyCourseManageFormPage(WAeUPEditFormPage):
489    """ Page to edit the student study course data
490    """
491    grok.context(IStudentStudyCourse)
492    grok.name('manage')
493    grok.require('waeup.manageStudents')
494    grok.template('studycoursemanagepage')
495    form_fields = grok.AutoFields(IStudentStudyCourse)
496    title = 'Study Course'
497    label = 'Manage study course'
498    pnav = 4
499    taboneactions = ['Save','Cancel']
500    tabtwoactions = ['Remove selected levels','Cancel']
501    tabthreeactions = ['Add study level']
502
503    def update(self):
504        super(StudyCourseManageFormPage, self).update()
505        tabs.need()
506        datatable.need()
507        return
508
509    @grok.action('Save')
510    def save(self, **data):
511        msave(self, **data)
512        return
513
514    @property
515    def level_dict(self):
516        studylevelsource = StudyLevelSource().factory
517        for code in studylevelsource.getValues(self.context):
518            title = studylevelsource.getTitle(self.context, code)
519            yield(dict(code=code, title=title))
520
521    @grok.action('Add study level')
522    def addStudyLevel(self, **data):
523        level_code = self.request.form.get('addlevel', None)
524        studylevel = StudentStudyLevel()
525        studylevel.level = int(level_code)
526        try:
527            self.context.addStudentStudyLevel(
528                self.context.certificate,studylevel)
529        except KeyError:
530            self.flash('This level exists.')
531        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
532        return
533
534    @grok.action('Remove selected levels')
535    def delStudyLevels(self, **data):
536        form = self.request.form
537        if form.has_key('val_id'):
538            child_id = form['val_id']
539        else:
540            self.flash('No study level selected.')
541            self.redirect(self.url(self.context, '@@manage')+'#tab-2')
542            return
543        if not isinstance(child_id, list):
544            child_id = [child_id]
545        deleted = []
546        for id in child_id:
547            try:
548                del self.context[id]
549                deleted.append(id)
550            except:
551                self.flash('Could not delete %s: %s: %s' % (
552                        id, sys.exc_info()[0], sys.exc_info()[1]))
553        if len(deleted):
554            self.flash('Successfully removed: %s' % ', '.join(deleted))
555        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
556        return
557
558class StudyLevelDisplayFormPage(WAeUPDisplayFormPage):
559    """ Page to display student study levels
560    """
561    grok.context(IStudentStudyLevel)
562    grok.name('index')
563    grok.require('waeup.viewStudent')
564    form_fields = grok.AutoFields(IStudentStudyLevel)
565    grok.template('studylevelpage')
566    pnav = 4
567
568    @property
569    def title(self):
570        return 'Study Level %s' % self.context.level_title
571
572    @property
573    def label(self):
574        return '%s: Study Level %s' % (
575            self.context.getStudent().name,self.context.level_title)
576
577    @property
578    def total_credits(self):
579        total_credits = 0
580        for key, val in self.context.items():
581            total_credits += val.credits
582        return total_credits
583
584class StudyLevelManageActionButton(ManageActionButton):
585    grok.order(1)
586    grok.context(IStudentStudyLevel)
587    grok.view(StudyLevelDisplayFormPage)
588    grok.require('waeup.manageStudents')
589    text = 'Manage'
590    target = 'manage'
591
592class StudyLevelManageFormPage(WAeUPEditFormPage):
593    """ Page to edit the student study level data
594    """
595    grok.context(IStudentStudyLevel)
596    grok.name('manage')
597    grok.require('waeup.manageStudents')
598    grok.template('studylevelmanagepage')
599    form_fields = grok.AutoFields(IStudentStudyLevel)
600    pnav = 4
601    taboneactions = ['Save','Cancel']
602    tabtwoactions = ['Add course ticket','Remove selected tickets','Cancel']
603
604    def update(self):
605        super(StudyLevelManageFormPage, self).update()
606        tabs.need()
607        datatable.need()
608        return
609
610    @property
611    def title(self):
612        return 'Study Level %s' % self.context.level_title
613
614    @property
615    def label(self):
616        return 'Manage study level %s' % self.context.level_title
617
618    @grok.action('Save')
619    def save(self, **data):
620        msave(self, **data)
621        return
622
623    @grok.action('Add course ticket')
624    def addCourseTicket(self, **data):
625        self.redirect(self.url(self.context, '@@add'))
626
627    @grok.action('Remove selected tickets')
628    def delCourseTicket(self, **data):
629        form = self.request.form
630        if form.has_key('val_id'):
631            child_id = form['val_id']
632        else:
633            self.flash('No ticket selected.')
634            self.redirect(self.url(self.context, '@@manage')+'#tab-2')
635            return
636        if not isinstance(child_id, list):
637            child_id = [child_id]
638        deleted = []
639        for id in child_id:
640            try:
641                del self.context[id]
642                deleted.append(id)
643            except:
644                self.flash('Could not delete %s: %s: %s' % (
645                        id, sys.exc_info()[0], sys.exc_info()[1]))
646        if len(deleted):
647            self.flash('Successfully removed: %s' % ', '.join(deleted))
648        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
649        return
650
651class CourseTicketAddFormPage(WAeUPAddFormPage):
652    """Add a course ticket.
653    """
654    grok.context(IStudentStudyLevel)
655    grok.name('add')
656    grok.require('waeup.manageStudents')
657    label = 'Add course ticket'
658    form_fields = grok.AutoFields(ICourseTicketAdd).omit(
659        'grade', 'score', 'automatic')
660    pnav = 4
661
662    @property
663    def title(self):
664        return 'Study Level %s' % self.context.level_title
665
666    @grok.action('Add course ticket')
667    def addCourseTicket(self, **data):
668        ticket = CourseTicket()
669        course = data['course']
670        ticket.core_or_elective = data['core_or_elective']
671        ticket.automatic = False
672        ticket.code = course.code
673        ticket.title = course.title
674        ticket.faculty = course.__parent__.__parent__.__parent__.title
675        ticket.department = course.__parent__.__parent__.title
676        ticket.credits = course.credits
677        ticket.passmark = course.passmark
678        ticket.semester = course.semester
679        try:
680            self.context.addCourseTicket(ticket)
681        except KeyError:
682            self.flash('The ticket exists.')
683            return
684        self.flash('Successfully added %s.' % ticket.code)
685        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
686        return
687
688    @grok.action('Cancel')
689    def cancel(self, **data):
690        self.redirect(self.url(self.context))
691
692class CourseTicketDisplayFormPage(WAeUPDisplayFormPage):
693    """ Page to display course tickets
694    """
695    grok.context(ICourseTicket)
696    grok.name('index')
697    grok.require('waeup.viewStudent')
698    form_fields = grok.AutoFields(ICourseTicket)
699    grok.template('courseticketpage')
700    pnav = 4
701
702    @property
703    def title(self):
704        return 'Course Ticket %s' % self.context.code
705
706    @property
707    def label(self):
708        return '%s: Course Ticket %s' % (
709            self.context.getStudent().name,self.context.code)
710
711class CourseTicketManageActionButton(ManageActionButton):
712    grok.order(1)
713    grok.context(ICourseTicket)
714    grok.view(CourseTicketDisplayFormPage)
715    grok.require('waeup.manageStudents')
716    text = 'Manage'
717    target = 'manage'
718
719class CourseTicketManageFormPage(WAeUPEditFormPage):
720    """ Page to manage course tickets
721    """
722    grok.context(ICourseTicket)
723    grok.name('manage')
724    grok.require('waeup.manageStudents')
725    form_fields = grok.AutoFields(ICourseTicket)
726    grok.template('courseticketmanagepage')
727    pnav = 4
728
729    @property
730    def title(self):
731        return 'Course Ticket %s' % self.context.code
732
733    @property
734    def label(self):
735        return 'Manage course ticket %s' % self.context.code
736
737    @grok.action('Save')
738    def save(self, **data):
739        msave(self, **data)
740        return
741
742class PaymentsDisplayFormPage(WAeUPDisplayFormPage):
743    """ Page to display the student payments
744    """
745    grok.context(IStudentPayments)
746    grok.name('index')
747    grok.require('waeup.viewStudent')
748    form_fields = grok.AutoFields(IStudentPayments)
749    #grok.template('paymentspage')
750    title = 'Payments'
751    pnav = 4
752
753    @property
754    def label(self):
755        return '%s: Payments' % self.context.__parent__.name
756
757class AccommodationDisplayFormPage(WAeUPDisplayFormPage):
758    """ Page to display the student accommodation data
759    """
760    grok.context(IStudentAccommodation)
761    grok.name('index')
762    grok.require('waeup.viewStudent')
763    form_fields = grok.AutoFields(IStudentAccommodation)
764    #grok.template('accommodationpage')
765    title = 'Accommodation'
766    pnav = 4
767
768    @property
769    def label(self):
770        return '%s: Accommodation Data' % self.context.__parent__.name
771
772class StudentHistoryPage(WAeUPPage):
773    """ Page to display student clearance data
774    """
775    grok.context(IStudent)
776    grok.name('history')
777    grok.require('waeup.viewStudent')
778    grok.template('studenthistory')
779    title = 'History'
780    pnav = 4
781
782    @property
783    def label(self):
784        return '%s: History' % self.context.name
785
786# Pages for students only
787
788class StudentBaseEditActionButton(ManageActionButton):
789    grok.order(1)
790    grok.context(IStudent)
791    grok.view(StudentBaseDisplayFormPage)
792    grok.require('waeup.handleStudent')
793    text = 'Change password'
794    target = 'bedit'
795
796class StudentPasswordSetting(grok.Adapter):
797    """Adapt IStudent to data needed for password settings.
798
799    We provide password getters/setters for the attached context (an
800    IStudent object) that cooperate seamless with the usual
801    formlib/form techniques.
802    """
803    grok.context(IStudent)
804    grok.provides(IStudentPasswordSetting)
805
806    def __init__(self, context):
807        self.name = context.name
808        self.password_repeat = context.password
809        self.context = context
810        return
811
812    def getPassword(self):
813        return self.context.password
814
815    def setPassword(self, password):
816        IUserAccount(self.context).setPassword(password)
817        return
818
819    password = property(getPassword, setPassword)
820
821class StudentBaseEditFormPage(WAeUPEditFormPage):
822    """ View to edit student base data by student
823    """
824    grok.context(IStudent)
825    grok.name('bedit')
826    grok.require('waeup.handleStudent')
827    #form_fields = grok.AutoFields(IStudentBaseEdit).omit(
828    #    'student_id', 'reg_number', 'matric_number')
829    form_fields = grok.AutoFields(IStudentPasswordSetting)
830    grok.template('baseeditpage')
831    label = 'Change password'
832    title = 'Base Data'
833    pnav = 4
834
835    def update(self):
836        super(StudentBaseEditFormPage, self).update()
837        self.wf_info = IWorkflowInfo(self.context)
838        return
839
840    def onFailure(self, action, data, errors):
841        new_status = []
842        other_errors = False
843        for error in errors:
844            msg = getattr(error, 'message', '')
845            if isinstance(msg, basestring) and msg != '':
846                new_status.append(msg)
847            else:
848                other_errors = True
849        if other_errors:
850            if new_status:
851                new_status.append('see below for further errors')
852            else:
853                new_status.append('See below for details.')
854        if new_status:
855            self.status = u'There were errors: %s' % ', '.join(new_status)
856        return
857
858    @grok.action('Save', failure=onFailure)
859    def save(self, **data):
860        self.applyData(self.context, **data)
861        self.flash('Form has been saved.')
862        return
863
864class StudentClearanceStartActionButton(ManageActionButton):
865    grok.order(1)
866    grok.context(IStudent)
867    grok.view(StudentClearanceDisplayFormPage)
868    grok.require('waeup.handleStudent')
869    icon = 'actionicon_start.png'
870    text = 'Start clearance'
871    target = 'start_clearance'
872
873    @property
874    def target_url(self):
875        if self.context.state != 'admitted':
876            return ''
877        return self.view.url(self.view.context, self.target)
878
879class StartClearancePage(WAeUPPage):
880    grok.context(IStudent)
881    grok.name('start_clearance')
882    grok.require('waeup.handleStudent')
883    grok.template('enterpin')
884    title = 'Start clearance'
885    label = 'Start clearance'
886    ac_prefix = 'CLR'
887    notice = ''
888    pnav = 4
889    buttonname = 'Start clearance now'
890
891    def update(self, SUBMIT=None):
892        self.ac_series = self.request.form.get('ac_series', None)
893        self.ac_number = self.request.form.get('ac_number', None)
894
895        if SUBMIT is None:
896            return
897        pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
898        code = get_access_code(pin)
899        if not code:
900            self.flash('Access code is invalid.')
901            return
902        # Mark pin as used (this also fires a pin related transition)
903        # and fire transition start_clearance
904        if code.state == USED:
905            self.flash('Access code has already been used.')
906            return
907        else:
908            comment = u"AC invalidated for %s" % self.context.student_id
909            # Here we know that the ac is in state initialized so we do not
910            # expect an exception
911            invalidate_accesscode(pin,comment)
912            self.context.clr_code = pin
913        IWorkflowInfo(self.context).fireTransition('start_clearance')
914        self.flash('Clearance process has been started.')
915        self.redirect(self.url(self.context,'cedit'))
916        return
917
918class StudentClearanceEditActionButton(ManageActionButton):
919    grok.order(1)
920    grok.context(IStudent)
921    grok.view(StudentClearanceDisplayFormPage)
922    grok.require('waeup.handleStudent')
923    text = 'Edit'
924    target = 'cedit'
925
926    @property
927    def target_url(self):
928        if self.context.clearance_locked:
929            return ''
930        return self.view.url(self.view.context, self.target)
931
932class StudentClearanceEditFormPage(StudentClearanceManageFormPage):
933    """ View to edit student clearance data by student
934    """
935    grok.context(IStudent)
936    grok.name('cedit')
937    grok.require('waeup.handleStudent')
938    form_fields = grok.AutoFields(
939        IStudentClearanceEdit).omit('clearance_locked')
940    label = 'Edit clearance data'
941    title = 'Clearance Data'
942    pnav = 4
943    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
944
945    def emitLockMessage(self):
946        self.flash('The requested form is locked (read-only).')
947        self.redirect(self.url(self.context))
948        return
949
950    def update(self):
951        if self.context.clearance_locked:
952            self.emitLockMessage()
953            return
954        datepicker.need()
955        return super(StudentClearanceEditFormPage, self).update()
956
957    @grok.action('Save')
958    def save(self, **data):
959        self.applyData(self.context, **data)
960        self.flash('Clearance form has been saved.')
961        return
962
963    @grok.action('Save and request clearance')
964    def requestclearance(self, **data):
965        self.applyData(self.context, **data)
966        self.context._p_changed = True
967        #if self.dataNotComplete():
968        #    self.flash(self.dataNotComplete())
969        #    return
970        self.flash('Clearance form has been saved.')
971        self.redirect(self.url(self.context,'request_clearance'))
972        return
973
974class RequestClearancePage(WAeUPPage):
975    grok.context(IStudent)
976    grok.name('request_clearance')
977    grok.require('waeup.handleStudent')
978    grok.template('enterpin')
979    title = 'Request clearance'
980    label = 'Request clearance'
981    notice = 'Enter the CLR access code used for starting clearance.'
982    ac_prefix = 'CLR'
983    pnav = 4
984    buttonname = 'Request clearance now'
985
986    def update(self, SUBMIT=None):
987        self.ac_series = self.request.form.get('ac_series', None)
988        self.ac_number = self.request.form.get('ac_number', None)
989        if SUBMIT is None:
990            return
991        pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number)
992        if self.context.clr_code != pin:
993            self.flash("This isn't your CLR access code.")
994            return
995        state = IWorkflowState(self.context).getState()
996        # This shouldn't happen, but the application officer
997        # might have forgotten to lock the form after changing the state
998        if state != CLEARANCE:
999            self.flash('This form cannot be submitted. Wrong state!')
1000            return
1001        IWorkflowInfo(self.context).fireTransition('request_clearance')
1002        self.flash('Clearance has been requested.')
1003        self.redirect(self.url(self.context))
1004        return
1005
1006class AddStudyLevelActionButton(AddActionButton):
1007    grok.order(1)
1008    grok.context(IStudentStudyCourse)
1009    grok.view(StudyCourseDisplayFormPage)
1010    grok.require('waeup.handleStudent')
1011    text = 'Add course list'
1012    target = 'add'
1013
1014    @property
1015    def target_url(self):
1016        student = self.view.context.getStudent()
1017        condition1 = student.state != 'school fee paid'
1018        condition2 = str(student['studycourse'].current_level) in \
1019            self.view.context.keys()
1020        if condition1 or condition2:
1021            return ''
1022        return self.view.url(self.view.context, self.target)
1023
1024class AddStudyLevelFormPage(WAeUPEditFormPage):
1025    """ Page for students to add current study levels
1026    """
1027    grok.context(IStudentStudyCourse)
1028    grok.name('add')
1029    grok.require('waeup.handleStudent')
1030    grok.template('studyleveladdpage')
1031    form_fields = grok.AutoFields(IStudentStudyCourse)
1032    title = 'Study Course'
1033    pnav = 4
1034
1035    @property
1036    def label(self):
1037        studylevelsource = StudyLevelSource().factory
1038        code = self.context.current_level
1039        title = studylevelsource.getTitle(self.context, code)
1040        return 'Add current level %s' % title
1041
1042    def emitLockMessage(self):
1043        self.flash('The requested form is locked (read-only).')
1044        self.redirect(self.url(self.context))
1045        return
1046
1047    def update(self):
1048        if self.context.getStudent().state != 'school fee paid':
1049            self.emitLockMessage()
1050            return
1051        super(AddStudyLevelFormPage, self).update()
1052        return
1053
1054    @grok.action('Create course list now')
1055    def addStudyLevel(self, **data):
1056        studylevel = StudentStudyLevel()
1057        studylevel.level = self.context.current_level
1058        studylevel.level_session = self.context.current_session
1059        try:
1060            self.context.addStudentStudyLevel(
1061                self.context.certificate,studylevel)
1062        except KeyError:
1063            self.flash('This level exists.')
1064        self.redirect(self.url(self.context))
1065        return
1066
1067class StudyLevelEditActionButton(ManageActionButton):
1068    grok.order(1)
1069    grok.context(IStudentStudyLevel)
1070    grok.view(StudyLevelDisplayFormPage)
1071    grok.require('waeup.handleStudent')
1072    text = 'Add and remove courses'
1073    target = 'edit'
1074
1075    @property
1076    def target_url(self):
1077        student = self.view.context.getStudent()
1078        condition1 = student.state != 'school fee paid'
1079        condition2 = student[
1080            'studycourse'].current_level != self.view.context.level
1081        if condition1 or condition2:
1082            return ''
1083        return self.view.url(self.view.context, self.target)
1084
1085class StudyLevelEditFormPage(WAeUPEditFormPage):
1086    """ Page to edit the student study level data by students
1087    """
1088    grok.context(IStudentStudyLevel)
1089    grok.name('edit')
1090    grok.require('waeup.handleStudent')
1091    grok.template('studyleveleditpage')
1092    form_fields = grok.AutoFields(IStudentStudyLevel).omit(
1093        'level_session', 'level_verdict')
1094    pnav = 4
1095
1096    def update(self):
1097        super(StudyLevelEditFormPage, self).update()
1098    #    tabs.need()
1099        datatable.need()
1100        return
1101
1102    @property
1103    def title(self):
1104        return 'Study Level %s' % self.context.level_title
1105
1106    @property
1107    def label(self):
1108        return 'Add and remove course tickets of study level %s' % self.context.level_title
1109
1110    @property
1111    def total_credits(self):
1112        total_credits = 0
1113        for key, val in self.context.items():
1114            total_credits += val.credits
1115        return total_credits
1116
1117    @grok.action('Add course ticket')
1118    def addCourseTicket(self, **data):
1119        self.redirect(self.url(self.context, 'ctadd'))
1120
1121    @grok.action('Remove selected tickets')
1122    def delCourseTicket(self, **data):
1123        form = self.request.form
1124        if form.has_key('val_id'):
1125            child_id = form['val_id']
1126        else:
1127            self.flash('No ticket selected.')
1128            self.redirect(self.url(self.context, '@@edit'))
1129            return
1130        if not isinstance(child_id, list):
1131            child_id = [child_id]
1132        deleted = []
1133        for id in child_id:
1134            # Student are not allowed to remove core tickets
1135            if not self.context[id].core_or_elective:
1136                try:
1137                    del self.context[id]
1138                    deleted.append(id)
1139                except:
1140                    self.flash('Could not delete %s: %s: %s' % (
1141                            id, sys.exc_info()[0], sys.exc_info()[1]))
1142        if len(deleted):
1143            self.flash('Successfully removed: %s' % ', '.join(deleted))
1144        self.redirect(self.url(self.context, u'@@edit'))
1145        return
1146
1147    @grok.action('Register course list')
1148    def register_courses(self, **data):
1149        state = IWorkflowState(self.context.getStudent()).getState()
1150        IWorkflowInfo(self.context.getStudent()).fireTransition('register_courses')
1151        self.flash('Course list has been registered.')
1152        self.redirect(self.url(self.context))
1153        return
1154
1155class CourseTicketAddFormPage2(CourseTicketAddFormPage):
1156    """Add a course ticket by student.
1157    """
1158    grok.name('ctadd')
1159    grok.require('waeup.handleStudent')
1160    form_fields = grok.AutoFields(ICourseTicketAdd).omit(
1161        'grade', 'score', 'core_or_elective', 'automatic')
1162
1163    @grok.action('Add course ticket')
1164    def addCourseTicket(self, **data):
1165        ticket = CourseTicket()
1166        course = data['course']
1167        ticket.automatic = False
1168        ticket.code = course.code
1169        ticket.title = course.title
1170        ticket.faculty = course.__parent__.__parent__.__parent__.title
1171        ticket.department = course.__parent__.__parent__.title
1172        ticket.credits = course.credits
1173        ticket.passmark = course.passmark
1174        ticket.semester = course.semester
1175        try:
1176            self.context.addCourseTicket(ticket)
1177        except KeyError:
1178            self.flash('The ticket exists.')
1179            return
1180        self.flash('Successfully added %s.' % ticket.code)
1181        self.redirect(self.url(self.context, u'@@edit'))
1182        return
Note: See TracBrowser for help on using the repository browser.