[Zope-dev] ZPatterns Image Archive Project

Phillip J. Eby pje@telecommunity.com
Sun, 01 Apr 2001 14:01:25 -0500


At 08:59 PM 3/30/01 -0800, Michael R. Bernstein wrote:
>"Phillip J. Eby" wrote:
>> 
>> Aha.  I think I understand what you're doing now.  You have an "Image" and
>> you have a "Rendering".  Two classes, different behaviors.  I'm assuming
>> that originals and thumbnails and whatever other renderings exist have no
>> real behavioral differences and can be implemented with the same class and
>> storage characteristics.
>
>The terminology I'm using is ArchiveImage (for the 'Image'
>class) and RackImage (for the 'Rendering' class).

I'd recommend a name change for RackImage, at least at the Specialist
level.  If you don't like Renderings, then maybe RenderedImages,
ActualImages, or some such.  Specialist names should reflect the *purpose*
of a thing in the application rather than the nature of the thing itself.


>I think that different sizes would have the same behavioural
>characteristics (simply an image file, really), but am less
>sure about storage. My application will attempt to store all
>the renderings in the ZODB, but if I want this to be
>reusable, I have to assume that someone (including me) might
>want to store the image data on the FS instead. If it's
>going to be stored on the FS, it would be natural to dump
>different sizes into separate directories, or even separate
>partitions. Two years from now, this web application could
>contain around 300 Gb of images (current prices on that much
>storage are about $1,000 for ten 30 Gb hard-drives, so I'm
>not too worried on that score).

This can still be accomplished with a single specialist, if your 'id'
format contains the necessary information to distinguish between image
sizes/types.  If a user of your framework wants to seperate the storage,
they can create more than one rack and have the specialist distinguish
between them using the contents of the 'id'.  It's best to keep
implementation simple in the reference implementation of a framework.


>> >That's what I meant, sorry. The RackImages need to appear to
>> >be attributes of the ZClass, and I'll use SkinScripts to
>> >accomplish that.
>> 
>> That's really only practical if you have a fixed set of rendering types and
>> names, but it'll work for retrieval.  It won't really help you with
>> creation and assignment, though.  I'd suggest specialized methods on Image
>> which delegate to the Renderings specialist.
>
>I was thinking of a manage_upload method on the
>ArchiveImage, that iterated through a list of sizes and used
>an external method that imports PIL to resize the image
>data, then passes the resized image data into the RackImage
>manage_upload method.
>
>Does that seem reasonable?

It seems to me that sizing renderings should be the responsibility of the
Renderings specialist.  That is, the ArchiveImage upload method would look
something like this:

for imageKind in ('fullsize','thumbnail'):
    Renderings.setRenderingFor(self,imageKind,imageData)


The setRenderingFor method would take the ArchiveImage's id, tack the
imagekind onto it, and either retrieve the current image or create a new
one, then re-size the image according to your rules for what size fullsize
or thumbnail is, and pass it to the current or new rendered image object.
(A counterpart method, getRenderingFor(archiveImage,imageKind) would do a
similar id transformation to retrieve a rendering when called by the
ArchiveImage's getRendering() method.)


Of course, this means that the Renderings specialist has to know what
sizes different size names mean, and that ArchiveImages have to know the
possible sizes.  Such knowledge being spread across two specialists means
there's a specialist missing: RenderingKinds.  This may be a sub-specialist
of Renderings, or a peer of Renderings within ArchiveImages.  If a
sub-specialist of Renderings, Renderings must expose a method to retrieve
the sizes, and the upload method above would look like this instead:

for imageKind in Renderings.getRenderingKindsFor(self):
    Renderings.setRenderingFor(self,imageKind,imageData)

Although, it may be in your framework that ArchiveImages are responsible
for knowing which kinds of renderings they should have, and the
RenderingKinds specialist will simply deal with implementation details such
as how each kind is sized and which rack they're stored in within the
Renderings specialist.

By the way, RenderingKinds is a sort of specialist that hasn't been
discussed much outside of the apps Ty and I work with - the "constant"
Specialist, one which contains application configuration or metadata rather
than "content".  Oftentimes it's handy to simply base a Specialist on a
TinyTable or similar product in order to set up configuration of constant
items like RenderingKinds.  Consider the TinyTable data below:

Columns: name:string scaling:float width:int height:int rackname:string

"fullsize",1.0,0,0,"bigimages"
"halfsize",0.5,0,0,"bigimages"
"thumbnail",0.0,64,64,"thumbnails"

The RenderingKinds specialist would have a single rack, which uses
SkinScript to pull in the data from the TinyTable (WITH
theTinyTable[self.id] COMPUTE scaling,width,height,rackname).

Now, you could make a RenderingKind class that uses this data to resize an
image.  That is RenderingKind would have a sizeImage(imageData) method that
returned a new image of the appropriate size.  It could also have a
rackname attribute which would tell the Renderings specialist which rack an
image of that kind should be stored in.  The user of the application could
reconfigure at will by changing the contents of the TinyTable.

This might be all that RenderingKind objects do in the application - resize
images and say where to put them.  But over time, you might find additional
uses for them.  Like for example if you had certain rules about which kinds
of renderings should be created for certain types of ArchiveImages.