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
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    )
[5273]50#from zope.formlib.objectwidget import ObjectWidget
51from zope.formlib.sequencewidget import ListSequenceWidget, SequenceDisplayWidget
52from zope.formlib.widget import CustomWidgetFactory
[5850]53from waeup.sirp.utils.helpers import ReST2HTML
[5303]54from waeup.sirp.widgets.objectwidget import (
[5301]55    WAeUPObjectWidget, WAeUPObjectDisplayWidget)
[5303]56from waeup.sirp.widgets.multilistwidget import (
[5273]57    MultiListWidget, MultiListDisplayWidget)
[5686]58from waeup.sirp.image.browser.widget import (
59    ThumbnailWidget, EncodingImageFileWidget,
60    )
[5320]61
[5273]62results_widget = CustomWidgetFactory(
[5301]63    WAeUPObjectWidget, ResultEntry)
[5273]64
65results_display_widget = CustomWidgetFactory(
[5301]66    WAeUPObjectDisplayWidget, ResultEntry)
[5273]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
[5844]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
[5822]83class ApplicationsPage(WAeUPPage):
84    grok.context(IApplicantsRoot)
85    grok.name('index')
86    title = 'Applicants'
[5843]87    pnav = 3
[6012]88
89    def update(self):
90        super(ApplicationsPage, self).update()
91        datatable.need()
92        return
93
[5822]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)
[5861]99            yield(dict(url=url, name=key, container=val))
[5273]100
[5828]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'
[5843]111    pnav = 3
[5828]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
[5822]135class AddApplicantsContainerActionButton(AddActionButton):
136    grok.context(IApplicantsRoot)
[5828]137    grok.view(ApplicantsRootEditPage)
[5822]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'
[5843]145    pnav = 3
[5822]146
[5828]147    def update(self, providername=None, name=None, title=None,
148               description=None, ADD=None, CANCEL=None):
[5822]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...
[5846]161        provider = getUtility(IApplicantsContainerProvider,
[5822]162                              name=providername)
163        container = provider.factory()
[5828]164        if title:
165            container.title = title
166        if description:
167            container.description = description
[5822]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):
[5825]174        """Get a list of applicants container providers.
[5822]175
[5825]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        """
[5846]194        providers = getAllUtilitiesRegisteredFor(IApplicantsContainerProvider)
[5822]195        result = [
196            {'name': getattr(x, 'grokcore.component.directive.name'),
197             'provider': x}
198             for x in providers
199            ]
200        return result
[5828]201
[5845]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)
[5828]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
[5843]221    pnav = 3
[5828]222    tab_title = u'Applicants'
223
224    @property
225    def link_target(self):
226        return self.view.application_url('applicants')
227
[6053]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
[6029]239class ApplicantsContainerPage(WAeUPDisplayFormPage):
[5830]240    """The standard view for regular applicant containers.
241    """
242    grok.context(IApplicantsContainer)
243    grok.name('index')
[6029]244    grok.template('applicantscontainerpage')
[5850]245    pnav = 3
[6053]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
[5837]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
[5830]260
[5850]261    def descriptionToHTML(self):
[6029]262        if self.context.description:
263            return ReST2HTML(self.context.description)
264        else:
265            return
[5850]266
[5832]267class ManageApplicantsContainerActionButton(ManageActionButton):
268    grok.context(IApplicantsContainer)
269    grok.view(ApplicantsContainerPage)
270    text = 'Manage'
271
272
[5837]273class ManageApplicantsContainer(WAeUPEditFormPage):
274    grok.context(IApplicantsContainer)
[5850]275    grok.name('manage')
[5982]276    grok.template('form_manage_applicants_container')
[5837]277    form_fields = grok.AutoFields(IApplicantsContainer)
[5844]278    # Use friendlier date widget...
279    form_fields['startdate'].custom_widget = FriendlyDateWidget
280    form_fields['enddate'].custom_widget = FriendlyDateWidget
[5850]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
[5845]291    pnav = 3
[5837]292
293    def update(self):
[5850]294        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]295        tabs.need()
[6015]296        datatable.need()  # Enable jQurey datatables for contents listing
[5837]297        return super(ManageApplicantsContainer, self).update()
298
[5850]299    @grok.action('Save')
[5837]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
[5886]309
310class LoginApplicant(WAeUPPage):
311    grok.context(IApplicantsContainer)
312    grok.name('login')
313    grok.require('zope.Public')
314
315    title = u'Login'
[5837]316   
[5886]317    @property
318    def label(self):
319        return self.title
320
321    pnav = 3
322    prefix = u'APP'
323   
[5896]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)
[5886]327        if SUBMIT is None:
328            return
329
[5894]330        if self.request.principal.id == 'zope.anybody':
331            self.flash('Entered credentials are invalid')
[5886]332            return
[5894]333
334        if not IApplicantPrincipal.providedBy(self.request.principal):
335            # Don't care if user is already authenticated as non-applicant
336            return
337
[5905]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
[5937]344           
345        # Assign current principal the owner role on created applicant
346        # record
347        role_manager = IPrincipalRoleManager(self.context)
348        role_manager.assignRoleToPrincipal(
[6043]349            'waeup.local.ApplicationOwner', self.request.principal.id)
[5937]350        self.redirect(self.url(self.context[pin], 'edit'))
[5886]351        return
352
[5758]353#class AddApplicant(WAeUPAddFormPage):
[5846]354#    grok.context(IApplicantsContainer)
[5758]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))
[5273]376
377class DisplayApplicant(WAeUPDisplayFormPage):
378    grok.context(IApplicant)
379    grok.name('index')
[5937]380    grok.require('waeup.viewApplication')
[5941]381    form_fields = grok.AutoFields(IApplicant).omit('locked')
[5273]382    form_fields['fst_sit_results'].custom_widget = list_results_display_widget
[5919]383    form_fields['passport'].custom_widget = ThumbnailWidget
[5273]384    label = 'Applicant'
385    title = 'Applicant'
[5843]386    pnav = 3
[5273]387
[5982]388class EditApplicantStudent(WAeUPEditFormPage):
389    """An applicant-centered edit view for applicant data.
390    """
[5273]391    grok.context(IApplicant)
392    grok.name('edit')
[5937]393    grok.require('waeup.editApplication')
[5941]394    form_fields = grok.AutoFields(IApplicantPDEEditData).omit('locked')
[5686]395    form_fields['passport'].custom_widget = EncodingImageFileWidget
[6040]396    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget
[5488]397    grok.template('form_edit_pde')
[5484]398
[5941]399    def emitLockMessage(self):
400        self.flash('The requested form is locked (read-only)')
401        self.redirect(self.url(self.context))
402        return
403   
[5686]404    def update(self):
[5941]405        if self.context.locked:
406            self.emitLockMessage()
407            return
[6040]408        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]409        super(EditApplicantStudent, self).update()
[5686]410        return
[5952]411
412    def filteredWidgets(self):
413        for widget in self.widgets:
414            if widget.name == 'form.confirm_passport':
415                continue
416            yield widget
417
[5484]418    @property
419    def label(self):
420        # XXX: Use current/upcoming session
421        return 'Apply for Post UDE Screening Test (2009/2010)'
[5273]422    title = 'Edit Application'
[5845]423    pnav = 3
[5273]424
425    @grok.action('Save')
426    def save(self, **data):
[5941]427        if self.context.locked:
428            self.emitLockMessage()
429            return
[5273]430        self.applyData(self.context, **data)
431        self.context._p_changed = True
432        return
433
[5484]434    @grok.action('Final Submit')
435    def finalsubmit(self, **data):
[5941]436        if self.context.locked:
437            self.emitLockMessage()
438            return
[5273]439        self.applyData(self.context, **data)
[5484]440        self.context._p_changed = True
[5941]441        if not self.dataComplete():
442            self.flash('Data yet not complete.')
443            return
444        self.context.locked = True
[5273]445        return
[5941]446
447    def dataComplete(self):
[5952]448        if context.confirm_passport is not True:
[5941]449            return False
450        if len(self.errors) > 0:
451            return False
452        return True
[5982]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
[6041]464    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget
[5982]465    grok.template('form_edit_full')
466
467    def update(self):
468        if self.context.locked:
469            self.emitLockMessage()
470            return
[6041]471        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]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.