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

Last change on this file since 6556 was 6476, checked in by Henrik Bettermann, 14 years ago

Remove application_date from schema. This date should not be displayed on forms and slips since we have the same information in the object history. It's needed in export files for sorting and filtering.

File size: 28.5 KB
RevLine 
[5273]1##
2## browser.py
3## Login : <uli@pu.smp.net>
[6153]4## Started on  Sun Jun 27 11:03:10 2010 Uli Fouquet & Henrik Bettermann
[5273]5## $Id$
[6078]6##
[6063]7## Copyright (C) 2010 Uli Fouquet & Henrik Bettermann
[5273]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.
[6078]12##
[5273]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.
[6078]17##
[5273]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"""
[6082]24import sys
[5273]25import grok
26
[6305]27from datetime import datetime
[6081]28from zope.formlib.widget import CustomWidgetFactory
[6358]29from zope.formlib.form import setUpEditWidgets
[5937]30from zope.securitypolicy.interfaces import IPrincipalRoleManager
[6153]31from zope.traversing.browser import absoluteURL
[6081]32
[6303]33from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
[6357]34from reportlab.pdfgen import canvas
[6364]35from reportlab.lib.units import cm
[6390]36from reportlab.lib.pagesizes import A4
[6364]37from reportlab.lib.styles import getSampleStyleSheet
38from reportlab.platypus import (Frame, Paragraph, Image,
39    Table, Spacer)
40from reportlab.platypus.tables import TableStyle
[6357]41
[6469]42from waeup.sirp.accesscodes import invalidate_accesscode, get_access_code
43from waeup.sirp.accesscodes.workflow import USED
[5273]44from waeup.sirp.browser import (
[6321]45    WAeUPPage, WAeUPEditFormPage, WAeUPAddFormPage, WAeUPDisplayFormPage)
[6081]46from waeup.sirp.browser.breadcrumbs import Breadcrumb
[6103]47from waeup.sirp.browser.layout import NullValidator
[6321]48from waeup.sirp.browser.pages import add_local_role, del_local_roles
[6013]49from waeup.sirp.browser.resources import datepicker, tabs, datatable
[6153]50from waeup.sirp.browser.viewlets import (
51    ManageActionButton, PrimaryNavTab, LeftSidebarLink
52    )
[6081]53from waeup.sirp.image.browser.widget import (
54    ThumbnailWidget, EncodingImageFileWidget,
[5822]55    )
[6184]56from waeup.sirp.interfaces import IWAeUPObject, ILocalRolesAssignable
[6321]57from waeup.sirp.permissions import get_users_with_local_roles
[6254]58from waeup.sirp.university.interfaces import ICertificate
[6054]59from waeup.sirp.widgets.datewidget import (
60    FriendlyDateWidget, FriendlyDateDisplayWidget)
[6084]61from waeup.sirp.widgets.restwidget import ReSTDisplayWidget
[5303]62from waeup.sirp.widgets.objectwidget import (
[5301]63    WAeUPObjectWidget, WAeUPObjectDisplayWidget)
[5303]64from waeup.sirp.widgets.multilistwidget import (
[5273]65    MultiListWidget, MultiListDisplayWidget)
[6153]66from waeup.sirp.applicants import ResultEntry, Applicant, get_applicant_data
[6081]67from waeup.sirp.applicants.interfaces import (
[6321]68    IApplicant, IApplicantPrincipal,IApplicantEdit, IApplicantsRoot,
69    IApplicantsContainer, IApplicantsContainerAdd, application_types_vocab
[5686]70    )
[6353]71from waeup.sirp.applicants.workflow import INITIALIZED, STARTED
[5320]72
[5273]73results_widget = CustomWidgetFactory(
[5301]74    WAeUPObjectWidget, ResultEntry)
[5273]75
76results_display_widget = CustomWidgetFactory(
[5301]77    WAeUPObjectDisplayWidget, ResultEntry)
[5273]78
79list_results_widget = CustomWidgetFactory(
80    MultiListWidget, subwidget=results_widget)
81
82list_results_display_widget = CustomWidgetFactory(
83    MultiListDisplayWidget, subwidget=results_display_widget)
84
[6353]85#TRANSITION_OBJECTS = create_workflow()
[6305]86
[6322]87#TRANSITION_DICT = dict([
88#    (transition_object.transition_id,transition_object.title)
89#    for transition_object in TRANSITION_OBJECTS])
[6305]90
[6067]91class ApplicantsRootPage(WAeUPPage):
[5822]92    grok.context(IApplicantsRoot)
93    grok.name('index')
[6153]94    grok.require('waeup.Public')
[5822]95    title = 'Applicants'
[6069]96    label = 'Application Section'
[5843]97    pnav = 3
[6012]98
99    def update(self):
[6067]100        super(ApplicantsRootPage, self).update()
[6012]101        datatable.need()
102        return
103
[5828]104class ManageApplicantsRootActionButton(ManageActionButton):
105    grok.context(IApplicantsRoot)
[6067]106    grok.view(ApplicantsRootPage)
[6198]107    grok.require('waeup.manageApplications')
[6069]108    text = 'Manage application section'
[5828]109
[6069]110class ApplicantsRootManageFormPage(WAeUPEditFormPage):
[5828]111    grok.context(IApplicantsRoot)
112    grok.name('manage')
[6107]113    grok.template('applicantsrootmanagepage')
[6069]114    title = 'Applicants'
115    label = 'Manage application section'
[5843]116    pnav = 3
[6198]117    grok.require('waeup.manageApplications')
[6069]118    taboneactions = ['Add applicants container', 'Remove selected','Cancel']
[6184]119    tabtwoactions1 = ['Remove selected local roles']
120    tabtwoactions2 = ['Add local role']
[6069]121    subunits = 'Applicants Containers'
[6078]122
[6069]123    def update(self):
124        tabs.need()
[6108]125        datatable.need()
[6069]126        return super(ApplicantsRootManageFormPage, self).update()
[5828]127
[6184]128    def getLocalRoles(self):
129        roles = ILocalRolesAssignable(self.context)
130        return roles()
131
132    def getUsers(self):
133        """Get a list of all users.
134        """
135        for key, val in grok.getSite()['users'].items():
136            url = self.url(val)
137            yield(dict(url=url, name=key, val=val))
138
139    def getUsersWithLocalRoles(self):
140        return get_users_with_local_roles(self.context)
141
[6069]142    # ToDo: Show warning message before deletion
143    @grok.action('Remove selected')
144    def delApplicantsContainers(self, **data):
145        form = self.request.form
146        child_id = form['val_id']
147        if not isinstance(child_id, list):
148            child_id = [child_id]
149        deleted = []
150        for id in child_id:
151            try:
152                del self.context[id]
153                deleted.append(id)
154            except:
155                self.flash('Could not delete %s: %s: %s' % (
156                        id, sys.exc_info()[0], sys.exc_info()[1]))
157        if len(deleted):
158            self.flash('Successfully removed: %s' % ', '.join(deleted))
[6078]159        self.redirect(self.url(self.context, '@@manage')+'#tab-1')
160        return
[5828]161
[6069]162    @grok.action('Add applicants container', validator=NullValidator)
163    def addApplicantsContainer(self, **data):
164        self.redirect(self.url(self.context, '@@add'))
[6078]165        return
166
[6069]167    @grok.action('Cancel', validator=NullValidator)
168    def cancel(self, **data):
169        self.redirect(self.url(self.context))
[6078]170        return
171
[6184]172    @grok.action('Add local role', validator=NullValidator)
173    def addLocalRole(self, **data):
174        return add_local_role(self,2, **data)
175
176    @grok.action('Remove selected local roles')
177    def delLocalRoles(self, **data):
178        return del_local_roles(self,2,**data)
179
[6069]180class ApplicantsContainerAddFormPage(WAeUPAddFormPage):
[5822]181    grok.context(IApplicantsRoot)
[6198]182    grok.require('waeup.manageApplications')
[5822]183    grok.name('add')
[6107]184    grok.template('applicantscontaineraddpage')
[6069]185    title = 'Applicants'
186    label = 'Add applicants container'
[5843]187    pnav = 3
[6078]188
[6103]189    form_fields = grok.AutoFields(
190        IApplicantsContainerAdd).omit('code').omit('title')
[6083]191    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
192    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
[6078]193
[6083]194    def update(self):
195        datepicker.need() # Enable jQuery datepicker in date fields.
196        return super(ApplicantsContainerAddFormPage, self).update()
197
[6069]198    @grok.action('Add applicants container')
199    def addApplicantsContainer(self, **data):
[6103]200        year = data['year']
201        code = u'%s%s' % (data['prefix'], year)
202        prefix = application_types_vocab.getTerm(data['prefix'])
203        title = u'%s %s/%s' % (prefix.title, year, year + 1)
[6087]204        if code in self.context.keys():
[6105]205            self.flash(
206                'An applicants container for the same application '
207                'type and entrance year exists already in the database.')
[5822]208            return
209        # Add new applicants container...
[6083]210        provider = data['provider'][1]
[5822]211        container = provider.factory()
[6069]212        self.applyData(container, **data)
[6087]213        container.code = code
214        container.title = title
215        self.context[code] = container
[6105]216        self.flash('Added: "%s".' % code)
[6069]217        self.redirect(self.url(self.context, u'@@manage')+'#tab-1')
[5822]218        return
[6078]219
[6103]220    @grok.action('Cancel', validator=NullValidator)
[6069]221    def cancel(self, **data):
[6103]222        self.redirect(self.url(self.context, '@@manage') + '#tab-1')
[6078]223
[5845]224class ApplicantsRootBreadcrumb(Breadcrumb):
225    """A breadcrumb for applicantsroot.
226    """
227    grok.context(IApplicantsRoot)
[6153]228    title = u'Application Section'
[6078]229
[5845]230class ApplicantsContainerBreadcrumb(Breadcrumb):
231    """A breadcrumb for applicantscontainers.
232    """
233    grok.context(IApplicantsContainer)
[6319]234
[6153]235class ApplicantBreadcrumb(Breadcrumb):
236    """A breadcrumb for applicants.
237    """
238    grok.context(IApplicant)
[6319]239
[6153]240    @property
241    def title(self):
242        """Get a title for a context.
243        """
244        return self.context.access_code
[5828]245
246class ApplicantsTab(PrimaryNavTab):
[6153]247    """Applicants tab in primary navigation.
[5828]248    """
[6078]249
[5828]250    grok.context(IWAeUPObject)
251    grok.order(3)
[6336]252    grok.require('waeup.Public')
[5828]253    grok.template('primarynavtab')
254
[5843]255    pnav = 3
[5828]256    tab_title = u'Applicants'
257
258    @property
259    def link_target(self):
260        return self.view.application_url('applicants')
261
[6029]262class ApplicantsContainerPage(WAeUPDisplayFormPage):
[5830]263    """The standard view for regular applicant containers.
264    """
265    grok.context(IApplicantsContainer)
266    grok.name('index')
[6153]267    grok.require('waeup.Public')
[6029]268    grok.template('applicantscontainerpage')
[5850]269    pnav = 3
[6053]270
[6105]271    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
[6054]272    form_fields['startdate'].custom_widget = FriendlyDateDisplayWidget('le')
273    form_fields['enddate'].custom_widget = FriendlyDateDisplayWidget('le')
[6084]274    form_fields['description'].custom_widget = ReSTDisplayWidget
[6053]275
[5837]276    @property
277    def title(self):
[6087]278        return "Applicants Container: %s" % self.context.title
[5837]279
280    @property
281    def label(self):
[6087]282        return self.context.title
[5830]283
[6107]284class ApplicantsContainerManageActionButton(ManageActionButton):
[6336]285    grok.order(1)
[5832]286    grok.context(IApplicantsContainer)
287    grok.view(ApplicantsContainerPage)
[6198]288    grok.require('waeup.manageApplications')
[6070]289    text = 'Manage applicants container'
[5832]290
[6336]291class LoginApplicantActionButton(ManageActionButton):
292    grok.order(2)
293    grok.context(IApplicantsContainer)
294    grok.view(ApplicantsContainerPage)
295    grok.require('waeup.Anonymous')
296    icon = 'login.png'
297    text = 'Login for applicants'
298    target = 'login'
[5832]299
[6107]300class ApplicantsContainerManageFormPage(WAeUPEditFormPage):
[5837]301    grok.context(IApplicantsContainer)
[5850]302    grok.name('manage')
[6107]303    grok.template('applicantscontainermanagepage')
[6105]304    form_fields = grok.AutoFields(IApplicantsContainer).omit('title')
305    taboneactions = ['Save','Cancel']
306    tabtwoactions = ['Add applicant', 'Remove selected','Cancel']
[6184]307    tabthreeactions1 = ['Remove selected local roles']
308    tabthreeactions2 = ['Add local role']
[5844]309    # Use friendlier date widget...
[6054]310    form_fields['startdate'].custom_widget = FriendlyDateWidget('le')
311    form_fields['enddate'].custom_widget = FriendlyDateWidget('le')
[6198]312    grok.require('waeup.manageApplications')
[5850]313
314    @property
315    def title(self):
[6087]316        return "Applicants Container: %s" % self.context.title
[6078]317
[5850]318    @property
319    def label(self):
[6087]320        return 'Manage applicants container'
[5850]321
[5845]322    pnav = 3
[5837]323
324    def update(self):
[5850]325        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]326        tabs.need()
[6015]327        datatable.need()  # Enable jQurey datatables for contents listing
[6107]328        return super(ApplicantsContainerManageFormPage, self).update()
[5837]329
[6184]330    def getLocalRoles(self):
331        roles = ILocalRolesAssignable(self.context)
332        return roles()
333
334    def getUsers(self):
335        """Get a list of all users.
336        """
337        for key, val in grok.getSite()['users'].items():
338            url = self.url(val)
339            yield(dict(url=url, name=key, val=val))
340
341    def getUsersWithLocalRoles(self):
342        return get_users_with_local_roles(self.context)
343
[5850]344    @grok.action('Save')
[5837]345    def apply(self, **data):
346        self.applyData(self.context, **data)
347        self.flash('Data saved.')
348        return
[6078]349
[6105]350    # ToDo: Show warning message before deletion
351    @grok.action('Remove selected')
352    def delApplicant(self, **data):
[6189]353        form = self.request.form
354        if form.has_key('val_id'):
355            child_id = form['val_id']
356        else:
357            self.flash('No applicant selected!')
358            self.redirect(self.url(self.context, '@@manage')+'#tab-2')
359            return
360        if not isinstance(child_id, list):
361            child_id = [child_id]
362        deleted = []
363        for id in child_id:
364            try:
365                del self.context[id]
366                deleted.append(id)
367            except:
368                self.flash('Could not delete %s: %s: %s' % (
369                        id, sys.exc_info()[0], sys.exc_info()[1]))
370        if len(deleted):
371            self.flash('Successfully removed: %s' % ', '.join(deleted))
372        self.redirect(self.url(self.context, u'@@manage')+'#tab-2')
373        return
[6105]374
375    @grok.action('Add applicant', validator=NullValidator)
376    def addApplicant(self, **data):
[6327]377        self.redirect(self.url(self.context, 'addapplicant'))
378        return
[6105]379
380    @grok.action('Cancel', validator=NullValidator)
[5837]381    def cancel(self, **data):
382        self.redirect(self.url(self.context))
383        return
[5886]384
[6184]385    @grok.action('Add local role', validator=NullValidator)
386    def addLocalRole(self, **data):
387        return add_local_role(self,3, **data)
[6105]388
[6184]389    @grok.action('Remove selected local roles')
390    def delLocalRoles(self, **data):
391        return del_local_roles(self,3,**data)
392
[5886]393class LoginApplicant(WAeUPPage):
394    grok.context(IApplicantsContainer)
395    grok.name('login')
[6153]396    grok.require('waeup.Public')
[5886]397
[6110]398    @property
399    def title(self):
400        return u"Applicant Login: %s" % self.context.title
[6078]401
[5886]402    @property
403    def label(self):
[6110]404        return u'Login for applicants only'
[5886]405
406    pnav = 3
[6319]407
[6110]408    @property
409    def ac_prefix(self):
410        return self.context.ac_prefix
[6078]411
[5896]412    def update(self, SUBMIT=None):
413        self.ac_series = self.request.form.get('form.ac_series', None)
414        self.ac_number = self.request.form.get('form.ac_number', None)
[5886]415        if SUBMIT is None:
416            return
[5894]417        if self.request.principal.id == 'zope.anybody':
[6105]418            self.flash('Entered credentials are invalid.')
[5886]419            return
[5894]420        if not IApplicantPrincipal.providedBy(self.request.principal):
421            # Don't care if user is already authenticated as non-applicant
422            return
[6377]423
424        # From here we handle an applicant (not an officer browsing)
[5905]425        pin = self.request.principal.access_code
[6375]426
427        # Mark pin as used (this also fires a pin related transition)
[6470]428        if get_access_code(pin).state == USED:
[6469]429            pass
430        else:
[6471]431            comment = u"AC invalidated"
[6469]432            # Here we know that the ac is in state initialized so we do not
433            # expect an exception
[6471]434            invalidate_accesscode(pin,comment)
[6375]435
[6377]436        if not pin in self.context.keys():
437            # Create applicant record
438            applicant = Applicant()
439            applicant.access_code = pin
440            self.context[pin] = applicant
[6359]441
[6377]442        role_manager = IPrincipalRoleManager(self.context[pin])
[6394]443        role_manager.assignRoleToPrincipal(
444            'waeup.local.ApplicationOwner', self.request.principal.id)
[6359]445
[6394]446        # Assign current principal the PortalUser role
[6397]447        role_manager = IPrincipalRoleManager(grok.getSite())
[6394]448        role_manager.assignRoleToPrincipal(
449            'waeup.PortalUser', self.request.principal.id)
[6377]450
451        # Mark application as started
452        if IWorkflowState(self.context[pin]).getState() is INITIALIZED:
453            IWorkflowInfo(self.context[pin]).fireTransition('start')
454
[5937]455        self.redirect(self.url(self.context[pin], 'edit'))
[5886]456        return
[6319]457
[6327]458class ApplicantAddFormPage(WAeUPAddFormPage):
459    """Add-form to add certificate to a department.
460    """
461    grok.context(IApplicantsContainer)
462    grok.require('waeup.manageApplications')
463    grok.name('addapplicant')
464    grok.template('applicantaddpage')
465    title = 'Applicants'
466    label = 'Add applicant'
467    pnav = 3
468
469    @property
470    def title(self):
471        return "Applicants Container: %s" % self.context.title
472
473    @property
474    def ac_prefix(self):
475        return self.context.ac_prefix
476
477    @grok.action('Create application record')
478    def addApplicant(self, **data):
479        ac_series = self.request.form.get('form.ac_series', None)
480        ac_number = self.request.form.get('form.ac_number', None)
481        pin = '%s-%s-%s' % (self.ac_prefix,ac_series,ac_number)
[6459]482        if not invalidate_accesscode(pin, comment=u"Invalidated by system"):
[6383]483            self.flash('%s is not a valid access code.' % pin)
484            self.redirect(self.url(self.context, '@@manage')+'#tab-2')
485            return
486        else:
[6327]487            # Create applicant record
488            applicant = Applicant()
489            applicant.access_code = pin
490            self.context[pin] = applicant
491        self.redirect(self.url(self.context[pin], 'edit'))
492        return
493
[6396]494class AccessCodeViewLink(LeftSidebarLink):
[6153]495    grok.order(1)
496    grok.require('waeup.Public')
[6396]497    icon = 'actionicon_view.png'
498    title = 'View Record'
499    target = '/@@index'
[5886]500
[6396]501    @property
502    def url(self):
[6153]503        if not IApplicantPrincipal.providedBy(self.request.principal):
504            return ''
505        access_code = getattr(self.request.principal,'access_code',None)
506        if access_code:
507            applicant_object = get_applicant_data(access_code)
[6396]508            return absoluteURL(applicant_object, self.request) + self.target
509        else:
510            return ''
[6153]511
[6396]512class AccessCodeEditLink(AccessCodeViewLink):
513    grok.order(2)
514    grok.require('waeup.Public')
515    icon = 'actionicon_modify.png'
516    title = 'Edit Record'
517    target = '/@@edit'
518
519    @property
520    def url(self):
521        if not IApplicantPrincipal.providedBy(self.request.principal):
522            return ''
523        access_code = getattr(self.request.principal,'access_code',None)
524        if access_code:
525            applicant_object = get_applicant_data(access_code)
526            if applicant_object.locked:
527                return ''
528            return absoluteURL(applicant_object, self.request) + self.target
529        else:
530            return ''
531
532class AccessCodeSlipLink(AccessCodeViewLink):
533    grok.order(3)
534    grok.require('waeup.Public')
535    icon = 'actionicon_pdf.png'
536    title = 'Download Slip'
537    target = '/application_slip.pdf'
538
[5273]539class DisplayApplicant(WAeUPDisplayFormPage):
540    grok.context(IApplicant)
541    grok.name('index')
[6198]542    grok.require('waeup.handleApplication')
[6320]543    form_fields = grok.AutoFields(IApplicant).omit(
544        'locked').omit('course_admitted')
[6196]545    #form_fields['fst_sit_results'].custom_widget = list_results_display_widget
[5919]546    form_fields['passport'].custom_widget = ThumbnailWidget
[6054]547    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
[5273]548    label = 'Applicant'
[6254]549    grok.template('form_display')
[5843]550    pnav = 3
[5273]551
[6196]552    @property
553    def title(self):
[6465]554        if self.request.principal.title == 'Applicant':
555            return u'Your Application Record'
[6196]556        return '%s' % self.context.access_code
557
558    @property
559    def label(self):
560        container_title = self.context.__parent__.title
561        return '%s Application Record' % container_title
562
[6254]563    def getCourseAdmitted(self):
[6355]564        """Return link, title and code in html format to the certificate
565           admitted.
[6351]566        """
[6254]567        course_admitted = self.context.course_admitted
568        if ICertificate.providedBy(course_admitted):
569            url = self.url(course_admitted)
570            title = course_admitted.title
571            code = course_admitted.code
[6366]572            return '<a href="%s">%s - %s</a>' %(url,code,title)
[6457]573        return ''
[6254]574
[6358]575class PDFActionButton(ManageActionButton):
576    grok.context(IApplicant)
[6396]577    #grok.view(DisplayApplicant)
578    grok.require('waeup.manageApplications')
[6358]579    icon = 'actionicon_pdf.png'
[6367]580    text = 'Download application slip'
[6358]581    target = 'application_slip.pdf'
582
583class ExportPDFPage(grok.View):
584    """Deliver a PDF slip of the context.
585    """
586    grok.context(IApplicant)
587    grok.name('application_slip.pdf')
588    grok.require('waeup.handleApplication')
589    form_fields = grok.AutoFields(IApplicant).omit(
590        'locked').omit('course_admitted')
591    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
592    prefix = 'form'
593
[6363]594    @property
595    def label(self):
596        container_title = self.context.__parent__.title
597        return '%s Application Record' % container_title
598
[6358]599    def getCourseAdmitted(self):
600        """Return title and code in html format to the certificate
601           admitted.
602        """
603        course_admitted = self.context.course_admitted
604        if ICertificate.providedBy(course_admitted):
605            title = course_admitted.title
606            code = course_admitted.code
[6365]607            return '%s - %s' %(code,title)
[6462]608        return ''
[6358]609
610    def setUpWidgets(self, ignore_request=False):
611        self.adapters = {}
612        self.widgets = setUpEditWidgets(
613            self.form_fields, self.prefix, self.context, self.request,
614            adapters=self.adapters, for_display=True,
615            ignore_request=ignore_request
616            )
617
618    def render(self):
[6364]619        # (0,0),(-1,-1) = whole table
620        # (0,0),(0,-1) = first column
621        # (-1,0),(-1,-1) = last column
622        # (0,0),(-1,0) = first row
623        # (0,-1),(-1,-1) = last row
624        SLIP_STYLE = TableStyle(
625            [('VALIGN',(0,0),(-1,-1),'TOP')]
626            )
[6358]627
628        pdf = canvas.Canvas('application_slip.pdf',pagesize=A4)
[6364]629        pdf.setTitle(self.label)
630        pdf.setSubject('Application')
631        pdf.setAuthor('%s (%s)' % (self.request.principal.title,
632            self.request.principal.id))
633        pdf.setCreator('WAeUP SIRP')
[6358]634        width, height = A4
635        style = getSampleStyleSheet()
[6365]636        pdf.line(1*cm,height-(1.8*cm),width-(1*cm),height-(1.8*cm))
[6363]637
[6358]638        story = []
[6365]639        frame_header = Frame(1*cm,1*cm,width-(1.7*cm),height-(1.7*cm))
[6363]640        header_title = getattr(grok.getSite(), 'name', u'Sample University')
641        story.append(Paragraph(header_title, style["Heading1"]))
642        frame_header.addFromList(story,pdf)
643
644        story = []
[6365]645        frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(3.5*cm))
[6364]646        story.append(Paragraph(self.label, style["Heading2"]))
647        #story.append(HRFlowable())
648        story.append(Spacer(1, 18))
649        for msg in self.context.history.messages:
650            f_msg = '<font face="Courier" size=10>%s</font>' % msg
651            story.append(Paragraph(f_msg, style["Normal"]))
[6363]652        story.append(Spacer(1, 24))
[6358]653        self.setUpWidgets()
[6363]654        data = []
[6358]655        for widget in self.widgets:
[6462]656            f_label = '<font size=12>%s</font>:' % widget.label.strip()
[6363]657            f_label = Paragraph(f_label, style["Normal"])
[6358]658            if widget.name != 'form.passport':
[6363]659                f_text = '<font size=12>%s</font>' % widget()
660                f_text = Paragraph(f_text, style["Normal"])
661                data.append([f_label,f_text])
[6360]662            else:
663                filename = widget._data.file.name
[6364]664                im = Image(filename,width=4*cm, height=3*cm,kind='bound')
[6363]665                data.append([f_label,im])
[6462]666        f_label = '<font size=12>Admitted Course of Study:</font>'
[6363]667        f_text = '<font size=12>%s</font>' % self.getCourseAdmitted()
668        f_label = Paragraph(f_label, style["Normal"])
669        f_text = Paragraph(f_text, style["Normal"])
670        data.append([f_label,f_text])
[6364]671        table = Table(data,style=SLIP_STYLE)
[6363]672        story.append(table)
673        frame_body.addFromList(story,pdf)
674
[6364]675        story = []
[6365]676        frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)
[6364]677        timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
678        f_text = '<font size=10>%s</font>' % timestamp
679        story.append(Paragraph(f_text, style["Normal"]))
680        frame_footer.addFromList(story,pdf)
681
[6358]682        self.response.setHeader(
683            'Content-Type', 'application/pdf')
684        return pdf.getpdfdata()
685
[6383]686class ApplicantManageActionButton(ManageActionButton):
[6198]687    grok.context(IApplicant)
688    grok.view(DisplayApplicant)
689    grok.require('waeup.manageApplications')
[6383]690    text = 'Manage application record'
[6198]691    target = 'edit_full'
692
[6196]693class EditApplicantFull(WAeUPEditFormPage):
694    """A full edit view for applicant data.
695    """
696    grok.context(IApplicant)
697    grok.name('edit_full')
[6198]698    grok.require('waeup.manageApplications')
[6476]699    form_fields = grok.AutoFields(IApplicant)
[6196]700    form_fields['passport'].custom_widget = EncodingImageFileWidget
701    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
702    grok.template('form_edit')
[6322]703    manage_applications = True
[6196]704    pnav = 3
705
706    def update(self):
707        datepicker.need() # Enable jQuery datepicker in date fields.
708        super(EditApplicantFull, self).update()
[6353]709        self.wf_info = IWorkflowInfo(self.context)
[6196]710        return
711
712    @property
713    def title(self):
714        return self.context.access_code
715
716    @property
717    def label(self):
718        container_title = self.context.__parent__.title
719        return '%s Application Form' % container_title
720
[6303]721    def getTransitions(self):
[6351]722        """Return a list of dicts of allowed transition ids and titles.
[6353]723
724        Each list entry provides keys ``name`` and ``title`` for
725        internal name and (human readable) title of a single
726        transition.
[6349]727        """
[6353]728        allowed_transitions = self.wf_info.getManualTransitions()
[6355]729        return [dict(name='', title='No transition')] +[
730            dict(name=x, title=y) for x, y in allowed_transitions]
[6303]731
[6196]732    @grok.action('Save')
733    def save(self, **data):
[6475]734        #import pdb; pdb.set_trace()
735        changed_fields = self.applyData(self.context, **data)
736        changed_fields = changed_fields.values()
737        fields_string = '+'.join('+'.join('+'.join(str(i) for i in b)) for b in changed_fields)
[6196]738        self.context._p_changed = True
[6303]739        form = self.request.form
740        if form.has_key('transition') and form['transition']:
[6305]741            transition_id = form['transition']
[6353]742            self.wf_info.fireTransition(transition_id)
[6196]743        self.flash('Form has been saved.')
[6475]744        ob_class = self.__implemented__.__name__.replace('waeup.sirp.','')
745        self.context.loggerInfo(ob_class, 'saved: % s' % fields_string)
[6196]746        return
747
748class EditApplicantStudent(EditApplicantFull):
[5982]749    """An applicant-centered edit view for applicant data.
750    """
[6196]751    grok.context(IApplicantEdit)
[5273]752    grok.name('edit')
[6198]753    grok.require('waeup.handleApplication')
[6459]754    form_fields = grok.AutoFields(IApplicantEdit).omit(
[6476]755        'locked', 'course_admitted', 'student_id',
[6459]756        'screening_score',
757        )
[5686]758    form_fields['passport'].custom_widget = EncodingImageFileWidget
[6054]759    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
[6196]760    grok.template('form_edit')
[6322]761    manage_applications = False
[6465]762    title = u'Your Application Form'
[5484]763
[6322]764
[5941]765    def emitLockMessage(self):
[6105]766        self.flash('The requested form is locked (read-only).')
[5941]767        self.redirect(self.url(self.context))
768        return
[6078]769
[5686]770    def update(self):
[5941]771        if self.context.locked:
[6367]772            self.emitLockMessage()
[5941]773            return
[6040]774        datepicker.need() # Enable jQuery datepicker in date fields.
[5982]775        super(EditApplicantStudent, self).update()
[5686]776        return
[5952]777
[6196]778    def dataNotComplete(self):
[6322]779        if not self.request.form.get('confirm_passport', False):
[6196]780            return 'Passport confirmation box not ticked.'
781        if len(self.errors) > 0:
782            return 'Form has errors.'
783        return False
[5952]784
[5273]785    @grok.action('Save')
786    def save(self, **data):
[5941]787        if self.context.locked:
788            self.emitLockMessage()
789            return
[5273]790        self.applyData(self.context, **data)
791        self.context._p_changed = True
[6196]792        self.flash('Form has been saved.')
[5273]793        return
794
[5484]795    @grok.action('Final Submit')
796    def finalsubmit(self, **data):
[5941]797        if self.context.locked:
798            self.emitLockMessage()
799            return
[5273]800        self.applyData(self.context, **data)
[5484]801        self.context._p_changed = True
[6196]802        if self.dataNotComplete():
803            self.flash(self.dataNotComplete())
[5941]804            return
[6303]805        state = IWorkflowState(self.context).getState()
[6322]806        # This shouldn't happen, but the application officer
807        # might have forgotten to lock the form after changing the state
[6303]808        if state != STARTED:
[6322]809            self.flash('This form cannot be submitted. Wrong state!')
[6303]810            return
811        IWorkflowInfo(self.context).fireTransition('submit')
[6476]812        self.context.application_date = datetime.now()
[5941]813        self.context.locked = True
[6196]814        self.flash('Form has been submitted.')
815        self.redirect(self.url(self.context))
[5273]816        return
[5941]817
[6367]818class ApplicantViewActionButton(ManageActionButton):
819    grok.context(IApplicant)
[6383]820    grok.view(EditApplicantFull)
[6396]821    grok.require('waeup.manageApplications')
[6383]822    icon = 'actionicon_view.png'
[6367]823    text = 'View application record'
[6396]824    target = 'index'
Note: See TracBrowser for help on using the repository browser.