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

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

Try out new date display widget.

File size: 15.8 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, datatable
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        datatable.need()
92        return
93
94    def getApplications(self):
95        """Get a list of all stored applicant containers.
96        """
97        for key, val in self.context.items():
98            url = self.url(val)
99            yield(dict(url=url, name=key, container=val))
100
101class ManageApplicantsRootActionButton(ManageActionButton):
102    grok.context(IApplicantsRoot)
103    grok.view(ApplicationsPage)
104    text = 'Manage applicant containers'
105
106class ApplicantsRootEditPage(WAeUPPage):
107    grok.context(IApplicantsRoot)
108    grok.name('manage')
109    grok.template('applicantsrooteditpage')
110    title = 'Edit applicants containers'
111    pnav = 3
112
113    def update(self, entries=None, DELETE=None, CANCEL=None):
114        if CANCEL is not None:
115            self.redirect(self.url(self.context))
116            return
117        if DELETE is None:
118            return
119        if entries is None:
120            return
121        if not isinstance(entries, list):
122            entries = [entries]
123        for name in entries:
124            del self.context[name]
125            self.flash('Deleted "%s"' % name)
126        return
127
128    def getApplications(self):
129        """Get a list of all stored applicant containers.
130        """
131        for key, val in self.context.items():
132            url = self.url(val)
133            yield(dict(url=url, name=key))
134
135class AddApplicantsContainerActionButton(AddActionButton):
136    grok.context(IApplicantsRoot)
137    grok.view(ApplicantsRootEditPage)
138    text = 'Add applicants container'
139
140class AddApplicantsContainer(WAeUPPage):
141    grok.context(IApplicantsRoot)
142    grok.name('add')
143    grok.template('addcontainer')
144    title = 'Add applicants container'
145    pnav = 3
146
147    def update(self, providername=None, name=None, title=None,
148               description=None, ADD=None, CANCEL=None):
149        if CANCEL is not None:
150            self.redirect(self.url(self.context))
151            return
152        if ADD is None:
153            return
154        if not name:
155            self.flash('Error: you must give a name')
156            return
157        if name in self.context.keys():
158            self.flash('A container of the given name already exists')
159            return
160        # Add new applicants container...
161        provider = getUtility(IApplicantsContainerProvider,
162                              name=providername)
163        container = provider.factory()
164        if title:
165            container.title = title
166        if description:
167            container.description = description
168        self.context[name] = container
169        self.flash('Added "%s".' % name)
170        self.redirect(self.url(self.context))
171        return
172       
173    def getContainerProviders(self):
174        """Get a list of applicants container providers.
175
176        Applicants container providers are named utilities that help
177        to create applicants containers of different types
178        (JAMB-based, non-JAMB-based, etc.).
179
180        The list returned contains dicts::
181
182          {'name': <utility_name>,
183           'provider': <provider instance>}
184
185        where `utility_name` is the name under which the respective
186        provider utility is registered and `provider` is the real
187        provider instance.
188
189        The `utility_name` can be used to lookup the utility anew (for
190        instance after submitting a form) and the `provider` instance
191        can be used to create new instances of the respective
192        applicants container type.
193        """
194        providers = getAllUtilitiesRegisteredFor(IApplicantsContainerProvider)
195        result = [
196            {'name': getattr(x, 'grokcore.component.directive.name'),
197             'provider': x}
198             for x in providers
199            ]
200        return result
201
202class ApplicantsRootBreadcrumb(Breadcrumb):
203    """A breadcrumb for applicantsroot.
204    """
205    grok.context(IApplicantsRoot)
206    title = u'Applicants'
207   
208class ApplicantsContainerBreadcrumb(Breadcrumb):
209    """A breadcrumb for applicantscontainers.
210    """
211    grok.context(IApplicantsContainer)
212
213class ApplicantsTab(PrimaryNavTab):
214    """Faculties-tab in primary navigation.
215    """
216    grok.context(IWAeUPObject)
217    grok.order(3)
218    grok.require('waeup.View')
219    grok.template('primarynavtab')
220
221    pnav = 3
222    tab_title = u'Applicants'
223
224    @property
225    def link_target(self):
226        return self.view.application_url('applicants')
227
228from waeup.sirp.widgets.datewidget import (
229    FormattedDateWidget, FormattedDateDisplayWidget,)
230
231FriendlyDateWidget = CustomWidgetFactory(
232    FormattedDateWidget,
233    cssClass='datepicker-le-year',
234    date_format='%d/%m/%Y')
235FriendlyDateDisplayWidget = CustomWidgetFactory(
236    FormattedDateDisplayWidget,
237    date_format='%d/%m/%Y')
238
239class ApplicantsContainerPage(WAeUPDisplayFormPage):
240    """The standard view for regular applicant containers.
241    """
242    grok.context(IApplicantsContainer)
243    grok.name('index')
244    grok.template('applicantscontainerpage')
245    pnav = 3
246
247    form_fields = grok.AutoFields(IApplicantsContainer)
248    # Use friendlier date widget...
249    form_fields['startdate'].custom_widget = FriendlyDateDisplayWidget
250    form_fields['enddate'].custom_widget = FriendlyDateDisplayWidget
251
252    @property
253    def title(self):
254        return "Applicants Container: %s" % getattr(
255            self.context, '__name__', 'unnamed')
256
257    @property
258    def label(self):
259        return self.title
260
261    def descriptionToHTML(self):
262        if self.context.description:
263            return ReST2HTML(self.context.description)
264        else:
265            return
266
267class ManageApplicantsContainerActionButton(ManageActionButton):
268    grok.context(IApplicantsContainer)
269    grok.view(ApplicantsContainerPage)
270    text = 'Manage'
271
272
273class ManageApplicantsContainer(WAeUPEditFormPage):
274    grok.context(IApplicantsContainer)
275    grok.name('manage')
276    grok.template('form_manage_applicants_container')
277    form_fields = grok.AutoFields(IApplicantsContainer)
278    # Use friendlier date widget...
279    form_fields['startdate'].custom_widget = FriendlyDateWidget
280    form_fields['enddate'].custom_widget = FriendlyDateWidget
281
282    @property
283    def title(self):
284        return "Manage applicants container: %s" % getattr(
285            self.context, '__name__', 'unnamed')
286   
287    @property
288    def label(self):
289        return self.title
290
291    pnav = 3
292
293    def update(self):
294        datepicker.need() # Enable jQuery datepicker in date fields.
295        tabs.need()
296        datatable.need()  # Enable jQurey datatables for contents listing
297        return super(ManageApplicantsContainer, self).update()
298
299    @grok.action('Save')
300    def apply(self, **data):
301        self.applyData(self.context, **data)
302        self.flash('Data saved.')
303        return
304       
305    @grok.action('Back')
306    def cancel(self, **data):
307        self.redirect(self.url(self.context))
308        return
309
310class LoginApplicant(WAeUPPage):
311    grok.context(IApplicantsContainer)
312    grok.name('login')
313    grok.require('zope.Public')
314
315    title = u'Login'
316   
317    @property
318    def label(self):
319        return self.title
320
321    pnav = 3
322    prefix = u'APP'
323   
324    def update(self, SUBMIT=None):
325        self.ac_series = self.request.form.get('form.ac_series', None)
326        self.ac_number = self.request.form.get('form.ac_number', None)
327        if SUBMIT is None:
328            return
329
330        if self.request.principal.id == 'zope.anybody':
331            self.flash('Entered credentials are invalid')
332            return
333
334        if not IApplicantPrincipal.providedBy(self.request.principal):
335            # Don't care if user is already authenticated as non-applicant
336            return
337
338        pin = self.request.principal.access_code
339        if pin not in self.context.keys():
340            # Create applicant record
341            applicant = Applicant()
342            applicant.access_code = pin
343            self.context[pin] = applicant
344           
345        # Assign current principal the owner role on created applicant
346        # record
347        role_manager = IPrincipalRoleManager(self.context)
348        role_manager.assignRoleToPrincipal(
349            'waeup.local.ApplicationOwner', self.request.principal.id)
350        self.redirect(self.url(self.context[pin], 'edit'))
351        return
352
353#class AddApplicant(WAeUPAddFormPage):
354#    grok.context(IApplicantsContainer)
355#    grok.name('add')
356#    form_fields = grok.AutoFields(IApplicant)
357#    form_fields['fst_sit_results'].custom_widget = list_results_widget
358#    form_fields['passport'].custom_widget = EncodingImageFileWidget
359#    label = 'Add Applicant'
360#    title = 'Add Applicant'
361#    pnav = 1
362#
363#    @grok.action('Add applicant')
364#    def addApplicant(self, **data):
365#        from waeup.sirp.jambtables.applicants import Applicant
366#        applicant = Applicant()
367#        self.applyData(applicant, **data)
368#        # XXX: temporarily disabled.
369#        #self.context[applicant.reg_no] = applicant
370#        try:
371#            self.context[applicant.access_code] = applicant
372#        except KeyError:
373#            self.flash('The given access code is already in use!')
374#            return
375#        self.redirect(self.url(self.context))
376
377class DisplayApplicant(WAeUPDisplayFormPage):
378    grok.context(IApplicant)
379    grok.name('index')
380    grok.require('waeup.viewApplication')
381    form_fields = grok.AutoFields(IApplicant).omit('locked')
382    form_fields['fst_sit_results'].custom_widget = list_results_display_widget
383    form_fields['passport'].custom_widget = ThumbnailWidget
384    label = 'Applicant'
385    title = 'Applicant'
386    pnav = 3
387
388class EditApplicantStudent(WAeUPEditFormPage):
389    """An applicant-centered edit view for applicant data.
390    """
391    grok.context(IApplicant)
392    grok.name('edit')
393    grok.require('waeup.editApplication')
394    form_fields = grok.AutoFields(IApplicantPDEEditData).omit('locked')
395    form_fields['passport'].custom_widget = EncodingImageFileWidget
396    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget
397    grok.template('form_edit_pde')
398
399    def emitLockMessage(self):
400        self.flash('The requested form is locked (read-only)')
401        self.redirect(self.url(self.context))
402        return
403   
404    def update(self):
405        if self.context.locked:
406            self.emitLockMessage()
407            return
408        datepicker.need() # Enable jQuery datepicker in date fields.
409        super(EditApplicantStudent, self).update()
410        return
411
412    def filteredWidgets(self):
413        for widget in self.widgets:
414            if widget.name == 'form.confirm_passport':
415                continue
416            yield widget
417
418    @property
419    def label(self):
420        # XXX: Use current/upcoming session
421        return 'Apply for Post UDE Screening Test (2009/2010)'
422    title = 'Edit Application'
423    pnav = 3
424
425    @grok.action('Save')
426    def save(self, **data):
427        if self.context.locked:
428            self.emitLockMessage()
429            return
430        self.applyData(self.context, **data)
431        self.context._p_changed = True
432        return
433
434    @grok.action('Final Submit')
435    def finalsubmit(self, **data):
436        if self.context.locked:
437            self.emitLockMessage()
438            return
439        self.applyData(self.context, **data)
440        self.context._p_changed = True
441        if not self.dataComplete():
442            self.flash('Data yet not complete.')
443            return
444        self.context.locked = True
445        return
446
447    def dataComplete(self):
448        if context.confirm_passport is not True:
449            return False
450        if len(self.errors) > 0:
451            return False
452        return True
453
454class EditApplicantFull(WAeUPEditFormPage):
455    """A full edit view for applicant data.
456
457    This one is meant to be used by officers only.
458    """
459    grok.context(IApplicant)
460    grok.name('edit_full')
461    grok.require('waeup.editFullApplication')
462    form_fields = grok.AutoFields(IApplicantPDEEditData).omit('locked')
463    form_fields['passport'].custom_widget = EncodingImageFileWidget
464    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget
465    grok.template('form_edit_full')
466
467    def update(self):
468        if self.context.locked:
469            self.emitLockMessage()
470            return
471        datepicker.need() # Enable jQuery datepicker in date fields.
472        super(EditApplicantFull, self).update()
473        return
474
475    def filteredWidgets(self):
476        for widget in self.widgets:
477            if widget.name == 'form.confirm_passport':
478                continue
479            yield widget
480
481    @property
482    def label(self):
483        return 'Application for %s' % self.context.access_code
484    title = 'Edit Application'
485    pnav = 3
486
487    @grok.action('Save')
488    def save(self, **data):
489        self.applyData(self.context, **data)
490        self.context._p_changed = True
491        return
Note: See TracBrowser for help on using the repository browser.