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

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

Enable new Friendly Date Widgets and remove trash from here.

File size: 15.5 KB
RevLine 
[5273]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##
[5824]22"""UI components for basic applicants and related components.
[5273]23"""
24import grok
25
[5837]26from datetime import datetime
27from hurry.jquery import jquery
28from hurry.jqueryui import jqueryui
[5822]29from zope.component import getUtility, getAllUtilitiesRegisteredFor
[5844]30from zope.formlib.widgets import FileWidget, DateWidget
[5937]31from zope.securitypolicy.interfaces import IPrincipalRoleManager
[5273]32from waeup.sirp.browser import (
33    WAeUPPage, WAeUPEditFormPage, WAeUPAddFormPage,
34    WAeUPDisplayFormPage, NullValidator)
[5442]35from waeup.sirp.browser.pages import LoginPage
[5320]36from waeup.sirp.interfaces import IWAeUPObject
[6013]37from waeup.sirp.browser.resources import datepicker, tabs, datatable
[5828]38from waeup.sirp.browser.viewlets import (
39    AddActionButton, ManageActionButton, PrimaryNavTab,
40    )
[5845]41from waeup.sirp.browser.breadcrumbs import Breadcrumb
[5910]42from waeup.sirp.applicants import get_applicant_data, ResultEntry, Applicant
[5758]43from waeup.sirp.applicants.interfaces import (
[5822]44    IApplicant, IApplicantPrincipal, IApplicantPDEEditData,
[5846]45    IApplicantsRoot, IApplicantsContainer, IApplicantsContainerProvider,
[5822]46    )
[5686]47from waeup.sirp.widgets.passportwidget import (
48    PassportWidget, PassportDisplayWidget
49    )
[6054]50from waeup.sirp.widgets.datewidget import (
51    FriendlyDateWidget, FriendlyDateDisplayWidget)
52
[5273]53#from zope.formlib.objectwidget import ObjectWidget
54from zope.formlib.sequencewidget import ListSequenceWidget, SequenceDisplayWidget
55from zope.formlib.widget import CustomWidgetFactory
[5850]56from waeup.sirp.utils.helpers import ReST2HTML
[5303]57from waeup.sirp.widgets.objectwidget import (
[5301]58    WAeUPObjectWidget, WAeUPObjectDisplayWidget)
[5303]59from waeup.sirp.widgets.multilistwidget import (
[5273]60    MultiListWidget, MultiListDisplayWidget)
[5686]61from waeup.sirp.image.browser.widget import (
62    ThumbnailWidget, EncodingImageFileWidget,
63    )
[5320]64
[5273]65results_widget = CustomWidgetFactory(
[5301]66    WAeUPObjectWidget, ResultEntry)
[5273]67
68results_display_widget = CustomWidgetFactory(
[5301]69    WAeUPObjectDisplayWidget, ResultEntry)
[5273]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
[5822]80class ApplicationsPage(WAeUPPage):
81    grok.context(IApplicantsRoot)
82    grok.name('index')
83    title = 'Applicants'
[5843]84    pnav = 3
[6012]85
86    def update(self):
87        super(ApplicationsPage, self).update()
88        datatable.need()
89        return
90
[5822]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)
[5861]96            yield(dict(url=url, name=key, container=val))
[5273]97
[5828]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'
[5843]108    pnav = 3
[5828]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
[5822]132class AddApplicantsContainerActionButton(AddActionButton):
133    grok.context(IApplicantsRoot)
[5828]134    grok.view(ApplicantsRootEditPage)
[5822]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'
[5843]142    pnav = 3
[5822]143
[5828]144    def update(self, providername=None, name=None, title=None,
145               description=None, ADD=None, CANCEL=None):
[5822]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...
[5846]158        provider = getUtility(IApplicantsContainerProvider,
[5822]159                              name=providername)
160        container = provider.factory()
[5828]161        if title:
162            container.title = title
163        if description:
164            container.description = description
[5822]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):
[5825]171        """Get a list of applicants container providers.
[5822]172
[5825]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        """
[5846]191        providers = getAllUtilitiesRegisteredFor(IApplicantsContainerProvider)
[5822]192        result = [
193            {'name': getattr(x, 'grokcore.component.directive.name'),
194             'provider': x}
195             for x in providers
196            ]
197        return result
[5828]198
[5845]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)
[5828]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
[5843]218    pnav = 3
[5828]219    tab_title = u'Applicants'
220
221    @property
222    def link_target(self):
223        return self.view.application_url('applicants')
224
[6029]225class ApplicantsContainerPage(WAeUPDisplayFormPage):
[5830]226    """The standard view for regular applicant containers.
227    """
228    grok.context(IApplicantsContainer)
229    grok.name('index')
[6029]230    grok.template('applicantscontainerpage')
[5850]231    pnav = 3
[6053]232
233    form_fields = grok.AutoFields(IApplicantsContainer)
234    # Use friendlier date widget...
[6054]235    form_fields['startdate'].custom_widget = FriendlyDateDisplayWidget('le')
236    form_fields['enddate'].custom_widget = FriendlyDateDisplayWidget('le')
[6053]237
[5837]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
[5830]246
[5850]247    def descriptionToHTML(self):
[6029]248        if self.context.description:
249            return ReST2HTML(self.context.description)
250        else:
251            return
[5850]252
[5832]253class ManageApplicantsContainerActionButton(ManageActionButton):
254    grok.context(IApplicantsContainer)
255    grok.view(ApplicantsContainerPage)
256    text = 'Manage'
257
258
[5837]259class ManageApplicantsContainer(WAeUPEditFormPage):
260    grok.context(IApplicantsContainer)
[5850]261    grok.name('manage')
[5982]262    grok.template('form_manage_applicants_container')
[5837]263    form_fields = grok.AutoFields(IApplicantsContainer)
[5844]264    # Use friendlier date widget...
[6054]265    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
266    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
[5850]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
[5845]277    pnav = 3
[5837]278
279    def update(self):
[5850]280        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]281        tabs.need()
[6015]282        datatable.need()  # Enable jQurey datatables for contents listing
[5837]283        return super(ManageApplicantsContainer, self).update()
284
[5850]285    @grok.action('Save')
[5837]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
[5886]295
296class LoginApplicant(WAeUPPage):
297    grok.context(IApplicantsContainer)
298    grok.name('login')
299    grok.require('zope.Public')
300
301    title = u'Login'
[5837]302   
[5886]303    @property
304    def label(self):
305        return self.title
306
307    pnav = 3
308    prefix = u'APP'
309   
[5896]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)
[5886]313        if SUBMIT is None:
314            return
315
[5894]316        if self.request.principal.id == 'zope.anybody':
317            self.flash('Entered credentials are invalid')
[5886]318            return
[5894]319
320        if not IApplicantPrincipal.providedBy(self.request.principal):
321            # Don't care if user is already authenticated as non-applicant
322            return
323
[5905]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
[5937]330           
331        # Assign current principal the owner role on created applicant
332        # record
333        role_manager = IPrincipalRoleManager(self.context)
334        role_manager.assignRoleToPrincipal(
[6043]335            'waeup.local.ApplicationOwner', self.request.principal.id)
[5937]336        self.redirect(self.url(self.context[pin], 'edit'))
[5886]337        return
338
[5758]339#class AddApplicant(WAeUPAddFormPage):
[5846]340#    grok.context(IApplicantsContainer)
[5758]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))
[5273]362
363class DisplayApplicant(WAeUPDisplayFormPage):
364    grok.context(IApplicant)
365    grok.name('index')
[5937]366    grok.require('waeup.viewApplication')
[5941]367    form_fields = grok.AutoFields(IApplicant).omit('locked')
[5273]368    form_fields['fst_sit_results'].custom_widget = list_results_display_widget
[5919]369    form_fields['passport'].custom_widget = ThumbnailWidget
[6054]370    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
[5273]371    label = 'Applicant'
372    title = 'Applicant'
[5843]373    pnav = 3
[5273]374
[5982]375class EditApplicantStudent(WAeUPEditFormPage):
376    """An applicant-centered edit view for applicant data.
377    """
[5273]378    grok.context(IApplicant)
379    grok.name('edit')
[5937]380    grok.require('waeup.editApplication')
[5941]381    form_fields = grok.AutoFields(IApplicantPDEEditData).omit('locked')
[5686]382    form_fields['passport'].custom_widget = EncodingImageFileWidget
[6054]383    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
[5488]384    grok.template('form_edit_pde')
[5484]385
[5941]386    def emitLockMessage(self):
387        self.flash('The requested form is locked (read-only)')
388        self.redirect(self.url(self.context))
389        return
390   
[5686]391    def update(self):
[5941]392        if self.context.locked:
393            self.emitLockMessage()
394            return
[6040]395        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]396        super(EditApplicantStudent, self).update()
[5686]397        return
[5952]398
399    def filteredWidgets(self):
400        for widget in self.widgets:
401            if widget.name == 'form.confirm_passport':
402                continue
403            yield widget
404
[5484]405    @property
406    def label(self):
407        # XXX: Use current/upcoming session
408        return 'Apply for Post UDE Screening Test (2009/2010)'
[5273]409    title = 'Edit Application'
[5845]410    pnav = 3
[5273]411
412    @grok.action('Save')
413    def save(self, **data):
[5941]414        if self.context.locked:
415            self.emitLockMessage()
416            return
[5273]417        self.applyData(self.context, **data)
418        self.context._p_changed = True
419        return
420
[5484]421    @grok.action('Final Submit')
422    def finalsubmit(self, **data):
[5941]423        if self.context.locked:
424            self.emitLockMessage()
425            return
[5273]426        self.applyData(self.context, **data)
[5484]427        self.context._p_changed = True
[5941]428        if not self.dataComplete():
429            self.flash('Data yet not complete.')
430            return
431        self.context.locked = True
[5273]432        return
[5941]433
434    def dataComplete(self):
[5952]435        if context.confirm_passport is not True:
[5941]436            return False
437        if len(self.errors) > 0:
438            return False
439        return True
[5982]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
[6054]451    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
[5982]452    grok.template('form_edit_full')
453
454    def update(self):
455        if self.context.locked:
456            self.emitLockMessage()
457            return
[6041]458        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]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.