Contact: fumanchu@aminus.org

Log in as guest/misc to create tickets

root/pinximus/__init__.py

Revision 127 (checked in by fumanchu, 5 years ago)

License and email cleanups.

  • Property svn:eol-style set to native
Line 
1 """
2 Pinximus, an image archive server.
3
4 Dependencies:
5     CherryPy, available at: http://www.cherrypy.org/
6     Dejavu,   available at: http://projects.amor.org/dejavu/
7     PIL,      available at: http://www.pythonware.com/products/pil/
8                  On Debian: apt-get install python-imaging
9
10
11 LICENSE
12 -------
13 This work, including the source code, documentation
14 and related data, is placed into the public domain.
15
16 The original author is Robert Brewer.
17 fumanchu@aminus.org
18 svn://projects.amor.org/misc/pinximus/trunk
19
20 THIS SOFTWARE IS PROVIDED AS-IS, WITHOUT WARRANTY
21 OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF
22 MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE
23 ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE
24 RESULTING FROM THE USE, MODIFICATION, OR
25 REDISTRIBUTION OF THIS SOFTWARE.
26 """
27
28 import os
29 localDir = os.path.join(os.getcwd(), os.path.dirname(__file__))
30 import re
31 try:
32     set
33 except NameError:
34     from sets import Set as set
35 try:
36     from cStringIO import StringIO
37 except ImportError:
38     from StringIO import StringIO
39
40 import cherrypy
41 from cherrypy.lib.cptools import serveFile
42 import cobbler
43 import dejavu
44 from dejavu import logic
45 from PIL import Image
46
47 from model import *
48
49
50 config = {'Libraries': {},
51           'Icons': {},
52           }
53
54
55 def tags_from_string(tagstr):
56     return [x for x in re.split(r"[,; \t\n\r\f\v]", tagstr) if x]
57
58
59 class ResultSet(cobbler.Template):
60     """Template for generating a set of result elements."""
61    
62     COLUMNS = 3
63    
64     def __init__(self):
65         cobbler.Template.__init__(self)
66         self.cells = []
67    
68     def add(self, **params):
69         params.update(self.params)
70         self.cells.append(self.templateText % params)
71    
72     def assemble(self):
73         output = []
74         curcol = 1
75         for cell in self.cells:
76             output.append(cell)
77             curcol += 1
78             if curcol > self.COLUMNS:
79                 curcol = 1
80                 output.append("</tr>")
81                 output.append("<tr>")
82         return u'\n'.join(output)
83
84
85 class ThumbnailSet(cobbler.Template):
86     """Template for generating a set of thumbnail elements."""
87    
88     COLUMNS = 3
89    
90     def __init__(self, resources=None):
91         cobbler.Template.__init__(self, localDir, "thumb.html")
92         if resources:
93             self.resources = set(resources)
94         else:
95             self.resources = set()
96    
97     def add(self, resource):
98         self.resources.add(resource)
99    
100     def assemble(self):
101         output = []
102         curcol = 1
103         resources = [(x.name(), x) for x in self.resources]
104         resources.sort()
105         for name, res in resources:
106             p = {'id': res.ID,
107                  'library': res.Library().Name,
108                  'libraryid': res.LibraryID,
109                  'name': name,
110                  'tags': " ".join(res.tags()),
111                  'title': '%s (%sx%s)' % (res.Path, res.Width, res.Height),
112                  }
113             p.update(self.params)
114             output.append(self.templateText % p)
115             curcol += 1
116             if curcol > self.COLUMNS:
117                 curcol = 1
118                 output.append("</tr>")
119                 output.append("<tr>")
120         return u'\n'.join(output)
121
122
123 class Root:
124    
125     def index(self, q=None):
126         main = cobbler.Template(localDir, "main.html")
127         if q is None:
128             main['results'] = ""
129         else:
130             box = arena.new_sandbox()
131             f = logic.Expression(lambda r, t: t.Value in tags_from_string(q))
132             results = [res for res, tag in box.recall(Resource & Tag, f)]
133             main['results'] = ThumbnailSet(results).assemble()
134         return main
135     index.exposed = True
136    
137     def library(self, id=None):
138         main = cobbler.Template(localDir, "main.html")
139         box = arena.new_sandbox()
140         if id is None or id == 'upload':
141             # Show a list of libraries with links
142             res = id or 'library'
143             libs = box.recall(Library)
144             libs.sort(dejavu.sort('Name'))
145             rs = ResultSet()
146             rs.templateText = ('    <td><a href="%(root)s/%(res)s/'
147                                '%(id)s">%(name)s</a></td>')
148             for lib in libs:
149                 rs.add(id=lib.ID, name=lib.Name, res=res)
150             main['results'] = rs.assemble()
151         else:
152             lib = box.Library(int(id))
153             if lib is None:
154                 raise cherrypy.HTTPError(404)
155             main['results'] = ThumbnailSet(lib.Resource()).assemble()
156         return main
157     library.exposed = True
158    
159     def resource(self, id, *args, **kwargs):
160         box = arena.new_sandbox()
161         res = box.Resource(int(id))
162         if res is None:
163             raise cherrypy.HTTPError(404)
164        
165         if args:
166             args = list(args)
167             cmd = args.pop(0)
168             if cmd == "tags":
169                 if cherrypy.request.method == "POST":
170                     res.set_tags(tags_from_string(kwargs['tags']))
171                     cherrypy.response.status = 204
172                     box.flush_all()
173                 else:
174                     return " ".join(res.tags())
175             else:
176                 raise cherrypy.HTTPError(404)
177         else:
178             return serveFile(res.fullpath())
179     resource.exposed = True
180    
181     def thumb(self, id):
182         box = arena.new_sandbox()
183         res = box.Resource(int(id))
184         if res is None:
185             raise cherrypy.HTTPError(404)
186        
187         return serveFile(res.thumbpath())
188     thumb.exposed = True
189    
190     def tag(self):
191         main = cobbler.Template(localDir, "main.html")
192         box = arena.new_sandbox()
193         tags = box.distinct(Tag, ['Value'])
194         tags.sort()
195         rs = ResultSet()
196         rs.templateText = ('    <td><a href="%(root)s/index?q=%(name)s">'
197                            '%(name)s</a></td>')
198         for t in tags:
199             rs.add(name=t)
200         main['results'] = rs.assemble()
201         return main
202     tag.exposed = True
203    
204     def upload(self, id, newfile=None, newname=None):
205         box = arena.new_sandbox()
206         lib = box.Library(int(id))
207         if lib is None:
208             raise cherrypy.HTTPError(404)
209        
210         if cherrypy.request.method == 'POST':
211             # Accept the uploaded file
212             libpath = os.path.normpath(lib.Path)
213             head, newname = os.path.split(newname or newfile.filename)
214             fullpath = os.path.join(libpath, newname)
215             outfile = open(fullpath, 'wb')
216             while True:
217                 data = newfile.file.read(8192)
218                 if not data:
219                     break
220                 outfile.write(data)
221             outfile.close()
222             lib.register_resource(fullpath)
223             box.flush_all()
224             return self.library(id)
225         else:
226             # Show an upload form.
227             main = cobbler.Template(localDir, "main.html")
228             main['results'] = """
229     <h2>Upload file to %s</h2>
230     <form action='%s/upload/%s' method='POST' enctype='multipart/form-data'>
231         <input type='file' name='newfile' />
232         <input type='text' name='newname' size='20' />
233         <input type='submit' value='UPLOAD' />
234     </form>""" % (lib.Name, cobbler.Template.params['root'], id)
235             return main
236     upload.exposed = True
237
238
239 def localize(path):
240     """If path is relative, return localDir + path, else return path unchanged."""
241     if not os.path.isabs(path):
242         path = os.path.join(localDir, path)
243     return path
244
245 def init_storage():
246    
247     # Override arena.load so that we can add our own sections.
248     import ConfigParser
249     configFileName = os.path.join(localDir, "storage.conf")
250     parser = ConfigParser.ConfigParser()
251     # Make names case-sensitive by overriding optionxform.
252     parser.optionxform = unicode
253     parser.read(configFileName)
254    
255     stores = []
256     for section in parser.sections():
257         opts = dict(parser.items(section))
258         if section in config:
259             config[section].update(opts)
260         else:
261             if opts.get("Class") == "dejavu.storage.storeshelve.StorageManagerShelve":
262                 path = opts.get("Path")
263                 if path:
264                     opts['Path'] = localize(path)
265             stores.append((int(opts.get("Load Order", "0")), section, opts))
266     stores.sort()
267    
268     for order, name, options in stores:
269         arena.add_store(name, options[u'Class'], options)
270    
271     # Register unit classes and upgrade schema.
272     arena.register(Library)
273     arena.register(Resource)
274     arena.register(Tag)
275     ps = PinximusSchema(arena)
276     ps.upgrade()
277     ps.assert_storage()
278    
279     # Compare libraries in conf with libraries in the database.
280     box = arena.new_sandbox()
281     existing = dict([(lib.Path, lib) for lib in box.recall(Library)])
282     for name, path in config['Libraries'].iteritems():
283         path = localize(path)
284         lib = existing.get(path)
285         if lib:
286             lib.Name = name
287         else:
288             lib = Library(Name=name, Path=path)
289             cherrypy.log("Loading library: %s" % path, "DEJAVU")
290             box.memorize(lib)
291             cherrypy.log("Library loaded: %s" % path, "DEJAVU")
292
293 def djvlog(message, flag):
294     """Dejavu logger (writes to error.log)."""
295     if flag & arena.logflags:
296         cherrypy.log(message, "DEJAVU", 0)
297
298 def shutdown_storage():
299     arena.shutdown()
300     cherrypy.log('Dejavu arena shut down.', "DEJAVU", 0)
301
302 def init_web():
303     conf = os.path.join(localDir, "web.conf")
304     conf = cherrypy.config.dict_from_config_file(conf)
305     conf.setdefault("/", {}).update(
306         {'static_filter.on': True,
307          'static_filter.dir': os.path.join(localDir, 'static'),
308          'static_filter.match': '\.(bmp|css|gif|ico|js|jpe?g|png)$',
309          })
310    
311     logfile = conf['global'].get('server.log_file')
312     if logfile:
313         conf['global']['server.log_file'] = localize(logfile)
314     logfile = conf['global'].get('server.log_access_file')
315     if logfile:
316         conf['global']['server.log_access_file'] = localize(logfile)
317    
318     cherrypy.tree.mount(Root(), conf=conf)
319     cobbler.Template.params.update(conf["cobbler"])
320    
321     cherrypy.server.on_stop_server_list.append(shutdown_storage)
322     arena.log = djvlog
Note: See TracBrowser for help on using the browser.