updated README to note that the project isn't working
[yourule] / yourule.py
1 #!/usr/bin/env python3
2
3 # YouRule: Onscreen Ruler Generator
4 #
5 # Copyright (C) 2007 Benjamin Mako Hill <mako@atdot.cc>
6 #
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the Affero General Public License as published
9 # by the Free Software Foundation, either version 1 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 # General Public License for more details.
16 #
17 # You should have received a copy of the Affero General Public License
18 # along with this program.  If not, see
19 # <http://http://www.affero.org/oagpl.html>.
20
21
22
23 import web
24 import sys, os, re
25 from storm.locals import *
26
27 # recalculate the path based on the location of the file
28 sys.path.append(os.path.dirname(__file__))
29 from svgruler import SVGRuler
30
31 from jinja2 import Environment, FileSystemLoader
32 jinja_env = Environment('<%', '%>', '<%=', '%>', '<%#', '%>', 
33                         loader=FileSystemLoader(os.path.dirname(__file__) + "/templates/"))
34
35 def render(filename, vars={}):
36     if re.match(r'.+\.\css$', filename):
37         web.header("Content-Type","text/css; charset=utf-8")
38     else:
39         web.header("Content-Type","text/html; charset=utf-8")
40
41     tmpl = jinja_env.get_template(filename + '.tmpl')
42     vars['homepath'] = web.ctx.homepath
43     vars['ctx'] = web.ctx
44     return tmpl.render(vars)
45
46 # the url map for the application
47 urls = ( '/?', 'index',
48          '/ruler_([0-9\.]+)px_([0-9\.]+)([A-Za-z]+).(svg|png|jpg)', 'ruler_img',
49          '/show/(.*(svg|png|jpg))', 'show_ruler',
50          '/gallery(.*)', 'gallery',
51          '/delete/(\d+)', 'delete',
52          '/undelete/(\d+)', 'undelete',
53          '/style.css', 'css')
54
55 database = create_database("mysql:yourule")
56
57 store = Store(database)
58
59 class Ruler(object):
60     __storm_table__ = "gallery"
61     id = Int(primary=True)
62     pixel_width = Float()
63     unit_width = Float()
64     model = Unicode()
65     units = Unicode()
66     visible = Int()
67     cm_in_ratio = 0.3937
68
69     def __init__(self, **kw):
70         self.pixel_width = float(kw['pixel_width'])
71         self.unit_width = float(kw['unit_width'])
72         self.units = str(kw['units'])
73         if 'model' in kw:
74             self.model = str(kw['model'])
75         else:
76             self.model = ''
77
78     def cm_width(self):
79         if self.units == 'centimeters':
80             return self.unit_width
81         elif self.units == 'inches':
82             return(round(self.unit_width / self.cm_in_ratio, 2))
83
84     def in_width(self):
85         if self.units == 'inches':
86             return self.unit_width
87         elif self.units == 'centimeters':
88             return(round(self.unit_width * self.cm_in_ratio, 2))
89
90     def url(self):
91         return('ruler_%spx_%s%s.png' % (self.pixel_width,
92                                         self.unit_width, self.units))
93
94
95 class index:
96     def GET(self):
97         return render('index', locals())
98
99     def POST(self):
100         input = web.input()
101
102         errormsg = validate_input(input)
103
104         if errormsg:
105             pixel_width = input['pixel_width']
106             unit_width = input['unit_width']
107             units = input['units']
108             return render('index', locals())
109         else:
110             ruler = Ruler(pixel_width = input['pixel_width'], 
111                           unit_width = input['unit_width'], 
112                           units = input['units'])
113             
114             web.redirect('/show/%s' % ruler.url())
115
116 class show_ruler:
117     def GET(self, ruler_url, ext):
118         if 'fromgallery' in web.input():
119             fromgallery = True
120         else:
121             fromgallery = False
122
123         other_unit, other_unit_url = get_other_unit(ruler_url)
124
125         return render('show_ruler', locals())
126
127 class ruler_img:
128     def GET(self, pixel_width=None, unit_width=None, units=None, ext=None):
129
130         # TODO check to see if it's a format that we support
131
132         # set ruler height to be 200 px always
133         pixel_width = float(pixel_width)
134         unit_width = float(unit_width)
135
136         ruler_height = 200
137
138         scale = pixel_width / unit_width
139         ruler_length = int(unit_width)
140
141         ruler = SVGRuler(scale, units, ruler_height, ruler_length)
142
143         # print the header
144         if ext == 'svg': ext = 'svg+xml'
145         web.header("Content-Type", "image/%s" % ext)
146
147         if ext == 'svg+xml':
148             return(ruler.getxml())
149         else:
150             pin, pout = os.popen2('convert -size %sx%s - %s:-' % \
151                                   (pixel_width, ruler_height, ext))
152
153             pin.write(ruler.getxml())
154             pin.close()
155             return(pout.read())
156
157 class gallery:
158     def GET(self, ruler_url):
159
160         if ruler_url:
161             pixel_width, unit_width, units = process_ruler_url(ruler_url)[0:3]
162
163         new_rulers = store.find(Ruler, Ruler.visible == 1)#.order_by(Ruler.model)
164
165         rulers = []
166         for ruler in new_rulers:
167             rulers.append(ruler)
168         
169         #rulers = map(lambda x: x, rulers)
170
171         return render('gallery', locals())
172
173     def POST(self, ruler_url):
174         input = web.input()
175
176         errormsg = validate_input(input)
177         if not input.model:
178             errormsg = 'Please fill out all fields.'
179
180         if errormsg:
181             pixel_width = input['pixel_width']
182             unit_width = input['unit_width']
183             units = input['units']
184             model = input['model']
185         else:
186             new_ruler = Ruler(pixel_width = input['pixel_width'],
187                               unit_width = input['unit_width'],
188                               units = input['units'],
189                               model = input['model'])
190
191             store.add(new_ruler)
192             store.commit()
193
194         rulers = store.find(Ruler, Ruler.visible == 1)
195         #rulers.order_by(Ruler.model)
196         return render('gallery', locals())
197
198 class delete:
199     def GET(self, id):
200         ruler = store.get(Ruler, int(id))
201         ruler.visible = 0
202         store.commit()
203         web.redirect('/gallery')
204
205
206 class undelete:
207     def GET(self, id):
208         ruler = store.get(Ruler, int(id))
209         ruler.visible = 1
210         store.commit()
211         web.redirect('/gallery')
212
213 class css:
214     def GET(self):
215         return render('style.css')
216
217 def get_other_unit(url):
218     pixel_width, unit_width, units = process_ruler_url(url)[0:3]
219
220     ruler = Ruler(pixel_width=pixel_width, unit_width=unit_width, units=units)
221     pixel_width, unit_width, units = process_ruler_url(url)[0:3]
222
223     if units == 'centimeters':
224         units = 'inches'
225         unit_width = ruler.in_width()
226     elif units == 'inches':
227         units = 'centimeters'
228         unit_width = ruler.cm_width()
229
230     new_ruler = Ruler(pixel_width=pixel_width, unit_width=unit_width,
231                       units=units)
232
233     return(units, new_ruler.url())
234
235
236 def process_ruler_url(url):
237     url = re.sub(r'^/?(ruler.*)$', r'\1', url)
238     return(re.match( r'ruler_([\d\.]+)px_([\d\.]+)(\w+).(png|svg|jpg)',
239            url).groups())
240
241 def validate_input(input):
242     errormsg = False
243
244     if not input.pixel_width \
245         or not input.unit_width:
246         errormsg = 'Please fill out all fields.'
247     elif not re.match('^[\d\.]+$', input.pixel_width) \
248         or not re.match('^[\d\.]+$', input.unit_width):
249         errormsg = "Widths must be numbers."
250     elif input['pixel_width'] < 0 \
251         or input['unit_width'] < 0:
252         errormsg = 'Widths must be greater than postive.'
253
254     return(errormsg)
255
256
257 web.webapi.internalerror = web.debugerror
258 if __name__ == "__main__":
259     web.run(urls, globals(), web.reloader)
260
261 app = web.application(urls, globals(), autoreload=False)
262 application = app.wsgifunc()
263

Benjamin Mako Hill || Want to submit a patch?