43bd0868ac1d5c86621723ed488d909c415f55a8
[Portfolio.git] / manipulation.py
1 # -*- coding: utf-8 -*-
2 ############################################################
3 # Copyright © 2005-2010 Benoît PIN <benoit.pin@ensmp.fr> #
4 # Plinn - http://plinn.org #
5 # #
6 # This program is free software; you can redistribute it #
7 # and/or modify it under the terms of the Creative Commons #
8 # "Attribution-Noncommercial 2.0 Generic" #
9 # http://creativecommons.org/licenses/by-nc/2.0/ #
10 ############################################################
11 """ Image threaded batch computation module
12 """
13
14 import threading
15 import logging
16 import atexit
17 from types import StringTypes
18 from math import ceil
19 import transaction
20 from ZODB.POSException import ConflictError
21 from zope.site.hooks import setSite
22 from cStringIO import StringIO
23
24 console = logging.getLogger('[manipulation thread]')
25
26 class ImageQueueProcessorThread(threading.Thread) :
27 """This thread is started at zope startup
28 """
29
30 __stopped = False
31
32
33 def __init__(self, portal_path, itemsPath) :
34 threading.Thread.__init__(self)
35 self.portal_path = portal_path
36 self.queue = []
37 if isinstance(itemsPath, StringTypes) :
38 itemsPath = [itemsPath]
39 for i in itemsPath :
40 self.queueAdd(i)
41
42 @property
43 def queueSize(self) :
44 return len(self.queue)
45
46 def queueAdd(self, itemPath) :
47 self.queue.append(itemPath)
48
49 def run(self) :
50 console.info('process started.')
51 #atexit.register(self.stop)
52 import Zope2
53 app = Zope2.app()
54 portal = app.unrestrictedTraverse(self.portal_path)
55 setSite(portal)
56 while not self.__stopped and self.queueSize :
57 self._process(app)
58
59 con = app._p_jar
60 con.close()
61 #con.close()
62 console.info('process finished.')
63 #print con
64 #print con.transaction_manager
65
66
67 def stop(self):
68 console.info('process stopped.')
69 self.__stopped = True
70
71 def _process(self, app) :
72 path = self.queue.pop(0)
73 try :
74 p = app.unrestrictedTraverse(path)
75 except KeyError :
76 console.warn('deleted during processing: %s' % path)
77 return
78
79 console.info('%d : %s' % (self.queueSize, p.absolute_url()))
80
81 try :
82 if not hasattr(p, 'thumbnail'):
83 p.makeThumbnail()
84 # print 'make thumbnail'
85
86 for size in ((500, 500), (600, 600), (800, 800)) :
87 # print 'resize at', size
88 p._getResizedImage(size, True)
89 transaction.commit()
90
91 zMin = p.tiles_min_zoom
92 zMax = p.tiles_max_zoom
93 zStep = p.tiles_step_zoom
94 levels = range(zMin, zMax + zStep, zStep)
95 zooms = [l/100. for l in levels]
96 todo = set(zooms) - set(p._tiles.keys())
97 if todo :
98 if p.tileGenerationLock.locked() :
99 console.info('skip %s: already tiling.' % p.absolute_url())
100 return
101
102 p.tileGenerationLock.acquire()
103 zooms = list(todo)
104 zooms.sort()
105 ppm = None
106 try :
107 ppm = p._getPPM()
108 for zoom in zooms :
109
110 # print 'tiling at', zoom
111 if zoom < 1 :
112 rppm = ppm.resize(ratio=zoom)
113 else :
114 rppm = ppm
115 p._makeTilesAt(zoom, rppm)
116 del rppm
117 transaction.commit()
118 finally :
119 del ppm
120 p.tileGenerationLock.release()
121
122 try :
123 delattr(p, '_v__methodResultsCache')
124 except AttributeError:
125 pass
126
127 p.tiles_available = 1
128
129 portal = app.unrestrictedTraverse(self.portal_path)
130 ctool = portal.portal_catalog
131 uid = '/'.join(p.getPhysicalPath())
132 ctool.reindexObject(p, idxs=['tiles_available'], uid=uid)
133 transaction.commit()
134
135 except ConflictError :
136 console.warn('Resync after ZODB ConflicError')
137 transaction.abort()
138 portal = app.unrestrictedTraverse(self.portal_path)
139 portal._p_jar.sync()
140 self.queueAdd(path)
141 return
142 except :
143 p.tiles_available = -1
144 import traceback
145 out = StringIO()
146 traceback.print_exc(None, out)
147 console.error(out.getvalue())