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

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

Enable new Friendly Date Widgets and remove trash from here.

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