Ignore:
Timestamp:
23 Aug 2018, 06:43:06 (6 years ago)
Author:
Henrik Bettermann
Message:

Implement the upload of pdf files in application section.

Location:
main/waeup.aaue/trunk/src/waeup/aaue/applicants
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • main/waeup.aaue/trunk/src/waeup/aaue/applicants/applicant.py

    r14829 r15113  
    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
     
    2527    ICustomApplicant, ICustomUGApplicantEdit, ICustomPGApplicantEdit,
    2628    IPUTMEApplicantEdit, ITranscriptApplicant, ICertificateRequest)
     29
     30@grok.subscribe(ICustomApplicant, grok.IObjectRemovedEvent)
     31def custom_handle_applicant_removed(applicant, event):
     32    """If an applicant is removed also pdf files of this applicant are removed.
     33    """
     34    file_store = getUtility(IExtFileStore)
     35    file_store.deleteFileByContext(applicant, attr='stateresult.pdf')
     36    return
    2737
    2838class CustomApplicant(NigeriaApplicant):
  • main/waeup.aaue/trunk/src/waeup/aaue/applicants/browser.py

    r15100 r15113  
    224224    )
    225225
     226MAX_FILE_UPLOAD_SIZE = 1024 * 500
     227
     228def handle_file_upload(upload, context, view, attr=None):
     229    """Handle upload of applicant files.
     230
     231    Returns `True` in case of success or `False`.
     232
     233    Please note that file pointer passed in (`upload`) most probably
     234    points to end of file when leaving this function.
     235    """
     236    size = file_size(upload)
     237    if size > MAX_FILE_UPLOAD_SIZE:
     238        view.flash(_('Uploaded file is too big!'))
     239        return False
     240    dummy, ext = os.path.splitext(upload.filename)
     241    ext.lower()
     242    if ext != '.pdf':
     243        view.flash(_('pdf file extension expected.'))
     244        return False
     245    upload.seek(0) # file pointer moved when determining size
     246    store = getUtility(IExtFileStore)
     247    file_id = IFileStoreNameChooser(context).chooseName(attr=attr)
     248    store.createFile(file_id, upload)
     249    return True
     250
    226251class CustomApplicantDisplayFormPage(NigeriaApplicantDisplayFormPage):
    227252    """A display view for applicant data.
    228253    """
     254
     255    @property
     256    def file_links(self):
     257        html = ''
     258        pdf = getUtility(IExtFileStore).getFileByContext(
     259            self.context, attr='stateresult.pdf')
     260        if pdf:
     261            html += '<a href="stateresult.pdf">Statement of Result</a>'
     262        return html
    229263
    230264    @property
     
    284318        return ''
    285319
     320    def update(self):
     321        super(CustomApplicantDisplayFormPage, self).update()
     322        self.extraform_url = self.url(self.context, 'stateresult.pdf')
     323        return
     324
    286325class CustomPDFActionButton(PDFActionButton):
    287326
     
    386425        return form_fields
    387426
     427    def update(self):
     428        super(CustomApplicantManageFormPage, self).update()
     429        upload_stateresult = self.request.form.get('form.stateresult', None)
     430        if upload_stateresult:
     431            # We got a fresh stateresult upload
     432            success = handle_file_upload(
     433                upload_stateresult, self.context, self, attr='stateresult.pdf')
     434            if success:
     435                self.context.writeLogMessage(self, 'saved: stateresult')
     436            else:
     437                self.upload_success = False
     438        self.max_file_upload_size = string_from_bytes(MAX_FILE_UPLOAD_SIZE)
     439        return
     440
    388441class CustomApplicantEditFormPage(NigeriaApplicantEditFormPage):
    389442    """An applicant-centered edit view for applicant data.
     
    400453            if not self.request.form.get('confirm_passport', False):
    401454                return _('Passport picture confirmation box not ticked.')
     455        if self.view.target in ('trans',) and \
     456            not store.getFileByContext(self.context, attr=u'stateresult.pdf'):
     457            return _('No statement of result pdf file uploaded.')
     458
    402459        return False
    403460
     
    460517        form_fields['reg_number'].for_display = True
    461518        return form_fields
     519
     520    def update(self):
     521        if self.context.locked or (
     522            self.context.__parent__.expired and
     523            self.context.__parent__.strict_deadline):
     524            self.emit_lock_message()
     525            return
     526        super(CustomApplicantEditFormPage, self).update()
     527        upload_stateresult = self.request.form.get('form.stateresult', None)
     528        if upload_stateresult:
     529            # We got a fresh stateresult upload
     530            success = handle_file_upload(
     531                upload_stateresult, self.context, self, attr='stateresult.pdf')
     532            if not success:
     533                self.upload_success = False
     534        self.max_file_upload_size = string_from_bytes(MAX_FILE_UPLOAD_SIZE)
     535        return
    462536
    463537class CustomApplicantRegistrationPage(NigeriaApplicantRegistrationPage):
     
    587661        return students_utils.renderPDF(self,'screening_data.pdf',
    588662            self.context, applicantview, note=self.note)
     663
     664class StateResult(grok.View):
     665    """Renders the pdf form extension for applicants.
     666    """
     667    grok.name('stateresult.pdf')
     668    grok.context(ICustomApplicant)
     669    grok.require('waeup.viewApplication')
     670
     671    def render(self):
     672        pdf = getUtility(IExtFileStore).getFileByContext(
     673            self.context, attr='stateresult.pdf')
     674        self.response.setHeader('Content-Type', 'application/pdf')
     675        return pdf
  • main/waeup.aaue/trunk/src/waeup/aaue/applicants/browser_templates/applicantdisplaypage.pt

    r14304 r15113  
    3737      </td>
    3838    <tr>
     39    <tr tal:condition="python: view.target in ('trans',)">
     40      <td class="fieldname" i18n:translate="">
     41        Files:
     42      </td>
     43      <td>
     44        <span tal:replace="structure view/file_links" />
     45      </td>
     46    </tr>
    3947  </tbody>
    4048</table>
  • main/waeup.aaue/trunk/src/waeup/aaue/applicants/browser_templates/applicanteditpage.pt

    r14555 r15113  
    4242          </span>
    4343          <span tal:replace="view/max_upload_size">10 KB</span>
     44      </tr>
     45      <tr tal:condition="python: view.target in ('trans',)">
     46        <td class="fieldname" i18n:translate="">
     47          Statement of Result:
     48        </td>
     49        <td>
     50          <p tal:condition="python: view.file_exists('stateresult.pdf')">
     51            <a href="stateresult.pdf"
     52               i18n:translate="">
     53              Download pdf file
     54            </a>
     55          </p>
     56          <div class="input-group half">
     57            <div class="input-group-btn">
     58              <div class="btn btn-default btn-file">
     59                Select&hellip;
     60              <input type="file" name="form.stateresult" />
     61              </div>
     62            </div>
     63            <input type="text" class="form-control" readonly>
     64          </div>
     65          <span i18n:translate="">
     66            Max. file size:
     67          </span>
     68          <span tal:replace="view/max_file_upload_size">10 KB</span>
    4469      </tr>
    4570      <tr tal:condition="view/manage_applications">
  • main/waeup.aaue/trunk/src/waeup/aaue/applicants/tests/test_browser.py

    r15099 r15113  
    2020import pytz
    2121import os
     22from StringIO import StringIO
    2223from zope.event import notify
    2324from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
    2425from zope.component import createObject, getUtility
    2526from waeup.kofa.configuration import SessionConfiguration
    26 from waeup.kofa.interfaces import IUserAccount
     27from waeup.kofa.interfaces import (
     28    IUserAccount, IExtFileStore, IFileStoreNameChooser)
    2729from waeup.kofa.applicants.container import ApplicantsContainer
    2830from waeup.kofa.browser.tests.test_pdf import samples_dir
     
    285287        self.assertFalse('Admitted Course of Study' in self.browser.contents)
    286288        return
     289
     290    def test_upload_stateresult_by_manager(self):
     291        # Add trans applicants container
     292        self.transcontainer = ApplicantsContainer()
     293        self.transcontainer.mode = 'create'
     294        self.transcontainer.code = u'trans%s' % session_1
     295        self.transcontainer.prefix = u'trans'
     296        self.transcontainer.application_category = u'no'
     297        self.transcontainer.year = session_1
     298        self.transcontainer.application_fee = 300.0
     299        self.transcontainer.title = u'This is the trans%s container' % session_1
     300        self.app['applicants'][self.transcontainer.code] = self.transcontainer
     301        delta = datetime.timedelta(days=10)
     302        self.transcontainer.startdate = datetime.datetime.now(pytz.utc) - delta
     303        self.transcontainer.enddate = datetime.datetime.now(pytz.utc) + delta
     304        # Add applicant
     305        transapplicant = createObject(u'waeup.Applicant')
     306        transapplicant.firstname = u'Anna'
     307        transapplicant.lastname = u'Post'
     308        self.app['applicants'][self.transcontainer.code].addApplicant(transapplicant)
     309        self.transapplicant = self.app['applicants'][self.transcontainer.code][
     310            transapplicant.application_number]
     311        self.transapplicant_view_path = ('http://localhost/app/applicants/trans%s/%s'
     312            % (session_1, transapplicant.application_number))
     313        self.transapplicant_manage_path = ('http://localhost/app/applicants/trans%s/%s/manage'
     314            % (session_1, transapplicant.application_number))
     315        # Login
     316        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
     317        self.browser.open(self.transapplicant_manage_path)
     318        # Create a pseudo file with acceptable size
     319        pdf_content = 'A' * 1024 * 300  # A string of 300 KB size
     320        pseudo_pdf = StringIO(pdf_content)
     321        ctrl = self.browser.getControl(name='form.stateresult')
     322        file_ctrl = ctrl.mech_control
     323        file_ctrl.add_file(pseudo_pdf, filename='myform.pdf')
     324        self.browser.getControl("Save").click() # submit form
     325        # Even though the form could not be saved ...
     326        self.assertTrue('Required input is missing' in self.browser.contents)
     327        # ... the file has been successfully uploaded
     328        pdf_url = self.transapplicant_manage_path.replace('manage', 'stateresult.pdf')
     329        self.browser.open(pdf_url)
     330        self.assertEqual(
     331            self.browser.headers['content-type'], 'application/pdf')
     332        self.assertEqual(len(self.browser.contents), 307200)
     333        # There is really a file stored for the applicant
     334        storage = getUtility(IExtFileStore)
     335        file_id = IFileStoreNameChooser(self.transapplicant).chooseName(
     336            attr='stateresult.pdf')
     337        # The stored file can be fetched
     338        fd = storage.getFile(file_id)
     339        file_len = len(fd.read())
     340        self.assertEqual(file_len, 307200)
     341        # A file link is displayed on the edit view ...
     342        self.browser.open(self.transapplicant_manage_path)
     343        self.assertTrue('<a href="stateresult.pdf">' in self.browser.contents)
     344        # ... and on the dislay view
     345        self.browser.open(self.transapplicant_view_path)
     346        self.assertTrue('<a href="stateresult.pdf">Statement of Result</a>'
     347            in self.browser.contents)
     348        # Adding file is properly logged
     349        logfile = os.path.join(
     350            self.app['datacenter'].storage, 'logs', 'applicants.log')
     351        logcontent = open(logfile).read()
     352        self.assertTrue(
     353            'zope.mgr - waeup.aaue.applicants.browser.CustomApplicantManageFormPage'
     354            ' - %s - saved: stateresult'
     355            % (self.transapplicant.applicant_id)
     356            in logcontent)
     357        # When an applicant is removed, also the pdf files are gone.
     358        del self.app['applicants'][self.transcontainer.code][self.transapplicant.application_number]
     359        fd = storage.getFile(file_id)
     360        self.assertTrue(fd is None)
Note: See TracChangeset for help on using the changeset viewer.