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

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

First try of using the new datewidgets.

File size: 15.4 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
228class ApplicantsContainerPage(WAeUPDisplayFormPage):
229    """The standard view for regular applicant containers.
230    """
231    grok.context(IApplicantsContainer)
232    grok.name('index')
233    grok.template('applicantscontainerpage')
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        if self.context.description:
247            return ReST2HTML(self.context.description)
248        else:
249            return
250
251class ManageApplicantsContainerActionButton(ManageActionButton):
252    grok.context(IApplicantsContainer)
253    grok.view(ApplicantsContainerPage)
254    text = 'Manage'
255
256from waeup.sirp.widgets.datewidget import FormattedDateWidget
257FriendlyDateWidget = CustomWidgetFactory(
258    FormattedDateWidget,
259    cssClass='datepicker-le-year',
260    date_format='%d/%m/%Y')
261
262
263class ManageApplicantsContainer(WAeUPEditFormPage):
264    grok.context(IApplicantsContainer)
265    grok.name('manage')
266    grok.template('form_manage_applicants_container')
267    form_fields = grok.AutoFields(IApplicantsContainer)
268    # Use friendlier date widget...
269    form_fields['startdate'].custom_widget = FriendlyDateWidget
270    form_fields['enddate'].custom_widget = FriendlyDateWidget
271
272    @property
273    def title(self):
274        return "Manage applicants container: %s" % getattr(
275            self.context, '__name__', 'unnamed')
276   
277    @property
278    def label(self):
279        return self.title
280
281    pnav = 3
282
283    def update(self):
284        datepicker.need() # Enable jQuery datepicker in date fields.
285        tabs.need()
286        datatable.need()  # Enable jQurey datatables for contents listing
287        return super(ManageApplicantsContainer, self).update()
288
289    @grok.action('Save')
290    def apply(self, **data):
291        self.applyData(self.context, **data)
292        self.flash('Data saved.')
293        return
294       
295    @grok.action('Back')
296    def cancel(self, **data):
297        self.redirect(self.url(self.context))
298        return
299
300class LoginApplicant(WAeUPPage):
301    grok.context(IApplicantsContainer)
302    grok.name('login')
303    grok.require('zope.Public')
304
305    title = u'Login'
306   
307    @property
308    def label(self):
309        return self.title
310
311    pnav = 3
312    prefix = u'APP'
313   
314    def update(self, SUBMIT=None):
315        self.ac_series = self.request.form.get('form.ac_series', None)
316        self.ac_number = self.request.form.get('form.ac_number', None)
317        if SUBMIT is None:
318            return
319
320        if self.request.principal.id == 'zope.anybody':
321            self.flash('Entered credentials are invalid')
322            return
323
324        if not IApplicantPrincipal.providedBy(self.request.principal):
325            # Don't care if user is already authenticated as non-applicant
326            return
327
328        pin = self.request.principal.access_code
329        if pin not in self.context.keys():
330            # Create applicant record
331            applicant = Applicant()
332            applicant.access_code = pin
333            self.context[pin] = applicant
334           
335        # Assign current principal the owner role on created applicant
336        # record
337        role_manager = IPrincipalRoleManager(self.context)
338        role_manager.assignRoleToPrincipal(
339            'waeup.local.ApplicationOwner', self.request.principal.id)
340        self.redirect(self.url(self.context[pin], 'edit'))
341        return
342
343#class AddApplicant(WAeUPAddFormPage):
344#    grok.context(IApplicantsContainer)
345#    grok.name('add')
346#    form_fields = grok.AutoFields(IApplicant)
347#    form_fields['fst_sit_results'].custom_widget = list_results_widget
348#    form_fields['passport'].custom_widget = EncodingImageFileWidget
349#    label = 'Add Applicant'
350#    title = 'Add Applicant'
351#    pnav = 1
352#
353#    @grok.action('Add applicant')
354#    def addApplicant(self, **data):
355#        from waeup.sirp.jambtables.applicants import Applicant
356#        applicant = Applicant()
357#        self.applyData(applicant, **data)
358#        # XXX: temporarily disabled.
359#        #self.context[applicant.reg_no] = applicant
360#        try:
361#            self.context[applicant.access_code] = applicant
362#        except KeyError:
363#            self.flash('The given access code is already in use!')
364#            return
365#        self.redirect(self.url(self.context))
366
367class DisplayApplicant(WAeUPDisplayFormPage):
368    grok.context(IApplicant)
369    grok.name('index')
370    grok.require('waeup.viewApplication')
371    form_fields = grok.AutoFields(IApplicant).omit('locked')
372    form_fields['fst_sit_results'].custom_widget = list_results_display_widget
373    form_fields['passport'].custom_widget = ThumbnailWidget
374    label = 'Applicant'
375    title = 'Applicant'
376    pnav = 3
377
378class EditApplicantStudent(WAeUPEditFormPage):
379    """An applicant-centered edit view for applicant data.
380    """
381    grok.context(IApplicant)
382    grok.name('edit')
383    grok.require('waeup.editApplication')
384    form_fields = grok.AutoFields(IApplicantPDEEditData).omit('locked')
385    form_fields['passport'].custom_widget = EncodingImageFileWidget
386    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget
387    grok.template('form_edit_pde')
388
389    def emitLockMessage(self):
390        self.flash('The requested form is locked (read-only)')
391        self.redirect(self.url(self.context))
392        return
393   
394    def update(self):
395        if self.context.locked:
396            self.emitLockMessage()
397            return
398        datepicker.need() # Enable jQuery datepicker in date fields.
399        super(EditApplicantStudent, self).update()
400        return
401
402    def filteredWidgets(self):
403        for widget in self.widgets:
404            if widget.name == 'form.confirm_passport':
405                continue
406            yield widget
407
408    @property
409    def label(self):
410        # XXX: Use current/upcoming session
411        return 'Apply for Post UDE Screening Test (2009/2010)'
412    title = 'Edit Application'
413    pnav = 3
414
415    @grok.action('Save')
416    def save(self, **data):
417        if self.context.locked:
418            self.emitLockMessage()
419            return
420        self.applyData(self.context, **data)
421        self.context._p_changed = True
422        return
423
424    @grok.action('Final Submit')
425    def finalsubmit(self, **data):
426        if self.context.locked:
427            self.emitLockMessage()
428            return
429        self.applyData(self.context, **data)
430        self.context._p_changed = True
431        if not self.dataComplete():
432            self.flash('Data yet not complete.')
433            return
434        self.context.locked = True
435        return
436
437    def dataComplete(self):
438        if context.confirm_passport is not True:
439            return False
440        if len(self.errors) > 0:
441            return False
442        return True
443
444class EditApplicantFull(WAeUPEditFormPage):
445    """A full edit view for applicant data.
446
447    This one is meant to be used by officers only.
448    """
449    grok.context(IApplicant)
450    grok.name('edit_full')
451    grok.require('waeup.editFullApplication')
452    form_fields = grok.AutoFields(IApplicantPDEEditData).omit('locked')
453    form_fields['passport'].custom_widget = EncodingImageFileWidget
454    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget
455    grok.template('form_edit_full')
456
457    def update(self):
458        if self.context.locked:
459            self.emitLockMessage()
460            return
461        datepicker.need() # Enable jQuery datepicker in date fields.
462        super(EditApplicantFull, self).update()
463        return
464
465    def filteredWidgets(self):
466        for widget in self.widgets:
467            if widget.name == 'form.confirm_passport':
468                continue
469            yield widget
470
471    @property
472    def label(self):
473        return 'Application for %s' % self.context.access_code
474    title = 'Edit Application'
475    pnav = 3
476
477    @grok.action('Save')
478    def save(self, **data):
479        self.applyData(self.context, **data)
480        self.context._p_changed = True
481        return
Note: See TracBrowser for help on using the repository browser.