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

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

Make use of the new datatables in applicants overview page.

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