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