Ménage, lint.
[Portfolio.git] / Products / Portfolio / 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 ZODB.POSException import ConnectionStateError
22 from zope.site.hooks import setSite
23 from cStringIO import StringIO
24
25 console = logging.getLogger('[manipulation thread]')
26
27 class ImageQueueProcessorThread(threading.Thread) :
28 """This thread is started at zope startup
29 """
30
31 __stopped = False
32
33
34 def __init__(self, portal_path, itemsPath) :
35 threading.Thread.__init__(self)
36 self.portal_path = portal_path
37 self.queue = []
38 if isinstance(itemsPath, StringTypes) :
39 itemsPath = [itemsPath]
40 for i in itemsPath :
41 self.queueAdd(i)
42
43 @property
44 def queueSize(self) :
45 return len(self.queue)
46
47 def queueAdd(self, itemPath) :
48 self.queue.append(itemPath)
49
50 def run(self) :
51 console.info('process started.')
52 #atexit.register(self.stop)
53 import Zope2
54 app = Zope2.app()
55 portal = app.unrestrictedTraverse(self.portal_path)
56 setSite(portal)
57 while not self.__stopped and self.queueSize :
58 self._process(app)
59
60 con = app._p_jar
61 try :
62 con.close()
63 except ConnectionStateError, e :
64 console.warn('ConnectionStateError raised before finished.')
65 console.info('process finished.')
66
67
68 def stop(self):
69 console.info('process stopped.')
70 self.__stopped = True
71
72 def _process(self, app) :
73 path = self.queue.pop(0)
74 try :
75 p = app.unrestrictedTraverse(path)
76 except KeyError :
77 console.warn('deleted during processing: %s' % path)
78 return
79
80 console.info('%d : %s' % (self.queueSize, p.absolute_url()))
81
82 try :
83 if not hasattr(p, 'thumbnail'):
84 p.makeThumbnail()
85 # print 'make thumbnail'
86
87 for size in ((500, 500), (600, 600), (800, 800)) :
88 # print 'resize at', size
89 p._getResizedImage(size, True)
90 transaction.commit()
91
92 zMin = p.tiles_min_zoom
93 zMax = p.tiles_max_zoom
94 zStep = p.tiles_step_zoom
95 levels = range(zMin, zMax + zStep, zStep)
96 zooms = [l/100. for l in levels]
97 todo = set(zooms) - set(p._tiles.keys())
98 if todo :
99 if p.tileGenerationLock.locked() :
100 console.info('skip %s: already tiling.' % p.absolute_url())
101 return
102
103 p.tileGenerationLock.acquire()
104 zooms = list(todo)
105 zooms.sort()
106 ppm = None
107 try :
108 ppm = p._getPPM()
109 for zoom in zooms :
110
111 # print 'tiling at', zoom
112 if zoom < 1 :
113 rppm = ppm.resize(ratio=zoom)
114 else :
115 rppm = ppm
116 p._makeTilesAt(zoom, rppm)
117 del rppm
118 transaction.commit()
119 finally :
120 del ppm
121 p.tileGenerationLock.release()
122
123 try :
124 delattr(p, '_v__methodResultsCache')
125 except AttributeError:
126 pass
127
128 p.tiles_available = 1
129 assert p._getCatalogTool()
130 p.reindexObject(idxs=['tiles_available'])
131 transaction.commit()
132
133 except ConflictError :
134 console.warn('Resync after ZODB ConflicError')
135 transaction.abort()
136 portal = app.unrestrictedTraverse(self.portal_path)
137 portal._p_jar.sync()
138 self.queueAdd(path)
139 return
140 except :
141 p.tiles_available = -1
142 import traceback
143 out = StringIO()
144 traceback.print_exc(None, out)
145 console.error(out.getvalue())