Ignore:
Timestamp:
4 Nov 2011, 18:36:27 (13 years ago)
Author:
uli
Message:

Throw in the complete mess of last 2 weeks. External file storage now works basically (tests pass), although there are lots of things still to remove, finetune, document, etc.

Location:
main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp
Files:
14 edited

Legend:

Unmodified
Added
Removed
  • main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/app.py

    r6952 r7002  
    99from waeup.sirp.students.container import StudentsContainer
    1010from waeup.sirp.hostels.container import HostelsContainer
    11 from waeup.sirp.imagestorage import ImageStorageFileRetrieval, ImageStorage
     11from waeup.sirp.imagestorage import (
     12    ImageStorageFileRetrieval, ImageStorage, ExtFileStore,)
    1213from waeup.sirp.interfaces import (
    1314    IUniversity, IWAeUPSIRPPluggable, IObjectUpgradeEvent, )
     
    2829        setup = setup_authentication,)
    2930
    30     grok.local_utility(
    31         ImageStorageFileRetrieval, provides = IFileRetrieval)
     31    #grok.local_utility(
     32    #    ImageStorageFileRetrieval, provides = IFileRetrieval)
    3233
     34    #grok.local_utility(
     35    #    ExtFileStore, provides = IFileRetrieval)
    3336    def __init__(self, *args, **kw):
    3437        super(University, self).__init__(*args, **kw)
  • main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/applicants/__init__.py

    r6632 r7002  
    33# Make this a package.
    44from waeup.sirp.applicants.applicant import (
    5     ResultEntry, Applicant, ApplicantFactory, ApplicantTraverser,
     5    ResultEntry, Applicant, ApplicantFactory, ApplicantImageStoreHandler,
     6    get_regno_or_ac, ApplicantImageNameChooser,
    67    )
    78from waeup.sirp.applicants.container import ApplicantsContainer
     
    1415    'Applicant',
    1516    'ApplicantFactory',
     17    'ApplicantImageNameChooser',
     18    'ApplicantImageStoreHandler',
    1619    'ApplicantsContainer',
    1720    'ApplicantsRoot',
    18     'ApplicantTraverser',
    1921    'application_exists',
    2022    'get_applicant_data',
     23    'get_regno_or_ac',
    2124    ]
  • main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/applicants/applicant.py

    r6632 r7002  
    2020## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    2121##
     22import os
    2223import grok
    2324from grok import index
     25from zope.component import getUtility
    2426from zope.component.interfaces import IFactory
    2527from zope.interface import implementedBy
    2628from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
    27 from waeup.sirp.interfaces import IObjectHistory
     29from waeup.sirp.interfaces import IObjectHistory, IExtFileStore
    2830from waeup.sirp.app import University
    2931from waeup.sirp.applicants.interfaces import (
    3032    IResultEntry, IApplicant, IApplicantEdit, default_passport_image,
    3133    )
     34from waeup.sirp.image import WAeUPImageFile
    3235from waeup.sirp.utils.helpers import attrs_to_fields
     36
     37def get_regno_or_ac(context):
     38    reg_no = getattr(context, 'reg_no', None)
     39    if reg_no is None:
     40        return getattr(context, 'access_code', None)
     41    return reg_no
    3342
    3443class ResultEntry(grok.Context):
     
    8190    access_code = index.Field(attribute='access_code')
    8291
    83 class ApplicantTraverser(grok.Traverser):
    84     """Get image of the context applicant.
    85 
    86     Each applicant can provide a passport photograph which will be
    87     returned by this traverser if:
    88 
    89     - we request the exact filename of the picture or
    90 
    91     - ask for a picture named 'passport.jpg'.
    92 
    93     If no picture was stored yet, we get a placeholder image when
    94     asking for `passport.jpg`.
    95 
    96     If none of the above applies, we return ``None``, most probably
    97     resulting a :exc:`NotFound` exception.
    98 
    99     """
    100     grok.context(IApplicant)
    101     def traverse(self, name):
    102         passport_filename = getattr(self.context.passport, 'filename', None)
    103         if name == passport_filename:
    104             return self.context.passport
    105         if name == 'passport.jpg':
    106             if self.context.passport is not None:
    107                 return self.context.passport
    108         return
     92#class ApplicantTraverser(grok.Traverser):
     93#    """Get image of the context applicant.
     94#
     95#    Each applicant can provide a passport photograph which will be
     96#    returned by this traverser if:
     97#
     98#    - we request the exact filename of the picture or
     99#
     100#    - ask for a picture named 'passport.jpg'.
     101#
     102#    If no picture was stored yet, we get a placeholder image when
     103#    asking for `passport.jpg`.
     104#
     105#    If none of the above applies, we return ``None``, most probably
     106#    resulting a :exc:`NotFound` exception.
     107#
     108#    """
     109#    grok.context(IApplicant)
     110#    def traverse(self, name):
     111#        if name != 'passport.jpg':
     112#            return
     113#        marked_filename = '__img_appl__%s.jpg' % (
     114#            get_regno_or_ac(self.context))
     115#        image = getUtility(IExtFileStore).getFile(marked_filename)
     116#        if image is None:
     117#            # Return placeholder
     118#            from waeup.sirp.applicants.interfaces import IMAGE_PATH
     119#            return open(os.path.join(IMAGE_PATH, 'placeholder_m.jpg'), 'rb')
     120#            pass
     121#        return image #WAeUPImageFile(marked_filename, image.read())
     122#        if not hasattr(self.context, 'passport'):
     123#            return None
     124#        passport_filename = getattr(self.context.passport, 'filename', None)
     125#        if name == passport_filename:
     126#            return self.context.passport
     127#        if name == 'passport.jpg':
     128#            if self.context.passport is not None:
     129#                return self.context.passport
     130#        return
    109131
    110132class ApplicantFactory(grok.GlobalUtility):
     
    121143    def getInterfaces(self):
    122144        return implementedBy(Applicant)
     145
     146from waeup.sirp.interfaces import IFileStoreHandler, IFileStoreNameChooser
     147from waeup.sirp.imagestorage import DefaultFileStoreHandler
     148
     149APPLICANT_IMAGE_STORE_NAME = 'img-applicant'
     150
     151class ApplicantImageNameChooser(grok.Adapter):
     152    grok.context(IApplicant)
     153    grok.implements(IFileStoreNameChooser)
     154
     155    def checkName(self, name=None):
     156        return name == self.chooseName(name, self.context)
     157
     158    def chooseName(self, name=None):
     159        parent_name = getattr(
     160            getattr(self.context, '__parent__', None),
     161            '__name__', '_default')
     162        marked_filename = '__%s__%s/%s.jpg' % (
     163            APPLICANT_IMAGE_STORE_NAME,
     164            parent_name, get_regno_or_ac(self.context))
     165        return marked_filename
     166
     167
     168class ApplicantImageStoreHandler(DefaultFileStoreHandler, grok.GlobalUtility):
     169    """Applicant specific image handling.
     170
     171    This handler knows in which path in a filestore to store applicant
     172    images and how to turn this kind of data into some (browsable)
     173    file object.
     174
     175    It is called from the global file storage, when it wants to
     176    get/store a file with a file id starting with
     177    ``__img-applicant__`` (the marker string for applicant images).
     178
     179    Like each other file store handler it does not handle the files
     180    really (this is done by the global file store) but only computes
     181    paths and things like this.
     182    """
     183    grok.implements(IFileStoreHandler)
     184    grok.name(APPLICANT_IMAGE_STORE_NAME)
     185
     186    def pathFromFileID(self, store, root, file_id):
     187        """All applicants images are filed in directory ``applicants``.
     188        """
     189        marker, filename, basename, ext = store.extractMarker(file_id)
     190        return os.path.join(root, 'applicants', filename)
     191
     192    def createFile(self, store, root, filename, file_id, file):
     193        """Create a browsable file-like object.
     194        """
     195        # possible other actions: check for jpeg format
     196        path = self.pathFromFileID(store, root, file_id)
     197        return file, path, WAeUPImageFile(filename, file_id)
  • main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/applicants/browser.py

    r6816 r7002  
    2222"""UI components for basic applicants and related components.
    2323"""
     24import os
    2425import sys
    2526import grok
     
    5758    )
    5859from waeup.sirp.image.image import createWAeUPImageFile
    59 from waeup.sirp.interfaces import IWAeUPObject, ILocalRolesAssignable
     60from waeup.sirp.interfaces import (
     61    IWAeUPObject, ILocalRolesAssignable, IExtFileStore, IFileStoreNameChooser)
    6062from waeup.sirp.permissions import get_users_with_local_roles
    6163from waeup.sirp.university.interfaces import ICertificate
     
    6567from waeup.sirp.widgets.objectwidget import (
    6668    WAeUPObjectWidget, WAeUPObjectDisplayWidget)
    67 from waeup.sirp.applicants import ResultEntry, Applicant, get_applicant_data
     69from waeup.sirp.applicants import (
     70    ResultEntry, Applicant, get_applicant_data, get_regno_or_ac)
    6871from waeup.sirp.applicants.interfaces import (
    6972    IApplicant, IApplicantPrincipal,IApplicantEdit, IApplicantsRoot,
    70     IApplicantsContainer, IApplicantsContainerAdd, application_types_vocab
     73    IApplicantsContainer, IApplicantsContainerAdd, application_types_vocab,
     74    IMAGE_PATH,
    7175    )
    7276from waeup.sirp.applicants.workflow import INITIALIZED, STARTED
     
    551555        'locked').omit('course_admitted')
    552556    #form_fields['fst_sit_results'].custom_widget = list_results_display_widget
    553     form_fields['passport'].custom_widget = ThumbnailWidget
     557    #form_fields['passport'].custom_widget = ThumbnailWidget
    554558    form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le')
    555559    label = 'Applicant'
    556560    grok.template('form_display')
    557561    pnav = 3
     562
     563    def update(self):
     564        self.passport_url = self.url(self.context, 'passport.jpg')
     565        return
    558566
    559567    @property
     
    705713    grok.require('waeup.manageApplications')
    706714    form_fields = grok.AutoFields(IApplicant)
    707     form_fields['passport'].custom_widget = EncodingImageFileWidget
     715    #form_fields['passport'].custom_widget = EncodingImageFileWidget
    708716    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
    709717    grok.template('form_edit')
     
    718726        if upload:
    719727            # We got a fresh upload
    720             image = createWAeUPImageFile(upload.filename, upload)
     728            #from waeup.sirp.interfaces import IExtFileStorage
     729            #from zope.component import queryUtility
     730            #storage = queryUtility(IExtFileStorage)
     731            # filename = '__img_appl__' + str(self.context.reg_no) #upload.filename
     732            from waeup.sirp.interfaces import (
     733                IExtFileStore, IFileStoreNameChooser)
     734            store = getUtility(IExtFileStore)
     735            file_id = IFileStoreNameChooser(self.context).chooseName()
     736            store.createFile(file_id, upload)
     737            #filename = '__img_appl__' + str(
     738            #    self.context.access_code) + '.jpg' #upload.filename
     739            print "FILENAME: ", file_id
     740            # image = createWAeUPImageFile(upload.filename, upload)
     741            #image = createWAeUPImageFile(filename, upload)
    721742            # This would normally be totally illegal. We set context
    722743            # data without the complete form data being
     
    731752            # we set the image here and not in 'save()' or other
    732753            # methods.
    733             self.context.passport = image
     754            #self.context.passport = image
    734755            upload.seek(0)
    735756            self.passport_changed = True
     
    782803        'screening_score',
    783804        )
    784     form_fields['passport'].custom_widget = EncodingImageFileWidget
     805    #form_fields['passport'].custom_widget = EncodingImageFileWidget
    785806    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
    786807    grok.template('form_edit')
     
    868889    text = 'View application record'
    869890    target = 'index'
     891
     892class PassportImage(grok.View):
     893    """Renders the passport image for applicants.
     894    """
     895    grok.name('passport.jpg')
     896    grok.context(IApplicant)
     897    grok.require('waeup.handleApplication')
     898
     899    def render(self):
     900        # A filename chooser turns a context into a filename suitable
     901        # for file storage.
     902        chooser = IFileStoreNameChooser(self.context)
     903        file_id = chooser.chooseName()
     904        image = getUtility(IExtFileStore).getFile(file_id)
     905        self.response.setHeader(
     906            'Content-Type', 'image/jpeg')
     907        if image is None:
     908            # show placeholder image
     909            return open(os.path.join(IMAGE_PATH, 'placeholder_m.jpg'), 'rb')
     910        return image
  • main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/applicants/browser_templates/form_display.pt

    r6350 r7002  
    1313<table class="zebra">
    1414  <tbody>
     15    <tr><td></td>
     16    <td><img src="" tal:attributes="src view/passport_url" /></td>
     17    </tr>
    1518    <tal:block repeat="widget view/widgets">
    1619      <tr>
  • main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/applicants/browser_templates/form_edit.pt

    r6756 r7002  
    4646        </tr>
    4747      </tal:block>
     48      <tr>
     49        <td class="label">Photograph</td>
     50        <td class="field">
     51          <span class="widget">
     52            <img src="passport.jpg" /><br />
     53            <input type="file" name="form.passport" />
     54          </span>
     55      </tr>
    4856      <tr tal:condition="view/manage_applications">
    4957        <td class="label"><label>Application Transition:</label></td>
  • main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/applicants/interfaces.py

    r6915 r7002  
    370370        required = False,
    371371        )
    372     passport = ImageFile(
    373         title = u'Passport Photograph',
    374         #default = DEFAULT_PASSPORT_IMAGE_MALE,
    375         defaultFactory = default_passport_image,
    376         description = u'Maximun file size is 20 kB.',
    377         required = True,
    378         max_size = 20480,
    379         )
     372    #passport = ImageFile(
     373    #    title = u'Passport Photograph',
     374    #    #default = DEFAULT_PASSPORT_IMAGE_MALE,
     375    #    defaultFactory = default_passport_image,
     376    #    description = u'Maximun file size is 20 kB.',
     377    #    required = True,
     378    #    max_size = 20480,
     379    #    )
    380380
    381381    #
  • main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/applicants/tests/test_applicant.py

    r6657 r7002  
    2222"""Tests for applicants and related.
    2323"""
     24import grok
     25import md5
    2426import unittest
    2527from StringIO import StringIO
    2628from hurry.file.interfaces import IFileRetrieval
    2729from zope.component import (
    28     provideAdapter, adapts, getGlobalSiteManager, provideUtility)
     30    provideAdapter, adapts, getGlobalSiteManager, provideUtility,
     31    queryUtility)
    2932from zope.component.hooks import setSite
    3033from zope.component.interfaces import IFactory
     
    3639from waeup.sirp.image import WAeUPImageFile, createWAeUPImageFile
    3740from waeup.sirp.image.interfaces import IWAeUPImageFile
     41from waeup.sirp.imagestorage import ImageStorageFileRetrieval, DefaultStorage
     42from waeup.sirp.interfaces import IFileStoreHandler, IFileStoreNameChooser
    3843from waeup.sirp.applicants import (
    39     ResultEntry, Applicant, ApplicantFactory, ApplicantTraverser,
     44    ResultEntry, Applicant, ApplicantFactory, get_regno_or_ac,
     45    ApplicantImageStoreHandler, ApplicantImageNameChooser,
    4046    )
    4147from waeup.sirp.applicants.interfaces import (
    4248    IResultEntry, IApplicant,
    4349    )
    44 from waeup.sirp.imagestorage import ImageStorageFileRetrieval
     50
    4551from waeup.sirp.testing import (FunctionalTestCase, FunctionalLayer)
    4652
     
    5157        pass
    5258
     59class HelperTests(FunctionalTestCase):
     60
     61    layer = FunctionalLayer
     62
     63    def test_get_regno_or_ac(self):
     64        # we can get reg_no or access_code of an applicants if it is set
     65        appl1 = Applicant()
     66        appl2 = Applicant()
     67        appl2.reg_no = u'foo'
     68        appl3 = Applicant()
     69        appl3.access_code = u'bar'
     70        appl4 = Applicant()
     71        appl4.reg_no = u'foo'
     72        appl4.access_code = u'bar'
     73        self.assertTrue(
     74            get_regno_or_ac(appl1) is None)
     75        self.assertEqual(
     76            get_regno_or_ac(appl2), u'foo')
     77        self.assertEqual(
     78            get_regno_or_ac(appl3), u'bar')
     79        self.assertEqual(
     80            get_regno_or_ac(appl4), u'foo')
     81        return
     82
     83    def test_image_store_handler_util_accessible(self):
     84        # we can get an IFileStoreHandler utility for applicants
     85        handler = queryUtility(IFileStoreHandler, name='img-applicant')
     86        self.assertTrue(
     87            isinstance(handler, ApplicantImageStoreHandler))
     88        return
     89
     90    def test_image_store_handler(self):
     91        store = DefaultStorage()
     92        handler = queryUtility(IFileStoreHandler, name='img-applicant')
     93        result1 = handler.pathFromFileID(
     94            store, '/fake-root', '__img-applicant__sample.jpg')
     95        result2 = handler.pathFromFileID(
     96            store, '/fake-root', '__img-applicant__dir1/sample.jpg')
     97        result3 = handler.pathFromFileID(
     98            store, '/fake-root', '__img-applicant__dir1/dir2/sample.jpg')
     99        self.assertEqual(
     100            result1, '/fake-root/applicants/sample.jpg')
     101        self.assertEqual(
     102            result2, '/fake-root/applicants/dir1/sample.jpg')
     103        self.assertEqual(
     104            result3, '/fake-root/applicants/dir1/dir2/sample.jpg')
     105        return
     106
     107class ApplicantImageNameChooserTests(FunctionalTestCase):
     108
     109    layer = FunctionalLayer
     110
     111    def test_iface(self):
     112        # make sure we implement promised interfaces
     113        obj = ApplicantImageNameChooser(None) # needs a context normally
     114        verify.verifyClass(IFileStoreNameChooser, ApplicantImageNameChooser)
     115        verify.verifyObject(IFileStoreNameChooser, obj)
     116        return
     117
     118    def test_name_chooser_available(self):
     119        # we can get a name chooser for applicant objects as adapter
     120        appl = Applicant()
     121        chooser = IFileStoreNameChooser(appl)
     122        self.assertTrue(chooser is not None)
     123        return
     124
     125    def test_name_chooser_applicant_wo_container(self):
     126        # we can get an image filename for applicants not in a container
     127        appl = Applicant()
     128        appl.reg_no = u'MY_REG_NO'
     129        chooser = IFileStoreNameChooser(appl)
     130        result = chooser.chooseName()
     131        # the file would be stored in a ``_default`` directory.
     132        self.assertEqual(
     133            result, '__img-applicant___default/MY_REG_NO.jpg')
     134        return
     135
     136    def test_name_chooser_applicant_w_container(self):
     137        appl = Applicant()
     138        appl.reg_no = u'MY_REG_NO'
     139        fake_container = grok.Container()
     140        fake_container.__name__ = 'folder'
     141        fake_container['appl'] = appl
     142        appl.__parent__ = fake_container
     143        chooser = IFileStoreNameChooser(appl)
     144        result = chooser.chooseName()
     145        self.assertEqual(
     146            result, '__img-applicant__folder/MY_REG_NO.jpg')
     147        return
     148
    53149class ResultEntryTest(unittest.TestCase):
    54150
     
    87183        return
    88184
    89     def test_passport_no_site(self):
     185    def DISABLEDtest_passport_no_site(self):
    90186        # make sure we get a real image stored in passport attr
    91187        image = self.applicant.passport.file.read()
     
    93189        return
    94190
    95     def test_passport_insite(self):
     191    def DISABLEDtest_passport_insite(self):
    96192        # make sure we get a real image in passport attr when inside a site.
    97193        # When an applicant is created 'inside a site', its passport
     
    103199        applicant = Applicant()
    104200        image = self.applicant.passport.file.read()
     201        # The image is really the placeholder_m.jpg image
    105202        self.assertEqual(len(image), 2059)
     203        self.assertEqual(md5.new(image).hexdigest(),
     204                         u'b48a1d39bbcb32e955d9ff2dea4ed0e6')
     205        # As the applicant has no reg_no yet, the image data is 'empty'
     206        self.assertEqual(self.applicant.passport.data,
     207                         '__img_appl__placeholder_m.jpg')
    106208        # The image contains a file_id instead of real image-data
    107209        self.assertEqual(self.applicant.passport.data,
     
    118220        # Install a IFileRetrieval utility that returns WAeUPImageFiles.
    119221        storage = ImageStorageFileRetrieval()
    120         provideUtility(storage, IFileRetrieval)
     222        #provideUtility(storage, IFileRetrieval)
    121223        self.factory = ApplicantFactory()
    122224        return
     
    137239        implemented_by = self.factory.getInterfaces()
    138240        assert implemented_by.isOrExtends(IApplicant)
    139 
    140 
    141 class ApplicantTraverserTest(FunctionalTestCase):
    142 
    143     layer = FunctionalLayer
    144 
    145     def setUp(self):
    146         super(ApplicantTraverserTest, self).setUp()
    147         provideAdapter(FakeImageLocation)
    148         self.applicant = Applicant()
    149 
    150         self.request = TestRequest('')
    151         return
    152 
    153     def tearDown(self):
    154         gsm = getGlobalSiteManager()
    155         gsm.unregisterAdapter(FakeImageLocation)
    156         super(ApplicantTraverserTest, self).tearDown()
    157         return
    158 
    159     def test_traverse_wo_passport(self):
    160         # Ask for some attribute not provided
    161         traverser = ApplicantTraverser(
    162             self.applicant, self.request
    163             )
    164         self.assertRaises(
    165             NotFound,
    166             traverser.publishTraverse, self.request, 'passport'
    167             )
    168         return
    169 
    170     def test_traverse_wo_image_passport_jpg(self):
    171         # Ask for applicant pic if we didn't provided one
    172         # We get a placeholder.
    173         traverser = ApplicantTraverser(
    174             self.applicant, self.request
    175             )
    176         result = traverser.publishTraverse(self.request, 'passport.jpg')
    177         self.assertTrue(isinstance(result, FakeImageLocation))
    178         return
    179 
    180     def test_traverse_w_image_passport_jpg(self):
    181         # Ask for applicant pic that's named 'passport.jpg'
    182         traverser = ApplicantTraverser(
    183             self.applicant, self.request
    184             )
    185         self.applicant.passport = createWAeUPImageFile(
    186             'nofile.jpg', StringIO('no-content'))
    187         self.applicant.passport.filename = 'mypic.jpg'
    188         result = traverser.publishTraverse(self.request, 'passport.jpg')
    189         self.assertTrue(isinstance(result, FakeImageLocation))
    190         return
    191 
    192     def test_traverse_w_image_some_jpg(self):
    193         # Ask for applicant pic by correct name
    194         traverser = ApplicantTraverser(
    195             self.applicant, self.request
    196             )
    197         self.applicant.passport = WAeUPImageFile('nofile.jpg', '')
    198         self.applicant.passport.filename = 'mypic.jpg'
    199         result = traverser.publishTraverse(self.request, 'mypic.jpg')
    200         self.assertTrue(isinstance(result, FakeImageLocation))
    201         return
  • main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/applicants/tests/test_browser.py

    r6816 r7002  
    2828from datetime import datetime, date, timedelta
    2929from mechanize import LinkNotFoundError
    30 from zope.component import createObject
     30from zope.component import createObject, getUtility
    3131from zope.component.hooks import setSite, clearSite
    3232from zope.security.interfaces import Unauthorized
     
    3737from waeup.sirp.applicants.container import ApplicantsContainer
    3838from waeup.sirp.applicants.applicant import Applicant
     39from waeup.sirp.interfaces import IExtFileStore, IFileStoreNameChooser
    3940from waeup.sirp.university.faculty import Faculty
    4041from waeup.sirp.university.department import Department
     
    350351        self.browser.getControl(name="SUBMIT").click()
    351352        pin = self.pins[2]
    352         appl = self.getRootFolder()['app']['applicants']['app2009']
    353         appl = appl[pin]
    354         passp = appl.passport
    355         passp_len = len(passp.file.read())
    356         self.assertEqual(passp_len, PH_LEN)
     353        #appl = self.getRootFolder()['app']['applicants']['app2009']
     354        #appl = appl[pin]
     355        #passp = appl.passport
     356        #passp_len = len(passp.file.read())
     357        #self.assertEqual(passp_len, PH_LEN)
    357358        #image_url = "%s/%s" % (self.browser.url, 'placeholder.jpg')
    358359        image_url = "%s/%s" % (self.browser.url, 'passport.jpg')
     
    639640        # There is a correct <img> link included
    640641        self.assertTrue(
    641             '<img src="placeholder_m.jpg" />' in self.browser.contents)
     642            '<img src="passport.jpg" />' in self.browser.contents)
    642643        # Browsing the link shows a real image
    643         self.browser.open(self.image_url('placeholder_m.jpg'))
     644        self.browser.open(self.image_url('passport.jpg'))
    644645        self.assertEqual(
    645646            self.browser.headers['content-type'], 'image/jpeg')
    646647        self.assertEqual(len(self.browser.contents), PH_LEN)
    647648
    648     def test_after_login_default_stored_in_imagestorage(self):
     649    def DISABLEDtest_after_login_default_stored_in_imagestorage(self):
    649650        # After login the applicants placeholder image is stored in
    650651        # an imagestorage
     
    672673        # There is a correct <img> link included
    673674        self.assertTrue(
    674             '<img src="placeholder_m.jpg" />' in self.browser.contents)
     675            '<img src="passport.jpg" />' in self.browser.contents)
    675676        # Browsing the link shows a real image
    676         self.browser.open(self.image_url('placeholder_m.jpg'))
     677        self.browser.open(self.image_url('passport.jpg'))
    677678        self.assertEqual(
    678679            self.browser.headers['content-type'], 'image/jpeg')
    679680        self.assertEqual(len(self.browser.contents), PH_LEN)
    680 
    681     def test_after_submit_default_stored_in_imagestorage(self):
    682         # After submitting an applicant form the default image is
    683         # correctly stored in an imagestorage
    684         self.login()
    685         self.browser.getControl("Save").click() # submit form
    686         storage = self.app['images']
    687         self.assertEqual(
    688             [x for x in storage.keys()],
    689             [u'b48a1d39bbcb32e955d9ff2dea4ed0e6'])
    690         file_id = self.applicant.passport.data
    691         self.assertEqual(
    692             file_id, u'b48a1d39bbcb32e955d9ff2dea4ed0e6-1')
    693         # The stored image can be fetched
    694         fd = storage.retrieveFile(file_id)
    695         file_len = len(fd.read())
    696         self.assertEqual(file_len, PH_LEN)
    697681
    698682    def test_uploaded_image_browsable_w_errors(self):
     
    709693        # There is a correct <img> link included
    710694        self.assertTrue(
    711             '<img src="myphoto.jpg" />' in self.browser.contents)
     695            '<img src="passport.jpg" />' in self.browser.contents)
    712696        # Browsing the link shows a real image
    713         self.browser.open(self.image_url('myphoto.jpg'))
     697        self.browser.open(self.image_url('passport.jpg'))
    714698        self.assertEqual(
    715699            self.browser.headers['content-type'], 'image/jpeg')
     
    726710        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
    727711        self.browser.getControl("Save").click() # submit form
    728         storage = self.app['images']
    729         self.assertTrue(
    730             u'18e57c7eac6ca7fb15b54b5b2bd4106d' in storage.keys())
    731         # The stored image can be fetched
    732         fd = storage.retrieveFile(u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')
    733         file_len = len(fd.read())
    734         self.assertEqual(file_len, 31)
    735         # The image uploaded belongs to the applicant
    736         file_id = self.applicant.passport.data
     712        storage = getUtility(IExtFileStore)
     713        file_id = IFileStoreNameChooser(self.applicant).chooseName()
     714        pseudo_image.seek(0) # reset our file data source
    737715        self.assertEqual(
    738             file_id, u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')
     716            storage.getFile(file_id).read(), pseudo_image.read())
     717        return
    739718
    740719    def test_uploaded_image_browsable_wo_errors(self):
     
    751730        # There is a correct <img> link included
    752731        self.assertTrue(
    753             '<img src="myphoto.jpg" />' in self.browser.contents)
     732            '<img src="passport.jpg" />' in self.browser.contents)
    754733        # Browsing the link shows a real image
    755         self.browser.open(self.image_url('myphoto.jpg'))
     734        self.browser.open(self.image_url('passport.jpg'))
    756735        self.assertEqual(
    757736            self.browser.headers['content-type'], 'image/jpeg')
     
    770749        self.browser.getControl("Save").click() # submit form
    771750        storage = self.app['images']
    772         self.assertTrue(
    773             u'18e57c7eac6ca7fb15b54b5b2bd4106d' in storage.keys())
     751        storage = getUtility(IExtFileStore)
     752        file_id = IFileStoreNameChooser(self.applicant).chooseName()
    774753        # The stored image can be fetched
    775         fd = storage.retrieveFile(u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')
    776         #fd = storage.retrieveFile(file_id)
     754        fd = storage.getFile(file_id)
    777755        file_len = len(fd.read())
    778756        self.assertEqual(file_len, 31)
    779         # The image uploaded belongs to the applicant
    780         file_id = self.applicant.passport.data
    781         self.assertEqual(
    782             file_id, u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')
    783757
    784758    def test_uploaded_images_equal(self):
     
    797771        file_ctrl = ctrl.mech_control
    798772        file_ctrl.add_file(pseudo_image, filename='myphoto.jpg')
    799         passport0 = self.applicant.passport
     773        file_id = IFileStoreNameChooser(self.applicant).chooseName()
     774        setSite(self.app)
     775        passport0 = getUtility(IExtFileStore).getFile(file_id)
    800776        self.browser.getControl("Save").click() # submit form with changed pic
    801         passport1 = self.applicant.passport
     777        passport1 = getUtility(IExtFileStore).getFile(file_id).read()
    802778        self.browser.getControl("Save").click() # submit form w/o changes
    803         passport2 = self.applicant.passport
     779        passport2 = getUtility(IExtFileStore).getFile(file_id).read()
     780        self.assertTrue(passport0 is None)
    804781        self.assertTrue(passport0 != passport1)
    805782        self.assertTrue(passport1 == passport2)
    806         self.assertTrue(passport1 is passport2)
    807783        return
    808784
  • main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/browser/browser.txt

    r6917 r7002  
    10641064
    10651065  >>> sorted(os.listdir(uploadpath))
    1066   ['finished', 'logs', 'unfinished']
     1066  ['finished', 'logs', 'media', 'unfinished']
    10671067
    10681068
     
    11051105
    11061106  >>> sorted(os.listdir(uploadpath))
    1107   ['finished', 'logs', 'myfaculties_zope.mgr.csv', 'unfinished']
     1107  ['finished', 'logs', 'media', 'myfaculties_zope.mgr.csv', 'unfinished']
    11081108
    11091109We create and upload also a CSV file containing departments:
  • main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/imagestorage.py

    r6528 r7002  
    2525import hashlib
    2626import os
     27import tempfile
    2728import transaction
    2829import warnings
     
    3031from ZODB.blob import Blob
    3132from persistent import Persistent
     33from hurry.file import HurryFile
    3234from hurry.file.interfaces import IFileRetrieval
     35from zope.component import queryUtility
     36from zope.interface import Interface
    3337from waeup.sirp.image import WAeUPImageFile
     38from waeup.sirp.interfaces import (
     39    IFileStoreNameChooser, IExtFileStore, IFileStoreHandler,)
    3440from waeup.sirp.utils.helpers import cmp_files
    3541
     
    4248    """
    4349    return hashlib.md5(fd.read()).hexdigest()
     50
     51class FileStoreNameChooser(grok.Adapter):
     52    grok.context(Interface)
     53    grok.implements(IFileStoreNameChooser)
     54
     55    def checkName(self, name):
     56        """Check whether an object name is valid.
     57
     58        Raises a user error if the name is not valid.
     59        """
     60        pass
     61
     62    def chooseName(self, name):
     63        """Choose a unique valid name for the object.
     64
     65        The given name and object may be taken into account when
     66        choosing the name.
     67
     68        chooseName is expected to always choose a valid name (that
     69        would pass the checkName test) and never raise an error.
     70        """
     71        return u'unknown_file'
    4472
    4573class Basket(grok.Container):
     
    216244        file_id = storage.storeFile(f, filename)
    217245        return WAeUPImageFile(filename, file_id)
     246
     247
     248class ExtFileStore(object): #grok.GlobalUtility):
     249    """External file store.
     250
     251    External file stores are meant to store files 'externally' of the
     252    ZODB, i.e. in filesystem.
     253
     254    Most important attribute of the external file store is the `root`
     255    path which gives the path to the location where files will be
     256    stored within.
     257
     258    By default `root` is a ``'media/'`` directory in the root of the
     259    datacenter root of a site.
     260
     261    The `root` attribute is 'read-only' because you normally don't
     262    want to change this path -- it is dynamic. That means, if you call
     263    the file store from 'within' a site, the root path will be located
     264    inside this site (a :class:`waeup.sirp.University` instance). If
     265    you call it from 'outside' a site some temporary dir (always the
     266    same during lifetime of the file store instance) will be used. The
     267    term 'temporary' tells what you can expect from this path
     268    persistence-wise.
     269
     270    If you insist, you can pass a root path on initialization to the
     271    constructor but when calling from within a site afterwards, the
     272    site will override your setting for security measures. This way
     273    you can safely use one file store for different sites in a Zope
     274    instance simultanously and files from one site won't show up in
     275    another.
     276
     277    An ExtFileStore instance is available as a global utility
     278    implementing :iface:`waeup.sirp.interfaces.IExtFileStore`.
     279
     280    To add and retrieve files from the storage, use the appropriate
     281    methods below.
     282    """
     283
     284    grok.implements(IExtFileStore)
     285
     286    _root = None
     287
     288    @property
     289    def root(self):
     290        """Root dir of this storage.
     291
     292        The root dir is a readonly value determined dynamically. It
     293        holds media files for sites or other components.
     294
     295        If a site is available we return a ``media/`` dir in the
     296        datacenter storage dir.
     297
     298        Otherwise we create a temporary dir which will be remembered
     299        on next call.
     300
     301        If a site exists and has a datacenter, it has always
     302        precedence over temporary dirs, also after a temporary
     303        directory was created.
     304
     305        Please note that retrieving `root` is expensive. You might
     306        want to store a copy once retrieved in order to minimize the
     307        number of calls to `root`.
     308
     309        """
     310        site = grok.getSite()
     311        if site is not None:
     312            root = os.path.join(site['datacenter'].storage, 'media')
     313            return root
     314        if self._root is None:
     315            self._root = tempfile.mkdtemp()
     316        return self._root
     317
     318    def __init__(self, root=None):
     319        self._root = root
     320        return
     321
     322    def getFile(self, file_id):
     323        """Get a file stored under file ID `file_id`.
     324
     325        If the file cannot be found ``None`` is returned.
     326        """
     327        marker, filename, base, ext = self.extractMarker(file_id)
     328        handler = queryUtility(IFileStoreHandler, name=marker,
     329                               default=DefaultFileStoreHandler())
     330        path = handler.pathFromFileID(self, self.root, file_id)
     331        if not os.path.exists(path):
     332            return None
     333        fd = open(path, 'rb')
     334        return fd
     335
     336    def createFile(self, filename, f):
     337        """Store a file.
     338        """
     339        file_id = filename
     340        root = self.root # Calls to self.root are expensive
     341        marker, filename, base, ext = self.extractMarker(file_id)
     342        handler = queryUtility(IFileStoreHandler, name=marker,
     343                               default=DefaultFileStoreHandler())
     344        f, path, file_obj = handler.createFile(
     345            self, root, file_id, filename, f)
     346        dirname = os.path.dirname(path)
     347        if not os.path.exists(dirname):
     348            os.makedirs(dirname, 0755)
     349        open(path, 'wb').write(f.read())
     350        return file_obj
     351
     352    def extractMarker(self, file_id):
     353        """split filename into marker, filename, basename, and extension.
     354
     355        A marker is a leading part of a string of form
     356        ``__MARKERNAME__`` followed by the real filename. This way we
     357        can put markers into a filename to request special processing.
     358
     359        Returns a quadruple
     360
     361          ``(marker, filename, basename, extension)``
     362
     363        where ``marker`` is the marker in lowercase, filename is the
     364        complete trailing real filename, ``basename`` is the basename
     365        of the filename and ``extension`` the filename extension of
     366        the trailing filename. See examples below.
     367
     368        Example:
     369
     370           >>> extractMarker('__MaRkEr__sample.jpg')
     371           ('marker', 'sample.jpg', 'sample', '.jpg')
     372
     373        If no marker is contained, we assume the whole string to be a
     374        real filename:
     375
     376           >>> extractMarker('no-marker.txt')
     377           ('', 'no-marker.txt', 'no-marker', '.txt')
     378
     379        Filenames without extension give an empty extension string:
     380
     381           >>> extractMarker('no-marker')
     382           ('', 'no-marker', 'no-marker', '')
     383
     384        """
     385        if not isinstance(file_id, basestring) or not file_id:
     386            return ('', '', '', '')
     387        parts = file_id.split('__', 2)
     388        marker = ''
     389        if len(parts) == 3 and parts[0] == '':
     390            marker = parts[1].lower()
     391            file_id = parts[2]
     392        basename, ext = os.path.splitext(file_id)
     393        return (marker, file_id, basename, ext)
     394
     395grok.global_utility(ExtFileStore, provides=IExtFileStore)
     396
     397class DefaultStorage(ExtFileStore):
     398    grok.provides(IFileRetrieval)
     399
     400grok.global_utility(DefaultStorage, provides=IFileRetrieval)
     401
     402class DefaultFileStoreHandler(grok.GlobalUtility):
     403    grok.implements(IFileStoreHandler)
     404
     405    def pathFromFileID(self, store, root, file_id):
     406        return os.path.join(root, file_id)
     407
     408    def createFile(self, store, root, filename, file_id, f):
     409        path = self.pathFromFileID(store, root, file_id)
     410        return f, path, WAeUPImageFile(filename, file_id)
     411        return path, HurryFile(filename, file_id)
  • main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/interfaces.py

    r6929 r7002  
    33import os
    44from datetime import datetime
     5from hurry.file.interfaces import IFileRetrieval
    56from hurry.workflow.interfaces import IWorkflow, IWorkflowInfo
    67from zc.sourcefactory.basic import BasicSourceFactory
     
    89from zope.component import getUtility
    910from zope.component.interfaces import IObjectEvent
     11from zope.container.interfaces import INameChooser
    1012from zope.interface import Interface, Attribute, implements
    1113from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
     
    548550        """Unregister a logger.
    549551        """
     552
     553#
     554# External File Storage and relatives
     555#
     556class IFileStoreNameChooser(INameChooser):
     557    """See zope.container.interfaces.INameChooser for base methods.
     558    """
     559    def checkName(name):
     560        """Check whether an object name is valid.
     561
     562        Raises a user error if the name is not valid.
     563        """
     564
     565    def chooseName(name):
     566        """Choose a unique valid name for the object.
     567
     568        The given name and object may be taken into account when
     569        choosing the name.
     570
     571        chooseName is expected to always choose a valid name (that would pass
     572        the checkName test) and never raise an error.
     573        """
     574
     575class IExtFileStore(IFileRetrieval):
     576    """A file storage that stores files in filesystem (not as blobs).
     577    """
     578    root = schema.TextLine(
     579        title = u'Root path of file store.',
     580        )
     581
     582    def getFile(file_id):
     583        """Get raw file data stored under file with `file_id`.
     584
     585        Returns a file descriptor open for reading.
     586        """
     587
     588    def createFile(filename, f):
     589        """Create file given by f with filename `filename`
     590
     591        Returns a hurry.file.File-based object.
     592        """
     593
     594class IFileStoreHandler(Interface):
     595    """Filestore handlers handle specific files for file stores.
     596
     597    If a file to store/get provides a specific filename, a file store
     598    looks up special handlers for that type of file.
     599
     600    """
     601    def pathFromFileID(store, root, filename):
     602        """Turn file id into path to store.
     603
     604        Returned path should be absolute.
     605        """
     606
     607    def createFile(store, root, filename, file_id, file):
     608        """Return some hurry.file based on `store` and `file_id`.
     609
     610        Some kind of callback method called by file stores to create
     611        file objects from file_id.
     612
     613        Returns a tuple ``(raw_file, path, file_like_obj)`` where the
     614        ``file_like_obj`` should be a HurryFile, a WAeUPImageFile or
     615        similar. ``raw_file`` is the (maybe changed) input file and
     616        ``path`` the relative internal path to store the file at.
     617
     618        Please make sure the ``raw_file`` is opened for reading and
     619        the file descriptor set at position 0 when returned.
     620
     621        This method also gets the raw input file object that is about
     622        to be stored and is expected to raise any exceptions if some
     623        kind of validation or similar fails.
     624        """
  • main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/tests/test_app.py

    r6593 r7002  
    4343        assert result is not None
    4444        assert IFileRetrieval.providedBy(result)
    45         assert isinstance(result, ImageStorageFileRetrieval)
    4645        return
    4746
  • main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/tests/test_imagestorage.py

    r6529 r7002  
    55import unittest
    66from StringIO import StringIO
     7from hurry.file import HurryFile
    78from hurry.file.interfaces import IFileRetrieval
     9from zope.component import getUtility, provideUtility, queryUtility
    810from zope.component.hooks import setSite
    911from zope.interface.verify import verifyClass, verifyObject
     12from grokcore.site import Site
    1013from waeup.sirp.app import University
     14from waeup.sirp.datacenter import DataCenter
    1115from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase
    1216from waeup.sirp.image import createWAeUPImageFile
    1317from waeup.sirp.imagestorage import (
    14     md5digest, Basket, ImageStorage, ImageStorageFileRetrieval,)
     18    md5digest, Basket, ImageStorage, ImageStorageFileRetrieval,
     19    FileStoreNameChooser, ExtFileStore, DefaultFileStoreHandler,
     20    DefaultStorage)
     21from waeup.sirp.interfaces import (
     22    IFileStoreNameChooser, IExtFileStore, IFileStoreHandler,)
    1523
    1624class HelperFuncsTests(unittest.TestCase):
     
    287295        return
    288296
    289     def test_waeupimagefile(self):
     297    def DISABLEDtest_waeupimagefile(self):
    290298        # Make sure WAeUPImageFile can use our file retrieval
    291299        setSite(self.app)
     
    294302        self.assertEqual(contents, 'Hi there!')
    295303
    296     def test_waeupimagefile_raw(self):
     304    def DISABLEDtest_waeupimagefile_raw(self):
    297305        # Make sure we can retrieve a file also if it was initialized
    298306        # with no image storage available
     
    301309        contents = myfile.file.read()
    302310        self.assertEqual(contents, 'Hi there!')
     311
     312class NameChooserTests(unittest.TestCase):
     313
     314    def test_iface(self):
     315        obj = FileStoreNameChooser(None)
     316        verifyClass(IFileStoreNameChooser, FileStoreNameChooser)
     317        verifyObject(IFileStoreNameChooser, obj)
     318        return
     319
     320class ExtFileStoreTests(unittest.TestCase):
     321    # Test external file store (non-functional mode)
     322
     323    def setUp(self):
     324        self.workdir = tempfile.mkdtemp()
     325        self.root = None
     326        return
     327
     328    def tearDown(self):
     329        shutil.rmtree(self.workdir)
     330        if self.root is not None:
     331            shutil.rmtree(self.root)
     332        return
     333
     334    def test_iface(self):
     335        obj = ExtFileStore(None)
     336        verifyClass(IExtFileStore, ExtFileStore)
     337        verifyObject(IExtFileStore, obj)
     338        return
     339
     340    def test_root_setup_wo_site(self):
     341        # if no site is available we can use a temporary root
     342        fs = ExtFileStore()
     343        self.root = fs.root
     344        self.assertTrue(isinstance(self.root, basestring))
     345        self.assertTrue(os.path.exists(self.root))
     346        return
     347
     348    def test_create_instance(self):
     349        storage1 = ExtFileStore()
     350        storage2 = ExtFileStore(root=self.workdir)
     351        self.root = storage1.root
     352        self.assertTrue(storage1.root is not None)
     353        self.assertTrue(storage1.root != storage2.root)
     354        self.assertEqual(storage2.root, self.workdir)
     355        return
     356
     357    def test_create_file(self):
     358        # We can store files
     359        storage = ExtFileStore(root=self.workdir)
     360        dummy_file = StringIO('sample file')
     361        image_file = storage.createFile('mysample', dummy_file)
     362        self.assertTrue('mysample' in os.listdir(storage.root))
     363        self.assertEqual('mysample', image_file.data)
     364        return
     365
     366    def test_get_file(self):
     367        # We can get files after having them stored
     368        storage = ExtFileStore(root=self.workdir)
     369        dummy_file = StringIO('sample file')
     370        image_file = storage.createFile('mysample', dummy_file)
     371        result = storage.getFile(image_file.data)
     372        self.assertEqual(result.read(), 'sample file')
     373        return
     374
     375    def test_extract_marker(self):
     376        # file stores support extracting markers from filenames
     377        storage = ExtFileStore(root=self.workdir)
     378        result1 = storage.extractMarker(None)
     379        result2 = storage.extractMarker('')
     380        result3 = storage.extractMarker('no-marker')
     381        result4 = storage.extractMarker('no-marker.txt')
     382        result5 = storage.extractMarker('__MARKER__foo.jpg')
     383        result6 = storage.extractMarker('__MaRkEr__foo.jpg')
     384        result7 = storage.extractMarker('__THE_MARKER__foo.jpg')
     385        result8 = storage.extractMarker('__A_MARK__my__foo.jpg')
     386
     387        self.assertEqual(result1, ('', '', '', ''))
     388        self.assertEqual(result2, ('', '', '', ''))
     389        self.assertEqual(result3, ('', 'no-marker', 'no-marker', ''))
     390        self.assertEqual(result4, ('', 'no-marker.txt', 'no-marker', '.txt'))
     391        self.assertEqual(result5, ('marker', 'foo.jpg', 'foo', '.jpg'))
     392        self.assertEqual(result6, ('marker', 'foo.jpg', 'foo', '.jpg'))
     393        self.assertEqual(result7, ('the_marker', 'foo.jpg', 'foo', '.jpg'))
     394        self.assertEqual(result8, ('a_mark', 'my__foo.jpg', 'my__foo', '.jpg'))
     395        return
     396
     397class DefaultFileStoreHandlerTests(unittest.TestCase):
     398
     399    def test_iface(self):
     400        obj = DefaultFileStoreHandler()
     401        verifyClass(IFileStoreHandler, DefaultFileStoreHandler)
     402        verifyObject(IFileStoreHandler, obj)
     403        return
     404
     405class CustomizedFileHandler(object):
     406    def pathFromFileID(self, store, root, file_id):
     407        """Turn filename into path to store.
     408        """
     409        return os.path.join(root, file_id[12:])
     410
     411    def createFile(self, store, root, file_id, filename, f):
     412        path = self.pathFromFileID(store, root, file_id)
     413        return f, path, HurryFile(filename, file_id)
     414
     415class FunctionalExtFileStoreTests(FunctionalTestCase):
     416
     417    layer = FunctionalLayer
     418
     419
     420    def setUp(self):
     421        super(FunctionalExtFileStoreTests, self).setUp()
     422        self.workdir = tempfile.mkdtemp()
     423        self.samplefile = os.path.join(self.workdir, 'sample')
     424        self.otherfile = os.path.join(self.workdir, 'other')
     425        open(self.samplefile, 'wb').write('Hi there!')
     426        open(self.otherfile, 'wb').write('Hi from other!')
     427        self.storage = ImageStorage()
     428        self.fd = open(self.samplefile, 'r')
     429        self.fd2 = open(self.otherfile, 'r')
     430        self.getRootFolder()['app'] = University()
     431        self.app = self.getRootFolder()['app']
     432        self.app['datacenter'].setStoragePath(self.workdir)
     433        # register a custom filename mangler
     434        provideUtility(
     435            CustomizedFileHandler(), IFileStoreHandler, name=u'mymarker')
     436        return
     437
     438
     439    def tearDown(self):
     440        super(FunctionalExtFileStoreTests, self).tearDown()
     441        self.fd.close()
     442        self.fd2.close()
     443        shutil.rmtree(self.workdir)
     444        return
     445
     446    def test_root_setup_w_site(self):
     447        # if a site is available we use it to determine the root dir
     448        fs = ExtFileStore()
     449        setSite(self.app)
     450        self.root = fs.root
     451        expected_root = os.path.join(
     452            self.app['datacenter'].storage, 'media')
     453        self.assertTrue(isinstance(self.root, basestring))
     454        self.assertEqual(self.root, expected_root)
     455        return
     456
     457    def test_get_utility(self):
     458        # we can get an ExtFileStore by global utility lookup
     459        fs1 = getUtility(IExtFileStore)
     460        fs2 = getUtility(IExtFileStore)
     461        self.assertTrue(isinstance(fs1, ExtFileStore))
     462        self.assertTrue(fs1 is fs2)
     463        return
     464
     465    def test_default_handler_create_file(self):
     466        # we can use the default handler to store files
     467        fs = ExtFileStore()
     468        result = fs.createFile('sample.txt', StringIO('sample text'))
     469        self.assertEqual(result.data, 'sample.txt')
     470        self.assertTrue('sample.txt' in os.listdir(fs.root))
     471        return
     472
     473    def test_default_handler_get_file(self):
     474        # we can get files stored by the default handler
     475        fs = ExtFileStore()
     476        fs.createFile('sample.txt', StringIO('sample text'))
     477        result1 = fs.getFile('sample.txt')
     478        result2 = fs.getFile('not-existent')
     479        self.assertEqual(result1.read(), 'sample text')
     480        self.assertTrue(result2 is None)
     481        return
     482
     483    def test_customized_handler_create_file(self):
     484        # we can use registered filename handlers
     485        fs = ExtFileStore()
     486        result = fs.createFile(
     487            '__MYMARKER__sample.txt', StringIO('sample text'))
     488        self.assertEqual(result.data, '__MYMARKER__sample.txt')
     489        self.assertTrue('sample.txt' in os.listdir(fs.root))
     490        return
     491
     492    def test_customized_handler_get_file(self):
     493        # we consider registered filename handlers when asking for
     494        # stored files.
     495        fs = ExtFileStore()
     496        fs.createFile('__MYMARKER__sample.txt', StringIO('sample text'))
     497        result1 = fs.getFile('__MYMARKER__sample.txt')
     498        result2 = fs.getFile('__MYMARKER__not-existent')
     499        result3 = fs.getFile('not-existent')
     500        self.assertEqual(result1.read(), 'sample text')
     501        self.assertTrue(result2 is None)
     502        self.assertTrue(result3 is None)
     503        return
     504
     505    def test_get_default_handler(self):
     506        # we can get a default handler
     507        result = queryUtility(IFileStoreHandler)
     508        self.assertTrue(
     509            isinstance(result, DefaultFileStoreHandler))
     510        return
     511
     512    def test_get_default_file_retrieval(self):
     513        # we get a file store when requesting a file retrieval
     514        result = queryUtility(IFileRetrieval)
     515        self.assertTrue(
     516            isinstance(result, DefaultStorage))
Note: See TracChangeset for help on using the changeset viewer.