Ignore:
Timestamp:
9 Dec 2019, 21:39:51 (5 years ago)
Author:
Henrik Bettermann
Message:

Add file upload facilities for statement of result upload.

Location:
main/kofacustom.iuokada/trunk/src/kofacustom/iuokada/applicants
Files:
2 added
3 edited

Legend:

Unmodified
Added
Removed
  • main/kofacustom.iuokada/trunk/src/kofacustom/iuokada/applicants/applicant.py

    r15563 r15878  
    1919import grok
    2020from zope.interface import implementedBy
     21from zope.component import getUtility
     22from waeup.kofa.interfaces import IExtFileStore
    2123from waeup.kofa.applicants.applicant import ApplicantFactory
    2224from waeup.kofa.utils.helpers import attrs_to_fields
     
    2426from kofacustom.iuokada.applicants.interfaces import(
    2527    ICustomApplicant, ICustomUGApplicantEdit, ICustomPGApplicantEdit, IPUTMEApplicantEdit)
     28
     29@grok.subscribe(ICustomApplicant, grok.IObjectRemovedEvent)
     30def custom_handle_applicant_removed(applicant, event):
     31    """If an applicant is removed also pdf files of this applicant are removed.
     32    """
     33    file_store = getUtility(IExtFileStore)
     34    file_store.deleteFileByContext(applicant, attr='stateresult.pdf')
     35    return
    2636
    2737class CustomApplicant(NigeriaApplicant):
  • main/kofacustom.iuokada/trunk/src/kofacustom/iuokada/applicants/browser.py

    r15809 r15878  
    1919"""
    2020import grok
     21import os
     22from zope.component import getUtility
    2123from zope.formlib.textwidgets import BytesDisplayWidget
     24from waeup.kofa.interfaces import (
     25    IExtFileStore, IFileStoreNameChooser)
     26from waeup.kofa.utils.helpers import string_from_bytes, file_size, now
    2227from waeup.kofa.applicants.browser import (
    2328    ApplicantRegistrationPage, ApplicantsContainerPage)
     
    4348    )
    4449from kofacustom.iuokada.applicants.interfaces import (
    45     ICustomPGApplicant, ICustomUGApplicant,
     50    ICustomPGApplicant, ICustomUGApplicant, ICustomApplicant,
    4651    ICustomPGApplicantEdit, ICustomUGApplicantEdit,
    4752    ICustomApplicantOnlinePayment
    4853    )
    4954from kofacustom.iuokada.interfaces import MessageFactory as _
     55
     56MAX_FILE_UPLOAD_SIZE = 1024 * 500
     57
     58def handle_file_upload(upload, context, view, attr=None):
     59    """Handle upload of applicant files.
     60
     61    Returns `True` in case of success or `False`.
     62
     63    Please note that file pointer passed in (`upload`) most probably
     64    points to end of file when leaving this function.
     65    """
     66    size = file_size(upload)
     67    if size > MAX_FILE_UPLOAD_SIZE:
     68        view.flash(_('Uploaded file is too big!'))
     69        return False
     70    dummy, ext = os.path.splitext(upload.filename)
     71    ext.lower()
     72    if ext != '.pdf':
     73        view.flash(_('pdf file extension expected.'))
     74        return False
     75    upload.seek(0) # file pointer moved when determining size
     76    store = getUtility(IExtFileStore)
     77    file_id = IFileStoreNameChooser(context).chooseName(attr=attr)
     78    store.createFile(file_id, upload)
     79    return True
    5080
    5181class CustomApplicantsContainerPage(ApplicantsContainerPage):
     
    73103
    74104    @property
     105    def file_links(self):
     106        html = ''
     107        pdf = getUtility(IExtFileStore).getFileByContext(
     108            self.context, attr='stateresult.pdf')
     109        if pdf:
     110            html += '<a href="%s">Statement of Result</a>' % self.url(
     111                self.context, 'stateresult.pdf')
     112        return html
     113
     114    @property
    75115    def form_fields(self):
    76116        if self.target is not None and self.target.startswith('pg'):
     
    112152        return form_fields
    113153
     154    def update(self):
     155        super(CustomApplicantManageFormPage, self).update()
     156        upload_stateresult = self.request.form.get('form.stateresult', None)
     157        if upload_stateresult:
     158            # We got a fresh stateresult upload
     159            success = handle_file_upload(
     160                upload_stateresult, self.context, self, attr='stateresult.pdf')
     161            if success:
     162                self.context.writeLogMessage(self, 'saved: stateresult')
     163            else:
     164                self.upload_success = False
     165        self.max_file_upload_size = string_from_bytes(MAX_FILE_UPLOAD_SIZE)
     166        return
     167
    114168class CustomApplicantEditFormPage(NigeriaApplicantEditFormPage):
    115169    """An applicant-centered edit view for applicant data.
    116170    """
    117171
     172    def dataNotComplete(self, data):
     173        store = getUtility(IExtFileStore)
     174        if self.context.__parent__.with_picture:
     175            store = getUtility(IExtFileStore)
     176            if not store.getFileByContext(self.context, attr=u'passport.jpg'):
     177                return _('No passport picture uploaded.')
     178            if not self.request.form.get('confirm_passport', False):
     179                return _('Passport picture confirmation box not ticked.')
     180        if self.context.subtype == 'transfer' and \
     181            not store.getFileByContext(self.context, attr=u'stateresult.pdf'):
     182            return _('No statement of result pdf file uploaded.')
     183        return False
     184
    118185    @property
    119186    def form_fields(self):
     
    129196        form_fields['reg_number'].for_display = True
    130197        return form_fields
     198
     199    def update(self):
     200        if self.context.locked or (
     201            self.context.__parent__.expired and
     202            self.context.__parent__.strict_deadline):
     203            self.emit_lock_message()
     204            return
     205        super(CustomApplicantEditFormPage, self).update()
     206        upload_stateresult = self.request.form.get('form.stateresult', None)
     207        if upload_stateresult:
     208            # We got a fresh stateresult upload
     209            success = handle_file_upload(
     210                upload_stateresult, self.context, self, attr='stateresult.pdf')
     211            if not success:
     212                self.upload_success = False
     213        self.max_file_upload_size = string_from_bytes(MAX_FILE_UPLOAD_SIZE)
     214        return
    131215
    132216class CustomPDFApplicationSlip(NigeriaPDFApplicationSlip):
     
    152236        return form_fields
    153237
     238class StateResult(grok.View):
     239    """Renders the pdf form extension for applicants.
     240    """
     241    grok.name('stateresult.pdf')
     242    grok.context(ICustomApplicant)
     243    grok.require('waeup.viewApplication')
     244
     245    def render(self):
     246        pdf = getUtility(IExtFileStore).getFileByContext(
     247            self.context, attr='stateresult.pdf')
     248        self.response.setHeader('Content-Type', 'application/pdf')
     249        return pdf
  • main/kofacustom.iuokada/trunk/src/kofacustom/iuokada/applicants/tests/test_browser.py

    r15563 r15878  
    1919Test the applicant-related UI components.
    2020"""
     21import grok
     22import datetime
     23import pytz
    2124import os
    22 import datetime
     25from StringIO import StringIO
     26from zope.event import notify
    2327from zope.component import createObject, getUtility
    2428from zope.catalog.interfaces import ICatalog
    2529from zope.intid.interfaces import IIntIds
    2630from hurry.workflow.interfaces import IWorkflowState
     31from zope.component import createObject, getUtility
     32from waeup.kofa.configuration import SessionConfiguration
     33from waeup.kofa.interfaces import (
     34    IUserAccount, IExtFileStore, IFileStoreNameChooser)
    2735from kofacustom.iuokada.testing import FunctionalLayer
     36from waeup.kofa.applicants.container import ApplicantsContainer
    2837from waeup.kofa.browser.tests.test_pdf import samples_dir
    29 from waeup.kofa.applicants.tests.test_browser import ApplicantsFullSetup
     38from waeup.kofa.applicants.tests.test_browser import (ApplicantsFullSetup,
     39    ApplicantsFullSetup, container_name_1, session_1)
    3040from waeup.kofa.applicants.tests.test_batching import ApplicantImportExportSetup
    3141from kofacustom.iuokada.applicants.export import CustomApplicantExporter
     
    91101        open(path, 'wb').write(self.browser.contents)
    92102        print "Sample application_slip.pdf written to %s" % path
     103
     104    def test_upload_stateresult_by_manager(self):
     105        # Add trans applicants container
     106        self.transcontainer = ApplicantsContainer()
     107        self.transcontainer.mode = 'create'
     108        self.transcontainer.code = u'ug%s' % session_1
     109        self.transcontainer.prefix = u'ug'
     110        self.transcontainer.application_category = u'no'
     111        self.transcontainer.year = session_1
     112        self.transcontainer.application_fee = 300.0
     113        self.transcontainer.title = u'This is the ug%s container' % session_1
     114        self.app['applicants'][self.transcontainer.code] = self.transcontainer
     115        delta = datetime.timedelta(days=10)
     116        self.transcontainer.startdate = datetime.datetime.now(pytz.utc) - delta
     117        self.transcontainer.enddate = datetime.datetime.now(pytz.utc) + delta
     118        # Add applicant
     119        transapplicant = createObject(u'waeup.Applicant')
     120        transapplicant.firstname = u'Anna'
     121        transapplicant.lastname = u'Post'
     122        transapplicant.subtype = 'transfer'
     123        self.app['applicants'][self.transcontainer.code].addApplicant(transapplicant)
     124        self.transapplicant = self.app['applicants'][self.transcontainer.code][
     125            transapplicant.application_number]
     126        self.transapplicant_view_path = ('http://localhost/app/applicants/ug%s/%s'
     127            % (session_1, transapplicant.application_number))
     128        self.transapplicant_manage_path = ('http://localhost/app/applicants/ug%s/%s/manage'
     129            % (session_1, transapplicant.application_number))
     130        # Login
     131        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
     132        self.browser.open(self.transapplicant_manage_path)
     133        # Create a pseudo file with acceptable size
     134        pdf_content = 'A' * 1024 * 300  # A string of 300 KB size
     135        pseudo_pdf = StringIO(pdf_content)
     136        ctrl = self.browser.getControl(name='form.stateresult')
     137        file_ctrl = ctrl.mech_control
     138        file_ctrl.add_file(pseudo_pdf, filename='myform.pdf')
     139        self.browser.getControl("Save").click() # submit form
     140        # Even though the form could not be saved ...
     141        self.assertTrue('Required input is missing' in self.browser.contents)
     142        # ... the file has been successfully uploaded
     143        pdf_url = self.transapplicant_manage_path.replace('manage', 'stateresult.pdf')
     144        self.browser.open(pdf_url)
     145        self.assertEqual(
     146            self.browser.headers['content-type'], 'application/pdf')
     147        self.assertEqual(len(self.browser.contents), 307200)
     148        # There is really a file stored for the applicant
     149        storage = getUtility(IExtFileStore)
     150        file_id = IFileStoreNameChooser(self.transapplicant).chooseName(
     151            attr='stateresult.pdf')
     152        # The stored file can be fetched
     153        fd = storage.getFile(file_id)
     154        file_len = len(fd.read())
     155        self.assertEqual(file_len, 307200)
     156        # A file link is displayed on the edit view ...
     157        self.browser.open(self.transapplicant_manage_path)
     158        self.assertTrue('<a href="stateresult.pdf">' in self.browser.contents)
     159        # ... and on the dislay view
     160        self.browser.open(self.transapplicant_view_path)
     161        self.assertTrue('stateresult.pdf">Statement of Result</a>'
     162            in self.browser.contents)
     163        # Adding file is properly logged
     164        logfile = os.path.join(
     165            self.app['datacenter'].storage, 'logs', 'applicants.log')
     166        logcontent = open(logfile).read()
     167        self.assertTrue(
     168            'zope.mgr - kofacustom.iuokada.applicants.browser.CustomApplicantManageFormPage'
     169            ' - %s - saved: stateresult'
     170            % (self.transapplicant.applicant_id)
     171            in logcontent)
     172        # When an applicant is removed, also the pdf files are gone.
     173        del self.app['applicants'][self.transcontainer.code][self.transapplicant.application_number]
     174        fd = storage.getFile(file_id)
     175        self.assertTrue(fd is None)
Note: See TracChangeset for help on using the changeset viewer.