- Timestamp:
- 4 Nov 2011, 18:36:27 (13 years ago)
- 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 9 9 from waeup.sirp.students.container import StudentsContainer 10 10 from waeup.sirp.hostels.container import HostelsContainer 11 from waeup.sirp.imagestorage import ImageStorageFileRetrieval, ImageStorage 11 from waeup.sirp.imagestorage import ( 12 ImageStorageFileRetrieval, ImageStorage, ExtFileStore,) 12 13 from waeup.sirp.interfaces import ( 13 14 IUniversity, IWAeUPSIRPPluggable, IObjectUpgradeEvent, ) … … 28 29 setup = setup_authentication,) 29 30 30 grok.local_utility(31 ImageStorageFileRetrieval, provides = IFileRetrieval)31 #grok.local_utility( 32 # ImageStorageFileRetrieval, provides = IFileRetrieval) 32 33 34 #grok.local_utility( 35 # ExtFileStore, provides = IFileRetrieval) 33 36 def __init__(self, *args, **kw): 34 37 super(University, self).__init__(*args, **kw) -
main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/applicants/__init__.py
r6632 r7002 3 3 # Make this a package. 4 4 from waeup.sirp.applicants.applicant import ( 5 ResultEntry, Applicant, ApplicantFactory, ApplicantTraverser, 5 ResultEntry, Applicant, ApplicantFactory, ApplicantImageStoreHandler, 6 get_regno_or_ac, ApplicantImageNameChooser, 6 7 ) 7 8 from waeup.sirp.applicants.container import ApplicantsContainer … … 14 15 'Applicant', 15 16 'ApplicantFactory', 17 'ApplicantImageNameChooser', 18 'ApplicantImageStoreHandler', 16 19 'ApplicantsContainer', 17 20 'ApplicantsRoot', 18 'ApplicantTraverser',19 21 'application_exists', 20 22 'get_applicant_data', 23 'get_regno_or_ac', 21 24 ] -
main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/applicants/applicant.py
r6632 r7002 20 20 ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 21 ## 22 import os 22 23 import grok 23 24 from grok import index 25 from zope.component import getUtility 24 26 from zope.component.interfaces import IFactory 25 27 from zope.interface import implementedBy 26 28 from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState 27 from waeup.sirp.interfaces import IObjectHistory 29 from waeup.sirp.interfaces import IObjectHistory, IExtFileStore 28 30 from waeup.sirp.app import University 29 31 from waeup.sirp.applicants.interfaces import ( 30 32 IResultEntry, IApplicant, IApplicantEdit, default_passport_image, 31 33 ) 34 from waeup.sirp.image import WAeUPImageFile 32 35 from waeup.sirp.utils.helpers import attrs_to_fields 36 37 def 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 33 42 34 43 class ResultEntry(grok.Context): … … 81 90 access_code = index.Field(attribute='access_code') 82 91 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 109 131 110 132 class ApplicantFactory(grok.GlobalUtility): … … 121 143 def getInterfaces(self): 122 144 return implementedBy(Applicant) 145 146 from waeup.sirp.interfaces import IFileStoreHandler, IFileStoreNameChooser 147 from waeup.sirp.imagestorage import DefaultFileStoreHandler 148 149 APPLICANT_IMAGE_STORE_NAME = 'img-applicant' 150 151 class 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 168 class 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 22 22 """UI components for basic applicants and related components. 23 23 """ 24 import os 24 25 import sys 25 26 import grok … … 57 58 ) 58 59 from waeup.sirp.image.image import createWAeUPImageFile 59 from waeup.sirp.interfaces import IWAeUPObject, ILocalRolesAssignable 60 from waeup.sirp.interfaces import ( 61 IWAeUPObject, ILocalRolesAssignable, IExtFileStore, IFileStoreNameChooser) 60 62 from waeup.sirp.permissions import get_users_with_local_roles 61 63 from waeup.sirp.university.interfaces import ICertificate … … 65 67 from waeup.sirp.widgets.objectwidget import ( 66 68 WAeUPObjectWidget, WAeUPObjectDisplayWidget) 67 from waeup.sirp.applicants import ResultEntry, Applicant, get_applicant_data 69 from waeup.sirp.applicants import ( 70 ResultEntry, Applicant, get_applicant_data, get_regno_or_ac) 68 71 from waeup.sirp.applicants.interfaces import ( 69 72 IApplicant, IApplicantPrincipal,IApplicantEdit, IApplicantsRoot, 70 IApplicantsContainer, IApplicantsContainerAdd, application_types_vocab 73 IApplicantsContainer, IApplicantsContainerAdd, application_types_vocab, 74 IMAGE_PATH, 71 75 ) 72 76 from waeup.sirp.applicants.workflow import INITIALIZED, STARTED … … 551 555 'locked').omit('course_admitted') 552 556 #form_fields['fst_sit_results'].custom_widget = list_results_display_widget 553 form_fields['passport'].custom_widget = ThumbnailWidget557 #form_fields['passport'].custom_widget = ThumbnailWidget 554 558 form_fields['date_of_birth'].custom_widget = FriendlyDateDisplayWidget('le') 555 559 label = 'Applicant' 556 560 grok.template('form_display') 557 561 pnav = 3 562 563 def update(self): 564 self.passport_url = self.url(self.context, 'passport.jpg') 565 return 558 566 559 567 @property … … 705 713 grok.require('waeup.manageApplications') 706 714 form_fields = grok.AutoFields(IApplicant) 707 form_fields['passport'].custom_widget = EncodingImageFileWidget715 #form_fields['passport'].custom_widget = EncodingImageFileWidget 708 716 form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year') 709 717 grok.template('form_edit') … … 718 726 if upload: 719 727 # 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) 721 742 # This would normally be totally illegal. We set context 722 743 # data without the complete form data being … … 731 752 # we set the image here and not in 'save()' or other 732 753 # methods. 733 self.context.passport = image754 #self.context.passport = image 734 755 upload.seek(0) 735 756 self.passport_changed = True … … 782 803 'screening_score', 783 804 ) 784 form_fields['passport'].custom_widget = EncodingImageFileWidget805 #form_fields['passport'].custom_widget = EncodingImageFileWidget 785 806 form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year') 786 807 grok.template('form_edit') … … 868 889 text = 'View application record' 869 890 target = 'index' 891 892 class 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 13 13 <table class="zebra"> 14 14 <tbody> 15 <tr><td></td> 16 <td><img src="" tal:attributes="src view/passport_url" /></td> 17 </tr> 15 18 <tal:block repeat="widget view/widgets"> 16 19 <tr> -
main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/applicants/browser_templates/form_edit.pt
r6756 r7002 46 46 </tr> 47 47 </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> 48 56 <tr tal:condition="view/manage_applications"> 49 57 <td class="label"><label>Application Transition:</label></td> -
main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/applicants/interfaces.py
r6915 r7002 370 370 required = False, 371 371 ) 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 # ) 380 380 381 381 # -
main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/applicants/tests/test_applicant.py
r6657 r7002 22 22 """Tests for applicants and related. 23 23 """ 24 import grok 25 import md5 24 26 import unittest 25 27 from StringIO import StringIO 26 28 from hurry.file.interfaces import IFileRetrieval 27 29 from zope.component import ( 28 provideAdapter, adapts, getGlobalSiteManager, provideUtility) 30 provideAdapter, adapts, getGlobalSiteManager, provideUtility, 31 queryUtility) 29 32 from zope.component.hooks import setSite 30 33 from zope.component.interfaces import IFactory … … 36 39 from waeup.sirp.image import WAeUPImageFile, createWAeUPImageFile 37 40 from waeup.sirp.image.interfaces import IWAeUPImageFile 41 from waeup.sirp.imagestorage import ImageStorageFileRetrieval, DefaultStorage 42 from waeup.sirp.interfaces import IFileStoreHandler, IFileStoreNameChooser 38 43 from waeup.sirp.applicants import ( 39 ResultEntry, Applicant, ApplicantFactory, ApplicantTraverser, 44 ResultEntry, Applicant, ApplicantFactory, get_regno_or_ac, 45 ApplicantImageStoreHandler, ApplicantImageNameChooser, 40 46 ) 41 47 from waeup.sirp.applicants.interfaces import ( 42 48 IResultEntry, IApplicant, 43 49 ) 44 from waeup.sirp.imagestorage import ImageStorageFileRetrieval 50 45 51 from waeup.sirp.testing import (FunctionalTestCase, FunctionalLayer) 46 52 … … 51 57 pass 52 58 59 class 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 107 class 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 53 149 class ResultEntryTest(unittest.TestCase): 54 150 … … 87 183 return 88 184 89 def test_passport_no_site(self):185 def DISABLEDtest_passport_no_site(self): 90 186 # make sure we get a real image stored in passport attr 91 187 image = self.applicant.passport.file.read() … … 93 189 return 94 190 95 def test_passport_insite(self):191 def DISABLEDtest_passport_insite(self): 96 192 # make sure we get a real image in passport attr when inside a site. 97 193 # When an applicant is created 'inside a site', its passport … … 103 199 applicant = Applicant() 104 200 image = self.applicant.passport.file.read() 201 # The image is really the placeholder_m.jpg image 105 202 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') 106 208 # The image contains a file_id instead of real image-data 107 209 self.assertEqual(self.applicant.passport.data, … … 118 220 # Install a IFileRetrieval utility that returns WAeUPImageFiles. 119 221 storage = ImageStorageFileRetrieval() 120 provideUtility(storage, IFileRetrieval)222 #provideUtility(storage, IFileRetrieval) 121 223 self.factory = ApplicantFactory() 122 224 return … … 137 239 implemented_by = self.factory.getInterfaces() 138 240 assert implemented_by.isOrExtends(IApplicant) 139 140 141 class ApplicantTraverserTest(FunctionalTestCase):142 143 layer = FunctionalLayer144 145 def setUp(self):146 super(ApplicantTraverserTest, self).setUp()147 provideAdapter(FakeImageLocation)148 self.applicant = Applicant()149 150 self.request = TestRequest('')151 return152 153 def tearDown(self):154 gsm = getGlobalSiteManager()155 gsm.unregisterAdapter(FakeImageLocation)156 super(ApplicantTraverserTest, self).tearDown()157 return158 159 def test_traverse_wo_passport(self):160 # Ask for some attribute not provided161 traverser = ApplicantTraverser(162 self.applicant, self.request163 )164 self.assertRaises(165 NotFound,166 traverser.publishTraverse, self.request, 'passport'167 )168 return169 170 def test_traverse_wo_image_passport_jpg(self):171 # Ask for applicant pic if we didn't provided one172 # We get a placeholder.173 traverser = ApplicantTraverser(174 self.applicant, self.request175 )176 result = traverser.publishTraverse(self.request, 'passport.jpg')177 self.assertTrue(isinstance(result, FakeImageLocation))178 return179 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.request184 )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 return191 192 def test_traverse_w_image_some_jpg(self):193 # Ask for applicant pic by correct name194 traverser = ApplicantTraverser(195 self.applicant, self.request196 )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 28 28 from datetime import datetime, date, timedelta 29 29 from mechanize import LinkNotFoundError 30 from zope.component import createObject 30 from zope.component import createObject, getUtility 31 31 from zope.component.hooks import setSite, clearSite 32 32 from zope.security.interfaces import Unauthorized … … 37 37 from waeup.sirp.applicants.container import ApplicantsContainer 38 38 from waeup.sirp.applicants.applicant import Applicant 39 from waeup.sirp.interfaces import IExtFileStore, IFileStoreNameChooser 39 40 from waeup.sirp.university.faculty import Faculty 40 41 from waeup.sirp.university.department import Department … … 350 351 self.browser.getControl(name="SUBMIT").click() 351 352 pin = self.pins[2] 352 appl = self.getRootFolder()['app']['applicants']['app2009']353 appl = appl[pin]354 passp = appl.passport355 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) 357 358 #image_url = "%s/%s" % (self.browser.url, 'placeholder.jpg') 358 359 image_url = "%s/%s" % (self.browser.url, 'passport.jpg') … … 639 640 # There is a correct <img> link included 640 641 self.assertTrue( 641 '<img src="p laceholder_m.jpg" />' in self.browser.contents)642 '<img src="passport.jpg" />' in self.browser.contents) 642 643 # Browsing the link shows a real image 643 self.browser.open(self.image_url('p laceholder_m.jpg'))644 self.browser.open(self.image_url('passport.jpg')) 644 645 self.assertEqual( 645 646 self.browser.headers['content-type'], 'image/jpeg') 646 647 self.assertEqual(len(self.browser.contents), PH_LEN) 647 648 648 def test_after_login_default_stored_in_imagestorage(self):649 def DISABLEDtest_after_login_default_stored_in_imagestorage(self): 649 650 # After login the applicants placeholder image is stored in 650 651 # an imagestorage … … 672 673 # There is a correct <img> link included 673 674 self.assertTrue( 674 '<img src="p laceholder_m.jpg" />' in self.browser.contents)675 '<img src="passport.jpg" />' in self.browser.contents) 675 676 # Browsing the link shows a real image 676 self.browser.open(self.image_url('p laceholder_m.jpg'))677 self.browser.open(self.image_url('passport.jpg')) 677 678 self.assertEqual( 678 679 self.browser.headers['content-type'], 'image/jpeg') 679 680 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 is683 # correctly stored in an imagestorage684 self.login()685 self.browser.getControl("Save").click() # submit form686 storage = self.app['images']687 self.assertEqual(688 [x for x in storage.keys()],689 [u'b48a1d39bbcb32e955d9ff2dea4ed0e6'])690 file_id = self.applicant.passport.data691 self.assertEqual(692 file_id, u'b48a1d39bbcb32e955d9ff2dea4ed0e6-1')693 # The stored image can be fetched694 fd = storage.retrieveFile(file_id)695 file_len = len(fd.read())696 self.assertEqual(file_len, PH_LEN)697 681 698 682 def test_uploaded_image_browsable_w_errors(self): … … 709 693 # There is a correct <img> link included 710 694 self.assertTrue( 711 '<img src=" myphoto.jpg" />' in self.browser.contents)695 '<img src="passport.jpg" />' in self.browser.contents) 712 696 # 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')) 714 698 self.assertEqual( 715 699 self.browser.headers['content-type'], 'image/jpeg') … … 726 710 file_ctrl.add_file(pseudo_image, filename='myphoto.jpg') 727 711 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 737 715 self.assertEqual( 738 file_id, u'18e57c7eac6ca7fb15b54b5b2bd4106d-1') 716 storage.getFile(file_id).read(), pseudo_image.read()) 717 return 739 718 740 719 def test_uploaded_image_browsable_wo_errors(self): … … 751 730 # There is a correct <img> link included 752 731 self.assertTrue( 753 '<img src=" myphoto.jpg" />' in self.browser.contents)732 '<img src="passport.jpg" />' in self.browser.contents) 754 733 # 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')) 756 735 self.assertEqual( 757 736 self.browser.headers['content-type'], 'image/jpeg') … … 770 749 self.browser.getControl("Save").click() # submit form 771 750 storage = self.app['images'] 772 s elf.assertTrue(773 u'18e57c7eac6ca7fb15b54b5b2bd4106d' in storage.keys())751 storage = getUtility(IExtFileStore) 752 file_id = IFileStoreNameChooser(self.applicant).chooseName() 774 753 # 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) 777 755 file_len = len(fd.read()) 778 756 self.assertEqual(file_len, 31) 779 # The image uploaded belongs to the applicant780 file_id = self.applicant.passport.data781 self.assertEqual(782 file_id, u'18e57c7eac6ca7fb15b54b5b2bd4106d-1')783 757 784 758 def test_uploaded_images_equal(self): … … 797 771 file_ctrl = ctrl.mech_control 798 772 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) 800 776 self.browser.getControl("Save").click() # submit form with changed pic 801 passport1 = self.applicant.passport777 passport1 = getUtility(IExtFileStore).getFile(file_id).read() 802 778 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) 804 781 self.assertTrue(passport0 != passport1) 805 782 self.assertTrue(passport1 == passport2) 806 self.assertTrue(passport1 is passport2)807 783 return 808 784 -
main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/browser/browser.txt
r6917 r7002 1064 1064 1065 1065 >>> sorted(os.listdir(uploadpath)) 1066 ['finished', 'logs', ' unfinished']1066 ['finished', 'logs', 'media', 'unfinished'] 1067 1067 1068 1068 … … 1105 1105 1106 1106 >>> sorted(os.listdir(uploadpath)) 1107 ['finished', 'logs', 'm yfaculties_zope.mgr.csv', 'unfinished']1107 ['finished', 'logs', 'media', 'myfaculties_zope.mgr.csv', 'unfinished'] 1108 1108 1109 1109 We create and upload also a CSV file containing departments: -
main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/imagestorage.py
r6528 r7002 25 25 import hashlib 26 26 import os 27 import tempfile 27 28 import transaction 28 29 import warnings … … 30 31 from ZODB.blob import Blob 31 32 from persistent import Persistent 33 from hurry.file import HurryFile 32 34 from hurry.file.interfaces import IFileRetrieval 35 from zope.component import queryUtility 36 from zope.interface import Interface 33 37 from waeup.sirp.image import WAeUPImageFile 38 from waeup.sirp.interfaces import ( 39 IFileStoreNameChooser, IExtFileStore, IFileStoreHandler,) 34 40 from waeup.sirp.utils.helpers import cmp_files 35 41 … … 42 48 """ 43 49 return hashlib.md5(fd.read()).hexdigest() 50 51 class 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' 44 72 45 73 class Basket(grok.Container): … … 216 244 file_id = storage.storeFile(f, filename) 217 245 return WAeUPImageFile(filename, file_id) 246 247 248 class 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 395 grok.global_utility(ExtFileStore, provides=IExtFileStore) 396 397 class DefaultStorage(ExtFileStore): 398 grok.provides(IFileRetrieval) 399 400 grok.global_utility(DefaultStorage, provides=IFileRetrieval) 401 402 class 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 3 3 import os 4 4 from datetime import datetime 5 from hurry.file.interfaces import IFileRetrieval 5 6 from hurry.workflow.interfaces import IWorkflow, IWorkflowInfo 6 7 from zc.sourcefactory.basic import BasicSourceFactory … … 8 9 from zope.component import getUtility 9 10 from zope.component.interfaces import IObjectEvent 11 from zope.container.interfaces import INameChooser 10 12 from zope.interface import Interface, Attribute, implements 11 13 from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm … … 548 550 """Unregister a logger. 549 551 """ 552 553 # 554 # External File Storage and relatives 555 # 556 class 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 575 class 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 594 class 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 43 43 assert result is not None 44 44 assert IFileRetrieval.providedBy(result) 45 assert isinstance(result, ImageStorageFileRetrieval)46 45 return 47 46 -
main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/tests/test_imagestorage.py
r6529 r7002 5 5 import unittest 6 6 from StringIO import StringIO 7 from hurry.file import HurryFile 7 8 from hurry.file.interfaces import IFileRetrieval 9 from zope.component import getUtility, provideUtility, queryUtility 8 10 from zope.component.hooks import setSite 9 11 from zope.interface.verify import verifyClass, verifyObject 12 from grokcore.site import Site 10 13 from waeup.sirp.app import University 14 from waeup.sirp.datacenter import DataCenter 11 15 from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase 12 16 from waeup.sirp.image import createWAeUPImageFile 13 17 from waeup.sirp.imagestorage import ( 14 md5digest, Basket, ImageStorage, ImageStorageFileRetrieval,) 18 md5digest, Basket, ImageStorage, ImageStorageFileRetrieval, 19 FileStoreNameChooser, ExtFileStore, DefaultFileStoreHandler, 20 DefaultStorage) 21 from waeup.sirp.interfaces import ( 22 IFileStoreNameChooser, IExtFileStore, IFileStoreHandler,) 15 23 16 24 class HelperFuncsTests(unittest.TestCase): … … 287 295 return 288 296 289 def test_waeupimagefile(self):297 def DISABLEDtest_waeupimagefile(self): 290 298 # Make sure WAeUPImageFile can use our file retrieval 291 299 setSite(self.app) … … 294 302 self.assertEqual(contents, 'Hi there!') 295 303 296 def test_waeupimagefile_raw(self):304 def DISABLEDtest_waeupimagefile_raw(self): 297 305 # Make sure we can retrieve a file also if it was initialized 298 306 # with no image storage available … … 301 309 contents = myfile.file.read() 302 310 self.assertEqual(contents, 'Hi there!') 311 312 class NameChooserTests(unittest.TestCase): 313 314 def test_iface(self): 315 obj = FileStoreNameChooser(None) 316 verifyClass(IFileStoreNameChooser, FileStoreNameChooser) 317 verifyObject(IFileStoreNameChooser, obj) 318 return 319 320 class 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 397 class DefaultFileStoreHandlerTests(unittest.TestCase): 398 399 def test_iface(self): 400 obj = DefaultFileStoreHandler() 401 verifyClass(IFileStoreHandler, DefaultFileStoreHandler) 402 verifyObject(IFileStoreHandler, obj) 403 return 404 405 class 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 415 class 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.