source: main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser.py @ 6012

Last change on this file since 6012 was 6012, checked in by uli, 14 years ago

Make use of the new datatables in applicants overview page.

File size: 15.1 KB
Line 
1##
2## browser.py
3## Login : <uli@pu.smp.net>
4## Started on  Sun Jun 27 11:03:10 2010 Uli Fouquet
5## $Id$
6##
7## Copyright (C) 2010 Uli Fouquet
8## This program is free software; you can redistribute it and/or modify
9## it under the terms of the GNU General Public License as published by
10## the Free Software Foundation; either version 2 of the License, or
11## (at your option) any later version.
12##
13## This program is distributed in the hope that it will be useful,
14## but WITHOUT ANY WARRANTY; without even the implied warranty of
15## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16## GNU General Public License for more details.
17##
18## You should have received a copy of the GNU General Public License
19## along with this program; if not, write to the Free Software
20## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21##
22"""UI components for basic applicants and related components.
23"""
24import grok
25
26from datetime import datetime
27from hurry.jquery import jquery
28from hurry.jqueryui import jqueryui
29from zope.component import getUtility, getAllUtilitiesRegisteredFor
30from zope.formlib.widgets import FileWidget, DateWidget
31from zope.securitypolicy.interfaces import IPrincipalRoleManager
32from waeup.sirp.browser import (
33    WAeUPPage, WAeUPEditFormPage, WAeUPAddFormPage,
34    WAeUPDisplayFormPage, NullValidator)
35from waeup.sirp.browser.pages import LoginPage
36from waeup.sirp.interfaces import IWAeUPObject
37from waeup.sirp.browser.resources import datepicker, tabs
38from waeup.sirp.browser.viewlets import (
39    AddActionButton, ManageActionButton, PrimaryNavTab,
40    )
41from waeup.sirp.browser.breadcrumbs import Breadcrumb
42from waeup.sirp.applicants import get_applicant_data, ResultEntry, Applicant
43from waeup.sirp.applicants.interfaces import (
44    IApplicant, IApplicantPrincipal, IApplicantPDEEditData,
45    IApplicantsRoot, IApplicantsContainer, IApplicantsContainerProvider,
46    )
47from waeup.sirp.widgets.passportwidget import (
48    PassportWidget, PassportDisplayWidget
49    )
50#from zope.formlib.objectwidget import ObjectWidget
51from zope.formlib.sequencewidget import ListSequenceWidget, SequenceDisplayWidget
52from zope.formlib.widget import CustomWidgetFactory
53from waeup.sirp.utils.helpers import ReST2HTML
54from waeup.sirp.widgets.objectwidget import (
55    WAeUPObjectWidget, WAeUPObjectDisplayWidget)
56from waeup.sirp.widgets.multilistwidget import (
57    MultiListWidget, MultiListDisplayWidget)
58from waeup.sirp.image.browser.widget import (
59    ThumbnailWidget, EncodingImageFileWidget,
60    )
61
62results_widget = CustomWidgetFactory(
63    WAeUPObjectWidget, ResultEntry)
64
65results_display_widget = CustomWidgetFactory(
66    WAeUPObjectDisplayWidget, ResultEntry)
67
68#list_results_widget = CustomWidgetFactory(
69#    ListSequenceWidget, subwidget=results_widget)
70
71list_results_widget = CustomWidgetFactory(
72    MultiListWidget, subwidget=results_widget)
73
74list_results_display_widget = CustomWidgetFactory(
75    MultiListDisplayWidget, subwidget=results_display_widget)
76
77#: A date widget that renders with CSS class `datepicker` thus
78#  enabling : the jQuery datepicker for this field if the form loaded
79#  the jQuery code.
80FriendlyDateWidget = CustomWidgetFactory(
81    DateWidget, cssClass='datepicker')
82
83class ApplicationsPage(WAeUPPage):
84    grok.context(IApplicantsRoot)
85    grok.name('index')
86    title = 'Applicants'
87    pnav = 3
88
89    def update(self):
90        super(ApplicationsPage, self).update()
91        from waeup.sirp.browser.resources import datatable
92        datatable.need()
93        return
94
95    def getApplications(self):
96        """Get a list of all stored applicant containers.
97        """
98        for key, val in self.context.items():
99            url = self.url(val)
100            yield(dict(url=url, name=key, container=val))
101
102class ManageApplicantsRootActionButton(ManageActionButton):
103    grok.context(IApplicantsRoot)
104    grok.view(ApplicationsPage)
105    text = 'Manage applicant containers'
106
107class ApplicantsRootEditPage(WAeUPPage):
108    grok.context(IApplicantsRoot)
109    grok.name('manage')
110    grok.template('applicantsrooteditpage')
111    title = 'Edit applicants containers'
112    pnav = 3
113
114    def update(self, entries=None, DELETE=None, CANCEL=None):
115        if CANCEL is not None:
116            self.redirect(self.url(self.context))
117            return
118        if DELETE is None:
119            return
120        if entries is None:
121            return
122        if not isinstance(entries, list):
123            entries = [entries]
124        for name in entries:
125            del self.context[name]
126            self.flash('Deleted "%s"' % name)
127        return
128
129    def getApplications(self):
130        """Get a list of all stored applicant containers.
131        """
132        for key, val in self.context.items():
133            url = self.url(val)
134            yield(dict(url=url, name=key))
135
136class AddApplicantsContainerActionButton(AddActionButton):
137    grok.context(IApplicantsRoot)
138    grok.view(ApplicantsRootEditPage)
139    text = 'Add applicants container'
140
141class AddApplicantsContainer(WAeUPPage):
142    grok.context(IApplicantsRoot)
143    grok.name('add')
144    grok.template('addcontainer')
145    title = 'Add applicants container'
146    pnav = 3
147
148    def update(self, providername=None, name=None, title=None,
149               description=None, ADD=None, CANCEL=None):
150        if CANCEL is not None:
151            self.redirect(self.url(self.context))
152            return
153        if ADD is None:
154            return
155        if not name:
156            self.flash('Error: you must give a name')
157            return
158        if name in self.context.keys():
159            self.flash('A container of the given name already exists')
160            return
161        # Add new applicants container...
162        provider = getUtility(IApplicantsContainerProvider,
163                              name=providername)
164        container = provider.factory()
165        if title:
166            container.title = title
167        if description:
168            container.description = description
169        self.context[name] = container
170        self.flash('Added "%s".' % name)
171        self.redirect(self.url(self.context))
172        return
173       
174    def getContainerProviders(self):
175        """Get a list of applicants container providers.
176
177        Applicants container providers are named utilities that help
178        to create applicants containers of different types
179        (JAMB-based, non-JAMB-based, etc.).
180
181        The list returned contains dicts::
182
183          {'name': <utility_name>,
184           'provider': <provider instance>}
185
186        where `utility_name` is the name under which the respective
187        provider utility is registered and `provider` is the real
188        provider instance.
189
190        The `utility_name` can be used to lookup the utility anew (for
191        instance after submitting a form) and the `provider` instance
192        can be used to create new instances of the respective
193        applicants container type.
194        """
195        providers = getAllUtilitiesRegisteredFor(IApplicantsContainerProvider)
196        result = [
197            {'name': getattr(x, 'grokcore.component.directive.name'),
198             'provider': x}
199             for x in providers
200            ]
201        return result
202
203class ApplicantsRootBreadcrumb(Breadcrumb):
204    """A breadcrumb for applicantsroot.
205    """
206    grok.context(IApplicantsRoot)
207    title = u'Applicants'
208   
209class ApplicantsContainerBreadcrumb(Breadcrumb):
210    """A breadcrumb for applicantscontainers.
211    """
212    grok.context(IApplicantsContainer)
213
214class ApplicantsTab(PrimaryNavTab):
215    """Faculties-tab in primary navigation.
216    """
217    grok.context(IWAeUPObject)
218    grok.order(3)
219    grok.require('waeup.View')
220    grok.template('primarynavtab')
221
222    pnav = 3
223    tab_title = u'Applicants'
224
225    @property
226    def link_target(self):
227        return self.view.application_url('applicants')
228
229class ApplicantsContainerPage(WAeUPPage):
230    """The standard view for regular applicant containers.
231    """
232    grok.context(IApplicantsContainer)
233    grok.name('index')
234    pnav = 3
235   
236    @property
237    def title(self):
238        return "Applicants Container: %s" % getattr(
239            self.context, '__name__', 'unnamed')
240
241    @property
242    def label(self):
243        return self.title
244
245    def descriptionToHTML(self):
246        return ReST2HTML(self.context.description)
247
248class ManageApplicantsContainerActionButton(ManageActionButton):
249    grok.context(IApplicantsContainer)
250    grok.view(ApplicantsContainerPage)
251    text = 'Manage'
252
253
254class ManageApplicantsContainer(WAeUPEditFormPage):
255    grok.context(IApplicantsContainer)
256    grok.name('manage')
257    grok.template('form_manage_applicants_container')
258    form_fields = grok.AutoFields(IApplicantsContainer)
259    # Use friendlier date widget...
260    form_fields['startdate'].custom_widget = FriendlyDateWidget
261    form_fields['enddate'].custom_widget = FriendlyDateWidget
262
263    @property
264    def title(self):
265        return "Manage applicants container: %s" % getattr(
266            self.context, '__name__', 'unnamed')
267   
268    @property
269    def label(self):
270        return self.title
271
272    pnav = 3
273
274    def update(self):
275        datepicker.need() # Enable jQuery datepicker in date fields.
276        tabs.need()
277        return super(ManageApplicantsContainer, self).update()
278
279    @grok.action('Save')
280    def apply(self, **data):
281        self.applyData(self.context, **data)
282        self.flash('Data saved.')
283        return
284       
285    @grok.action('Back')
286    def cancel(self, **data):
287        self.redirect(self.url(self.context))
288        return
289
290class LoginApplicant(WAeUPPage):
291    grok.context(IApplicantsContainer)
292    grok.name('login')
293    grok.require('zope.Public')
294
295    title = u'Login'
296   
297    @property
298    def label(self):
299        return self.title
300
301    pnav = 3
302    prefix = u'APP'
303   
304    def update(self, SUBMIT=None):
305        self.ac_series = self.request.form.get('form.ac_series', None)
306        self.ac_number = self.request.form.get('form.ac_number', None)
307        if SUBMIT is None:
308            return
309
310        if self.request.principal.id == 'zope.anybody':
311            self.flash('Entered credentials are invalid')
312            return
313
314        if not IApplicantPrincipal.providedBy(self.request.principal):
315            # Don't care if user is already authenticated as non-applicant
316            return
317
318        pin = self.request.principal.access_code
319        if pin not in self.context.keys():
320            # Create applicant record
321            applicant = Applicant()
322            applicant.access_code = pin
323            self.context[pin] = applicant
324           
325        # Assign current principal the owner role on created applicant
326        # record
327        role_manager = IPrincipalRoleManager(self.context)
328        role_manager.assignRoleToPrincipal(
329            'waeup.ApplicationOwner', self.request.principal.id)
330        self.redirect(self.url(self.context[pin], 'edit'))
331        return
332
333#class AddApplicant(WAeUPAddFormPage):
334#    grok.context(IApplicantsContainer)
335#    grok.name('add')
336#    form_fields = grok.AutoFields(IApplicant)
337#    form_fields['fst_sit_results'].custom_widget = list_results_widget
338#    form_fields['passport'].custom_widget = EncodingImageFileWidget
339#    label = 'Add Applicant'
340#    title = 'Add Applicant'
341#    pnav = 1
342#
343#    @grok.action('Add applicant')
344#    def addApplicant(self, **data):
345#        from waeup.sirp.jambtables.applicants import Applicant
346#        applicant = Applicant()
347#        self.applyData(applicant, **data)
348#        # XXX: temporarily disabled.
349#        #self.context[applicant.reg_no] = applicant
350#        try:
351#            self.context[applicant.access_code] = applicant
352#        except KeyError:
353#            self.flash('The given access code is already in use!')
354#            return
355#        self.redirect(self.url(self.context))
356
357class DisplayApplicant(WAeUPDisplayFormPage):
358    grok.context(IApplicant)
359    grok.name('index')
360    grok.require('waeup.viewApplication')
361    form_fields = grok.AutoFields(IApplicant).omit('locked')
362    form_fields['fst_sit_results'].custom_widget = list_results_display_widget
363    #form_fields['passport'].custom_widget = PassportDisplayWidget
364    form_fields['passport'].custom_widget = ThumbnailWidget
365    #form_fields['passport'].custom_widget = EncodingImageFileWidget
366    label = 'Applicant'
367    title = 'Applicant'
368    pnav = 3
369
370class EditApplicantStudent(WAeUPEditFormPage):
371    """An applicant-centered edit view for applicant data.
372    """
373    grok.context(IApplicant)
374    grok.name('edit')
375    grok.require('waeup.editApplication')
376    form_fields = grok.AutoFields(IApplicantPDEEditData).omit('locked')
377    #form_fields['passport'].custom_widget = FileWidget
378    #form_fields['passport'].custom_widget = PassportWidget
379    form_fields['passport'].custom_widget = EncodingImageFileWidget
380    grok.template('form_edit_pde')
381
382    def emitLockMessage(self):
383        self.flash('The requested form is locked (read-only)')
384        self.redirect(self.url(self.context))
385        return
386   
387    def update(self):
388        if self.context.locked:
389            self.emitLockMessage()
390            return
391        super(EditApplicantStudent, self).update()
392        return
393
394    def filteredWidgets(self):
395        for widget in self.widgets:
396            if widget.name == 'form.confirm_passport':
397                continue
398            yield widget
399
400    @property
401    def label(self):
402        # XXX: Use current/upcoming session
403        return 'Apply for Post UDE Screening Test (2009/2010)'
404    title = 'Edit Application'
405    pnav = 3
406
407    @grok.action('Save')
408    def save(self, **data):
409        if self.context.locked:
410            self.emitLockMessage()
411            return
412        self.applyData(self.context, **data)
413        self.context._p_changed = True
414        return
415
416    @grok.action('Final Submit')
417    def finalsubmit(self, **data):
418        if self.context.locked:
419            self.emitLockMessage()
420            return
421        self.applyData(self.context, **data)
422        self.context._p_changed = True
423        if not self.dataComplete():
424            self.flash('Data yet not complete.')
425            return
426        self.context.locked = True
427        return
428
429    def dataComplete(self):
430        if context.confirm_passport is not True:
431            return False
432        if len(self.errors) > 0:
433            return False
434        return True
435
436class EditApplicantFull(WAeUPEditFormPage):
437    """A full edit view for applicant data.
438
439    This one is meant to be used by officers only.
440    """
441    grok.context(IApplicant)
442    grok.name('edit_full')
443    grok.require('waeup.editFullApplication')
444    form_fields = grok.AutoFields(IApplicantPDEEditData).omit('locked')
445    form_fields['passport'].custom_widget = EncodingImageFileWidget
446    grok.template('form_edit_full')
447
448    def update(self):
449        if self.context.locked:
450            self.emitLockMessage()
451            return
452        super(EditApplicantFull, self).update()
453        return
454
455    def filteredWidgets(self):
456        for widget in self.widgets:
457            if widget.name == 'form.confirm_passport':
458                continue
459            yield widget
460
461    @property
462    def label(self):
463        return 'Application for %s' % self.context.access_code
464    title = 'Edit Application'
465    pnav = 3
466
467    @grok.action('Save')
468    def save(self, **data):
469        self.applyData(self.context, **data)
470        self.context._p_changed = True
471        return
Note: See TracBrowser for help on using the repository browser.