gdritter repos frony-ritter-designs / 50ccb04
Add initial version of Python files Getty Ritter 4 years ago
2 changed file(s) with 768 addition(s) and 0 deletion(s). Collapse all Expand all
1 #!/usr/bin/python2
2
3 import flask
4 import web
5 import markdown
6 import storage
7 import sys
8
9 web.template.ALLOWED_AST_NODES.append('Constant')
10
11
12 def slugify(string):
13 def process(char):
14 if char in '- \n\t\f':
15 return '-'
16 elif char.isalpha():
17 return char.lower()
18 else:
19 return ''
20 return ''.join(process(c) for c in string)[:40]
21
22
23 def thumb(url):
24 if url:
25 return url[:-4] + '_thumb' + url[-4:]
26
27
28 def five(n):
29 return '{0:05}'.format(n)
30
31
32 db = storage.DB()
33 render = web.template.render('templates/',
34 globals=dict(markdown=markdown.markdown,
35 slugify=slugify,
36 all_categories=db.all_categories,
37 thumb=thumb,
38 five=five))
39
40 concat = '\n'.join
41
42
43 def snip(text):
44 if len(text) < 256:
45 return text
46 else:
47 return text[:256] + '...'
48
49
50 def main(func):
51 def new_func(*args, **kwargs):
52 web.header('Content-Type', 'application/xhtml+xml')
53 return render.main(*func(*args, **kwargs))
54 new_func.__name__ = func.__name__
55 return new_func
56
57
58 class index:
59 @main
60 def GET(self):
61 try:
62 pg = db.get_page('main')
63 return ('Frony Ritter Designs',
64 markdown.markdown(pg.text))
65 except:
66 raise web.notfound()
67
68
69 class contact:
70
71 @main
72 def GET(self):
73 return 'Contact Information', 'Send me an email at my email address.'
74
75
76 class design:
77
78 @main
79 def GET(self, id, slug=None):
80 try:
81 id = int(id)
82 design = db.get_design(id)
83 return 'Designs', render.design_page(*design)
84 except:
85 raise web.notfound()
86
87 def POST(self, id, slug='electric-boogaloo'):
88 try:
89 id = int(id)
90 except:
91 raise web.redirect('/')
92 input = dict(web.input())
93 if ('title' in input and
94 'description' in input and
95 'category' in input and
96 'tags' in input):
97 db.put_design(id,
98 input['title'],
99 input['description'],
100 input['category'])
101 db.process_tag_list(id, input['tags'])
102 if not slug:
103 slug = slugify(input['title'])
104 raise web.redirect('/edit/design/{0:05}/slug/'.format(id, slug))
105
106
107 class all_designs:
108
109 @main
110 def GET(self):
111 winput = web.input(page=0)
112 page = int(winput['page'])
113 min, max = db.max_page_ranges()
114 data = db.get_all(offset=page)
115 return ('Designs',
116 render.design_list(
117 (render.design_tile(name, pics, snip(desc), id)
118 for name, pics, desc, cat, id in data),
119 page,
120 max,
121 page > min,
122 page < max))
123
124
125 class category:
126
127 @main
128 def GET(self, cat):
129 winput = web.input(page=0)
130 page = int(winput['page'])
131 data = db.get_designs_by_category(cat, page)
132 min, max = db.max_page_range_for_category(cat)
133 return (cat.capitalize(),
134 render.by_category_list(
135 cat,
136 (render.design_tile(name, pics, 0, id)
137 for name, pics, desc, cat, id in data),
138 page,
139 page > min,
140 page < (max - 1)))
141
142
143 class category_tag:
144
145 @main
146 def GET(self, cat, tag):
147 winput = web.input(page=0)
148 page = int(winput['page'])
149 data = db.get_designs_by_category_and_tag(cat, tag, page)
150 min, max = db.max_page_range_for_category(cat)
151 return (cat.capitalize(),
152 render.by_category_list(
153 cat,
154 (render.design_tile(name, pics, 0, id)
155 for name, pics, desc, cat, id in data),
156 page,
157 page > min,
158 page < (max - 1)))
159
160
161 class tag:
162
163 @main
164 def GET(self, tag):
165 winput = web.input(page=0)
166 page = int(winput['page'])
167 data = db.get_designs_by_tag(tag, page)
168 min, max = db.max_page_range_for_tag(tag)
169 pretty_tag = ' '.join(w.capitalize()
170 for w in tag.split('_'))
171 return (pretty_tag,
172 render.by_tag_list(
173 tag,
174 (render.design_tile(name, pics, 0, id)
175 for name, pics, desc, cat, id in data),
176 page,
177 page > min,
178 page < max))
179
180
181 class all_categories:
182
183 @main
184 def GET(self):
185 categories = db.all_categories()
186 return 'Category', render.select_category(categories)
187
188
189 class all_tags:
190
191 @main
192 def GET(self):
193 pretty = lambda t: ' '.join(w.capitalize()
194 for w in t.split('_'))
195 tags = (
196 (t, pretty(t), db.num_for_tag(t))
197 for t in sorted(db.get_all_tags()))
198 return 'All Tags', render.select_tag(tags)
199
200
201 class page:
202
203 @main
204 def GET(self, name):
205 try:
206 pg = db.get_page(name)
207 return (pg.title,
208 markdown.markdown(pg.text))
209 except:
210 raise web.notfound()
211
212
213 class admin:
214
215 class edit_all_designs:
216
217
218 @main
219 def GET(self):
220 winput = web.input(page=0)
221 page = int(winput['page'])
222 min, max = db.max_page_ranges()
223 data = db.get_all(offset=page)
224 return ('Edit Mode',
225 render.edit_design_list(
226 ((name, pics, id) for name, pics, _, _, id in data),
227 page,
228 page > min,
229 page < max))
230
231 def POST(self):
232 id = db.new_design()
233 raise web.redirect('/edit/design/{0:05}/'.format(
234 id))
235
236 class edit_design:
237
238
239 @main
240 def GET(self, id, slug=None):
241 try:
242 id = int(id)
243 design = db.get_design(id)
244 tags = ' '.join(db.get_tags_for_design(id))
245 (name, images,
246 description,
247 cat, id) = design
248 return name, render.edit_design(
249 name, images, description, cat, id, tags)
250 except:
251 raise web.notfound()
252
253 class delete_design:
254
255
256 @main
257 def GET(self, id):
258 try:
259 id = int(id)
260 except:
261 raise web.notfound()
262 design = db.get_design(id)
263 return ('Delete',
264 render.delete_design(*design))
265
266
267 def POST(self, id):
268 try:
269 id = int(id)
270 except:
271 raise web.notfound()
272 db.delete_design(id)
273 raise web.redirect('/edit/design/')
274
275 class add_photo:
276
277
278 @main
279 def GET(self):
280 input = web.input()
281 if 'id' in input:
282 return ('Add Photo For Design {0}'.format(input.id),
283 render.photo_upload(input.id))
284 else:
285 return ('Add Generic Photo', render.photo_upload(-1))
286
287
288 def POST(self):
289 input = web.input(file={})
290 try:
291 id = int(input['id'])
292 except:
293 raise web.redirect('/design/')
294 if 'file' in input:
295 photo_id = db.add_photo(input['file'], id)
296 if id == -1:
297 raise web.redirect('/edit/view-photo/{0:09}/'.format(photo_id))
298 else:
299 raise web.redirect('/edit/design/{0:05}/'.format(id))
300
301 class all_files:
302
303
304 @main
305 def GET(self):
306 files = db.all_files()
307 return ('All Uploaded Files', render.all_files(files))
308
309 class add_file:
310
311
312 @main
313 def GET(self):
314 return ('Add File', render.file_upload())
315
316
317 def POST(self):
318 input = web.input(file={})
319 if 'file' in input:
320 file_id = db.add_photo(input['file'])
321 raise web.redirect('/edit/file-list/')
322
323 class modify_photo:
324
325
326 @main
327 def GET(self, id):
328 design_id = web.input().design_id
329 return ('Delete Photo',
330 render.photo_delete(id, design_id))
331
332
333 def POST(self, id):
334 input = web.input()
335 design_id = int(input['design_id'])
336 db.delete_photo(id)
337 raise web.redirect('/edit/design/{0:05}/'.format(
338 design_id))
339
340 class view_all_photos:
341
342
343 @main
344 def GET(self):
345 winput = web.input(page=0)
346 page = int(winput['page'])
347 min, max = db.max_photo_page_ranges()
348 data = db.get_all_photos(offset=page)
349 return ('Photos',
350 render.photo_list((render.photo_tile(p) for p in data),
351 page, max, page > min, page < max))
352
353 class view_photo:
354
355
356 @main
357 def GET(self, id):
358 photo = db.get_photo_by_id(id)
359 if photo:
360 return ('Viewing Photo {0}'.format(id),
361 render.view_photo(id, photo))
362 else:
363 raise web.notfound()
364
365 class edit_page_list:
366
367
368 @main
369 def GET(self):
370 return ('Edit Page List',
371 render.edit_pages(db.get_page_list()))
372
373 class edit_page:
374
375
376 @main
377 def GET(self, name):
378 return (
379 'Edit Page {0}'.format(name),
380 render.edit_page(name, db.get_page(name)))
381
382
383 def POST(self, name):
384 input = web.input()
385 text = input.get('text', '')
386 title = input.get('title', name.capitalize())
387 db.set_page(name, title, text)
388 raise web.redirect('/edit/pages/{0}/'.format(name))
389
390 class edit_about:
391
392
393 @main
394 def GET(self):
395 return (
396 'Edit About',
397 render.edit_about(db.get_about_text()))
398
399
400 def POST(self):
401 input = web.input()
402 text = input.get('text', '')
403 db.set_about_text(text)
404 raise web.redirect('/edit/about/')
405
406
407 urls = (
408
409 # viewing urls
410 '/', index,
411 '/design/?', all_designs,
412 '/design/([^/]*)/([^/]*)/?$', design,
413 '/design/([^/]*)/?$', design,
414 '/category/?', all_categories,
415 '/category/([^/]*)/?', category,
416 '/category/([^/]*)/tag/([^/]*)/?', category_tag,
417 '/tag/?', all_tags,
418 '/tag/([^/]*)/?', tag,
419
420 # editing urls
421 '/edit/design/?', admin.edit_all_designs,
422 '/edit/design/([^/]*)/?', admin.edit_design,
423 '/edit/design/([^/]*)/([^/]*)/?', admin.edit_design,
424 '/edit/delete/([^/]*)/?', admin.delete_design,
425 '/edit/photo/?', admin.add_photo,
426 '/edit/photo/([^/]*)/?', admin.modify_photo,
427 '/edit/view-photo/?', admin.view_all_photos,
428 '/edit/view-photo/([^/]*)/?', admin.view_photo,
429 '/edit/file-list/?', admin.all_files,
430 '/edit/file/?', admin.add_file,
431 '/edit/pages/?', admin.edit_page_list,
432 '/edit/pages/([^/]*)/?', admin.edit_page,
433 '/edit/about/?', admin.edit_about,
434
435 # catch-all for other pages
436 '/([^/]*)/?', page
437
438 )
439
440 # app = web.application(urls, globals())
441 # # web.wsgi.runwsgi = lambda func, addr=None: web.wsgi.runfcgi(func, addr)
442
443 # if __name__ == '__main__':
444 # app.run()
1 import os
2 import web
3 from PIL import Image
4
5 THUMB_SIZE = (100, 100)
6
7
8 class DB:
9
10 def __init__(self, per_page=16):
11 self._db = web.database(dbn='sqlite',
12 db='frony.db')
13 self.per_page = per_page
14 self.categories = {}
15 self.photo_num = self.get_max_photo_num() + 1
16
17 def get_max_photo_num(self):
18 n = 0
19 for file in os.listdir(os.path.join('static', 'photos')):
20 try:
21 n = max(int(file[:5]), n)
22 except:
23 pass
24 return n
25
26 def get_category_id(self, nm):
27 id = list(self._db.where('categories', name=nm))
28 return id and id[0].id
29
30 def all_categories(self):
31 categories = self._db.select('categories')
32 return [(c.name, c.id, c.nicename) for c in categories]
33
34 def get_category_name(self, id):
35 if id in self.categories:
36 return self.categories[id]
37 else:
38 nm = list(self._db.where('categories', id=id))
39 if nm:
40 self.categories[id] = nm[0].name
41 return nm and nm[0].name or 'unknown'
42
43 def get_design(self, id):
44 d = list(self._db.where('designs', id=id))
45 if not d:
46 return None
47 else:
48 d = d[0]
49 return (d.title,
50 self.get_all_photos_for_design(d.id),
51 d.description,
52 self.get_category_name(d.category),
53 d.id)
54
55 def put_design(self, id, title, description, category):
56 description = description.replace('\r\n', '\n')
57 self._db.update(
58 'designs',
59 where='id = $id',
60 vars=dict(id=id),
61 title=title,
62 description=description,
63 category=category
64 )
65
66 def get_photo_by_id(self, id):
67 try:
68 return self._db.where('photos', id=id)[0].filename
69 except:
70 return None
71
72 def get_picture(self, id):
73 try:
74 return (self._db.where('photos', design_id=id,
75 limit=1))[0].filename
76 except:
77 return None
78
79 def get_all_photos(self, offset=0, pp=None):
80 if not pp:
81 pp = self.per_page
82 ps = list(self._db.select('photos',
83 offset=offset * pp,
84 limit=pp,
85 order='id DESC'))
86 return ps
87
88 def get_all_photos_for_design(self, id):
89 return list(d.filename for d in self._db.where('photos',
90 design_id=id))
91
92 def get_all(self, offset=0, pp=None):
93 if not pp:
94 pp = self.per_page
95 ds = list(self._db.select('designs',
96 offset=offset * self.per_page,
97 limit=pp,
98 order='id DESC'))
99 return ((d.title,
100 self.get_picture(d.id),
101 d.description,
102 self.get_category_name(d.category),
103 d.id) for d in ds)
104
105 def get_new_charts(self, pp):
106 ds = list(self._db.select('designs',
107 where='category = 8',
108 limit=pp,
109 order='id DESC'))
110 return ((d.title,
111 self.get_picture(d.id),
112 d.description,
113 self.get_category_name(d.category),
114 d.id) for d in ds)
115
116 def get_designs_by_category(self, cat, offset=0):
117 cat = self.get_category_id(cat)
118 ds = self._db.where('designs',
119 category=cat,
120 offset=offset * self.per_page,
121 limit=self.per_page,
122 order='id DESC')
123 return ((d.title,
124 self.get_picture(d.id),
125 d.description,
126 cat,
127 d.id) for d in ds)
128
129 def get_designs_by_category_and_tag(self, cat, tag, offset=0):
130 cid = self.get_category_id(cat)
131 ds = self._db.query(
132 '''select * from designs, tags
133 where tags.tag_name = $tag
134 and category=$cat
135 and designs.id = tags.design_id
136 order by designs.id desc
137 limit $per_page offset $offset''',
138 vars=dict(tag=tag,
139 cat=cid,
140 offset=offset * self.per_page,
141 per_page=self.per_page))
142
143 return ((d.title,
144 self.get_picture(d.design_id),
145 d.description,
146 cat,
147 d.design_id) for d in ds)
148
149 def new_design(self):
150 new_id = self._db.query(
151 'select max(id) as n from designs')[0].n + 1
152 self._db.insert(
153 'designs',
154 id=new_id,
155 title='New Design',
156 description='',
157 category=0)
158 return new_id
159
160 def delete_design(self, id):
161 self._db.delete(
162 'designs',
163 where='id = $id',
164 vars=dict(id=id))
165
166 def add_file(self, file):
167 new_id = (self._db.query('select max(id) as n from files')[0].n + 1)
168 extn = file.filename.split()[-1]
169 name = '{0:05}.{1}'.format(new_id, extn)
170 path = '/' + os.path.join('srv', 'http', 'frd',
171 'static', 'files', name)
172 self.file_num = self.get_max_file_num() + 1
173 with open(path, 'rb') as f:
174 f.write(file.file)
175 return new_id
176
177 def all_files(self):
178 return list(self._db.where(''))
179
180 def add_photo(self, file, d_id):
181 self.photo_num = self.get_max_photo_num() + 1
182 new_id = (self._db.query(
183 'select max(id) as n from photos')[0].n + 1)
184 new_num = self.photo_num
185 self.photo_num += 1
186 extension = file.filename[-3:]
187 name = '{0:05}.{1}'.format(new_num, extension)
188 thumb = '{0:05}_thumb.{1}'.format(new_num, extension)
189 img = Image.open(file.file)
190 img.thumbnail((400, 400), Image.ANTIALIAS)
191 img.save('/' + os.path.join('srv', 'http',
192 'frd', 'static', 'photos', name))
193 img.thumbnail(THUMB_SIZE, Image.ANTIALIAS)
194 img.save(os.path.join('static', 'photos', thumb))
195 self._db.insert(
196 'photos',
197 id=new_id,
198 filename=name,
199 design_id=d_id)
200 return new_id
201
202 def delete_photo(self, photo_name):
203 self._db.delete(
204 'photos',
205 where='filename = $filename',
206 vars=dict(filename=photo_name))
207
208 def max_photo_page_ranges(self):
209 num_designs = self._db.query(
210 'select count(*) as n from photos')[0].n
211 return (0, (num_designs // self.per_page))
212
213 def max_page_ranges(self):
214 num_designs = self._db.query(
215 'select count(*) as n from designs')[0].n
216 return (0, (num_designs // self.per_page))
217
218 def max_page_range_for_category(self, cat):
219 cat_id = self.get_category_id(cat)
220 num_designs = self._db.query(
221 'select count(*) as n from designs where category = $cat',
222 vars=dict(cat=cat_id))[0].n
223 return (0, 1 + (num_designs // self.per_page))
224
225 def get_all_tags(self):
226 all_tags = [
227 t.tag_name for t in
228 self._db.query(
229 'select distinct tag_name from tags')]
230 return all_tags
231
232 def get_designs_by_tag(self, tag, offset=0):
233 ds = self._db.query(
234 '''select * from designs, tags
235 where tags.tag_name = $tag
236 and designs.id = tags.design_id
237 order by designs.id desc
238 limit $per_page offset $offset''',
239 vars=dict(tag=tag,
240 offset=offset * self.per_page,
241 per_page=self.per_page))
242
243 return ((d.title,
244 self.get_picture(d.design_id),
245 d.description,
246 self.get_category_name(d.category),
247 d.design_id) for d in ds)
248
249 def get_tags_for_design(self, design_id):
250 return [
251 t.tag_name for t in
252 self._db.select(
253 'tags',
254 what='tag_name',
255 where='design_id = $d_id',
256 vars=dict(d_id=design_id))]
257
258 def process_tag_list(self, design_id, tag_string):
259 new_tag_id = (
260 lambda: (self._db.query(
261 'select max(id) as n from tags')[0].n or 0) + 1)
262 tag_list = tag_string.lower().split()
263 self._db.delete(
264 'tags', where='design_id = $d_id',
265 vars=dict(d_id=design_id))
266 for t in tag_list:
267 self._db.insert(
268 'tags',
269 id=new_tag_id(),
270 tag_name=t,
271 design_id=design_id)
272
273 def max_page_range_for_tag(self, tag):
274 num_designs = self._db.query(
275 'select count(*) as n from tags where tag_name = $t',
276 vars=dict(t=tag))[0].n
277 return (0, (num_designs // self.per_page))
278
279 def num_for_tag(self, tag):
280 return self._db.query(
281 'select count(*) as n from tags where tag_name = $t',
282 vars={'t': tag})[0].n
283
284 def get_about_text(self):
285 return self._db.where('pages', name='about')[0].text
286
287 def set_about_text(self, text):
288 self._db.update('pages', where='name = \'about\'',
289 text=text)
290
291 def get_page_list(self):
292 return self._db.query('select * from pages')
293
294 def add_page(self, name, title):
295 self._db.insert('pages', name=name, title=title, text='')
296
297 def get_page(self, name):
298 pg = list(self._db.where('pages', name=name))
299 if pg:
300 return pg[0]
301 else:
302 raise Exception('No page named {0}'.format(name))
303
304 def set_page(self, name, title, text):
305 self._db.update('pages',
306 where='name = $name',
307 title=title,
308 text=text,
309 vars={'name': name})
310
311 def get_clacker(self, new_visitor=None):
312 if new_visitor is not None:
313 exists = self._db.query(
314 'select count(*) as n from visits where visitor = $v',
315 vars={'v': new_visitor})[0].n
316 else:
317 exists = 1
318 total = self._db.query('select count(*) as n from visits')[0].n
319 if exists == 0 and new_visitor:
320 self._db.insert('visits',
321 visitor=new_visitor)
322 return total + 1
323 else:
324 return total