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

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

Generate student ids randomly (tests will follow).

  • Property svn:keywords set to Id
File size: 14.8 KB
RevLine 
[6621]1## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
2## This program is free software; you can redistribute it and/or modify
3## it under the terms of the GNU General Public License as published by
4## the Free Software Foundation; either version 2 of the License, or
5## (at your option) any later version.
6##
7## This program is distributed in the hope that it will be useful,
8## but WITHOUT ANY WARRANTY; without even the implied warranty of
9## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10## GNU General Public License for more details.
11##
12## You should have received a copy of the GNU General Public License
13## along with this program; if not, write to the Free Software
14## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15##
16"""UI components for students and related components.
17"""
18import sys
19import grok
20
21from datetime import datetime
22from zope.formlib.widget import CustomWidgetFactory
23from zope.formlib.form import setUpEditWidgets
24from zope.securitypolicy.interfaces import IPrincipalRoleManager
25from zope.traversing.browser import absoluteURL
[6622]26from zope.component import (
27    createObject,)
[6621]28
29from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
30from reportlab.pdfgen import canvas
31from reportlab.lib.units import cm
32from reportlab.lib.pagesizes import A4
33from reportlab.lib.styles import getSampleStyleSheet
34from reportlab.platypus import (Frame, Paragraph, Image,
35    Table, Spacer)
36from reportlab.platypus.tables import TableStyle
37
38from waeup.sirp.accesscodes import invalidate_accesscode, get_access_code
39from waeup.sirp.accesscodes.workflow import USED
40from waeup.sirp.browser import (
41    WAeUPPage, WAeUPEditFormPage, WAeUPAddFormPage, WAeUPDisplayFormPage)
42from waeup.sirp.browser.breadcrumbs import Breadcrumb
43from waeup.sirp.browser.layout import NullValidator
44from waeup.sirp.browser.pages import add_local_role, del_local_roles
45from waeup.sirp.browser.resources import datepicker, tabs, datatable
46from waeup.sirp.browser.viewlets import (
[6642]47    ManageActionButton, PrimaryNavTab,
[6635]48    AddActionButton, ActionButton, PlainActionButton,
[6621]49    )
50from waeup.sirp.image.browser.widget import (
51    ThumbnailWidget, EncodingImageFileWidget,
52    )
53from waeup.sirp.image.image import createWAeUPImageFile
54from waeup.sirp.interfaces import IWAeUPObject, ILocalRolesAssignable
55from waeup.sirp.permissions import get_users_with_local_roles
56from waeup.sirp.university.interfaces import ICertificate
57from waeup.sirp.widgets.datewidget import (
58    FriendlyDateWidget, FriendlyDateDisplayWidget)
59from waeup.sirp.widgets.restwidget import ReSTDisplayWidget
60from waeup.sirp.widgets.objectwidget import (
61    WAeUPObjectWidget, WAeUPObjectDisplayWidget)
62from waeup.sirp.widgets.multilistwidget import (
63    MultiListWidget, MultiListDisplayWidget)
64from waeup.sirp.students.interfaces import (
[6631]65    IStudentsContainer, IStudent, IStudentClearance,
[6635]66    IStudentPersonal, IStudentBase, IStudentStudyCourse,
[6642]67    IStudentPayments, IStudentAccommodation, IStudentNavigation
[6621]68    )
[6651]69from waeup.sirp.students.student import Student
[6626]70from waeup.sirp.students.catalog import search
[6621]71
72class StudentsTab(PrimaryNavTab):
73    """Students tab in primary navigation.
74    """
75
76    grok.context(IWAeUPObject)
77    grok.order(3)
78    grok.require('waeup.viewStudents')
79    grok.template('primarynavtab')
80
81    pnav = 4
82    tab_title = u'Students'
83
84    @property
85    def link_target(self):
86        return self.view.application_url('students')
87
[6629]88class StudentsBreadcrumb(Breadcrumb):
89    """A breadcrumb for the students container.
90    """
91    grok.context(IStudentsContainer)
92    title = u'Students'
93
[6635]94class SudyCourseBreadcrumb(Breadcrumb):
95    """A breadcrumb for the student study course.
96    """
97    grok.context(IStudentStudyCourse)
98    title = u'Study Course'
99
100class PaymentsBreadcrumb(Breadcrumb):
101    """A breadcrumb for the student payments folder.
102    """
103    grok.context(IStudentPayments)
104    title = u'Payments'
105
106class AccommodationBreadcrumb(Breadcrumb):
107    """A breadcrumb for the student accommodation folder.
108    """
109    grok.context(IStudentAccommodation)
110    title = u'Accommodation'
111
[6626]112class StudentsContainerPage(WAeUPPage):
113    """The standard view for student containers.
[6621]114    """
115    grok.context(IStudentsContainer)
116    grok.name('index')
117    grok.require('waeup.viewStudents')
118    grok.template('studentscontainerpage')
[6642]119    pnav = 4
[6621]120
121    @property
122    def title(self):
123        return "Students"
124
125    @property
126    def label(self):
[6622]127        return self.title
128
[6626]129    def update(self, *args, **kw):
130        datatable.need()
131        form = self.request.form
132        self.hitlist = []
133        if 'searchterm' in form and form['searchterm']:
134            self.searchterm = form['searchterm']
135            self.searchtype = form['searchtype']
136        elif 'old_searchterm' in form:
137            self.searchterm = form['old_searchterm']
138            self.searchtype = form['old_searchtype']
139        else:
140            if 'search' in form:
141                self.flash('Empty search string.')
142            return
143        self.hitlist = search(query=self.searchterm,
144            searchtype=self.searchtype, view=self)
145        if not self.hitlist:
146            self.flash('No student found.')
147        return
148
149class StudentsContainerManageActionButton(ManageActionButton):
[6622]150    grok.order(1)
151    grok.context(IStudentsContainer)
152    grok.view(StudentsContainerPage)
153    grok.require('waeup.manageStudents')
[6647]154    text = 'Manage student section'
[6622]155
[6626]156
157class StudentsContainerManagePage(WAeUPPage):
158    """The manage page for student containers.
[6622]159    """
160    grok.context(IStudentsContainer)
161    grok.name('manage')
162    grok.require('waeup.manageStudents')
[6626]163    grok.template('studentscontainermanagepage')
[6642]164    pnav = 4
[6647]165    title = 'Manage student section'
[6622]166
167    @property
168    def label(self):
169        return self.title
170
[6626]171    def update(self, *args, **kw):
172        datatable.need()
173        form = self.request.form
174        self.hitlist = []
175        if 'searchterm' in form and form['searchterm']:
176            self.searchterm = form['searchterm']
177            self.searchtype = form['searchtype']
178        elif 'old_searchterm' in form:
179            self.searchterm = form['old_searchterm']
180            self.searchtype = form['old_searchtype']
181        else:
182            if 'search' in form:
183                self.flash('Empty search string.')
184            return
185        #import pdb; pdb.set_trace()
186        if not 'entries' in form:
187            self.hitlist = search(query=self.searchterm,
188                searchtype=self.searchtype, view=self)
189            if not self.hitlist:
190                self.flash('No student found.')
191            return
192        entries = form['entries']
193        if isinstance(entries, basestring):
194            entries = [entries]
195        deleted = []
196        for entry in entries:
197            if 'remove' in form:
198                del self.context[entry]
199                deleted.append(entry)
200        self.hitlist = search(query=self.searchterm,
201            searchtype=self.searchtype, view=self)
202        if len(deleted):
203            self.flash('Successfully removed: %s' % ', '.join(deleted))
[6622]204        return
205
[6626]206class StudentsContainerAddActionButton(AddActionButton):
207    grok.order(1)
208    grok.context(IStudentsContainer)
209    grok.view(StudentsContainerManagePage)
210    grok.require('waeup.manageStudents')
211    text = 'Add student'
212    target = 'addstudent'
213
[6622]214class StudentAddFormPage(WAeUPAddFormPage):
215    """Add-form to add a student.
216    """
217    grok.context(IStudentsContainer)
218    grok.require('waeup.manageStudents')
219    grok.name('addstudent')
220    grok.template('studentaddpage')
221    form_fields = grok.AutoFields(IStudent)
222    title = 'Students'
223    label = 'Add student'
[6642]224    pnav = 4
[6622]225
226    @grok.action('Create student record')
227    def addStudent(self, **data):
228        student = createObject(u'waeup.Student')
229        self.applyData(student, **data)
230        try:
[6633]231            self.context.addStudent(student)
[6622]232        except KeyError:
233            self.flash('The student id chosen already exists.')
234            return
[6626]235        self.flash('Student record created.')
[6651]236        self.redirect(self.url(self.context[student.student_id], 'index'))
[6622]237        return
238
[6631]239class StudentBaseDisplayFormPage(WAeUPDisplayFormPage):
240    """ Page to display student base data
241    """
[6622]242    grok.context(IStudent)
243    grok.name('index')
244    grok.require('waeup.viewStudents')
[6635]245    grok.template('studentpage')
[6631]246    form_fields = grok.AutoFields(IStudentBase)
[6642]247    pnav = 4
248    title = 'Base Data'
[6622]249
250    @property
251    def label(self):
[6631]252        return '%s: Base Data' % self.context.name
253
254class StudentBaseManageActionButton(ManageActionButton):
255    grok.order(1)
256    grok.context(IStudent)
257    grok.view(StudentBaseDisplayFormPage)
258    grok.require('waeup.manageStudents')
[6635]259    text = 'Edit'
[6631]260    target = 'edit_base'
261
262class StudentBaseManageFormPage(WAeUPEditFormPage):
263    """ View to edit student base data
264    """
265    grok.context(IStudent)
266    grok.name('edit_base')
267    grok.require('waeup.manageStudents')
268    form_fields = grok.AutoFields(IStudentBase).omit('student_id')
[6638]269    grok.template('studentbasemanagepage')
[6631]270    label = 'Edit base data'
[6642]271    title = 'Base Data'
272    pnav = 4
[6631]273
[6638]274    def update(self):
275        datepicker.need() # Enable jQuery datepicker in date fields.
276        super(StudentBaseManageFormPage, self).update()
277        self.wf_info = IWorkflowInfo(self.context)
278        return
279
280    def getTransitions(self):
281        """Return a list of dicts of allowed transition ids and titles.
282
283        Each list entry provides keys ``name`` and ``title`` for
284        internal name and (human readable) title of a single
285        transition.
286        """
287        allowed_transitions = self.wf_info.getManualTransitions()
288        return [dict(name='', title='No transition')] +[
289            dict(name=x, title=y) for x, y in allowed_transitions]
290
291    @grok.action('Save')
292    def save(self, **data):
293        changed_fields = self.applyData(self.context, **data)
294        changed_fields = changed_fields.values()
295        fields_string = '+'.join(' + '.join(str(i) for i in b) for b in changed_fields)
296        self.context._p_changed = True
297        form = self.request.form
298        if form.has_key('transition') and form['transition']:
299            transition_id = form['transition']
300            self.wf_info.fireTransition(transition_id)
301        self.flash('Form has been saved.')
302        ob_class = self.__implemented__.__name__.replace('waeup.sirp.','')
[6644]303        if fields_string:
304            self.context.loggerInfo(ob_class, 'saved: % s' % fields_string)
[6638]305        return
306
[6631]307class StudentClearanceDisplayFormPage(WAeUPDisplayFormPage):
308    """ Page to display student clearance data
309    """
310    grok.context(IStudent)
311    grok.name('view_clearance')
312    grok.require('waeup.viewStudents')
313    form_fields = grok.AutoFields(IStudentClearance)
[6650]314    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
[6642]315    title = 'Clearance Data'
316    pnav = 4
[6631]317
318    @property
319    def label(self):
320        return '%s: Clearance Data' % self.context.name
321
322class StudentClearanceManageActionButton(ManageActionButton):
323    grok.order(1)
324    grok.context(IStudent)
325    grok.view(StudentClearanceDisplayFormPage)
326    grok.require('waeup.manageStudents')
[6635]327    text = 'Edit'
[6631]328    target = 'edit_clearance'
329
330class StudentClearanceManageFormPage(WAeUPEditFormPage):
331    """ Page to edit student clearance data
332    """
333    grok.context(IStudent)
334    grok.name('edit_clearance')
[6649]335    grok.require('waeup.manageStudents')
[6631]336    form_fields = grok.AutoFields(IStudentClearance)
337    label = 'Edit clearance data'
[6642]338    title = 'Clearance Data'
339    pnav = 4
[6631]340
[6650]341    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
342
343    def update(self):
344        datepicker.need() # Enable jQuery datepicker in date fields.
345        return super(StudentClearanceManageFormPage, self).update()
346
[6631]347class StudentPersonalDisplayFormPage(WAeUPDisplayFormPage):
348    """ Page to display student personal data
349    """
350    grok.context(IStudent)
351    grok.name('view_personal')
352    grok.require('waeup.viewStudents')
353    form_fields = grok.AutoFields(IStudentPersonal)
[6642]354    title = 'Personal Data'
355    pnav = 4
[6631]356
357    @property
358    def label(self):
359        return '%s: Personal Data' % self.context.name
360
361class StudentPersonalManageActionButton(ManageActionButton):
362    grok.order(1)
363    grok.context(IStudent)
364    grok.view(StudentPersonalDisplayFormPage)
365    grok.require('waeup.manageStudents')
[6635]366    text = 'Edit'
[6631]367    target = 'edit_personal'
368
369class StudentPersonalManageFormPage(WAeUPEditFormPage):
370    """ Page to edit student clearance data
371    """
372    grok.context(IStudent)
373    grok.name('edit_personal')
374    grok.require('waeup.viewStudents')
375    form_fields = grok.AutoFields(IStudentPersonal)
376    label = 'Edit personal data'
[6642]377    title = 'Personal Data'
378    pnav = 4
[6631]379
[6635]380class StudyCourseDisplayFormPage(WAeUPDisplayFormPage):
381    """ Page to display the student study course data
382    """
383    grok.context(IStudentStudyCourse)
384    grok.name('index')
385    grok.require('waeup.viewStudents')
386    form_fields = grok.AutoFields(IStudentStudyCourse)
387    #grok.template('studycoursepage')
[6642]388    title = 'Study Course'
389    pnav = 4
[6635]390
391    @property
392    def label(self):
[6642]393        return '%s: Study Course' % self.context.__parent__.name
[6635]394
[6649]395class StudyCourseManageActionButton(ManageActionButton):
396    grok.order(1)
397    grok.context(IStudentStudyCourse)
398    grok.view(StudyCourseDisplayFormPage)
399    grok.require('waeup.manageStudents')
400    text = 'Edit'
401    target = 'edit'
402
403class StudyCourseManageFormPage(WAeUPEditFormPage):
404    """ Page to edit the student study course data
405    """
406    grok.context(IStudentStudyCourse)
407    grok.name('edit')
408    grok.require('waeup.manageStudents')
409    form_fields = grok.AutoFields(IStudentStudyCourse)
410    label = 'Edit clearance data'
411    title = 'Study Course'
412    label = 'Edit study course'
413    pnav = 4
414
[6635]415class PaymentsDisplayFormPage(WAeUPDisplayFormPage):
416    """ Page to display the student payments
417    """
418    grok.context(IStudentPayments)
419    grok.name('index')
420    grok.require('waeup.viewStudents')
421    form_fields = grok.AutoFields(IStudentPayments)
422    #grok.template('paymentspage')
[6642]423    title = 'Payments'
424    pnav = 4
[6635]425
426    @property
427    def label(self):
428        return '%s: Payments' % self.context.__parent__.name
429
430class AccommodationDisplayFormPage(WAeUPDisplayFormPage):
431    """ Page to display the student accommodation data
432    """
433    grok.context(IStudentAccommodation)
434    grok.name('index')
435    grok.require('waeup.viewStudents')
436    form_fields = grok.AutoFields(IStudentAccommodation)
437    #grok.template('accommodationpage')
[6642]438    title = 'Accommodation'
439    pnav = 4
[6635]440
441    @property
442    def label(self):
443        return '%s: Accommodation Data' % self.context.__parent__.name
[6637]444
445class StudentHistoryPage(WAeUPPage):
446    """ Page to display student clearance data
447    """
448    grok.context(IStudent)
449    grok.name('history')
450    grok.require('waeup.viewStudents')
451    grok.template('studenthistory')
[6642]452    title = 'History'
453    pnav = 4
[6637]454
455    @property
456    def label(self):
457        return '%s: History' % self.context.name
Note: See TracBrowser for help on using the repository browser.