Start to switch to Mustache templates
Getty Ritter
4 years ago
3 | 3 | import flask |
4 | 4 | import web |
5 | 5 | import markdown |
6 | import pystache | |
6 | 7 | import storage |
7 | 8 | import sys |
8 | 9 | |
9 | 10 | web.template.ALLOWED_AST_NODES.append('Constant') |
10 | 11 | |
12 | class Templates: | |
13 | renderer = pystache.Renderer() | |
14 | ||
15 | def load_template(name): | |
16 | with open(f"templates/{name}.mustache") as f: | |
17 | parsed = pystache.parse(f.read()) | |
18 | return lambda stuff: Templates.renderer.render(parsed, stuff) | |
19 | ||
20 | main = load_template('main') | |
21 | design_page = load_template('design_page') | |
11 | 22 | |
12 | 23 | def slugify(string): |
13 | 24 | def process(char): |
30 | 41 | |
31 | 42 | |
32 | 43 | db = storage.DB() |
33 |
render = web.template.render(' |
|
44 | render = web.template.render('old_templates/', | |
34 | 45 | globals=dict(markdown=markdown.markdown, |
35 | 46 | slugify=slugify, |
36 | 47 | all_categories=db.all_categories, |
49 | 60 | |
50 | 61 | def main(func): |
51 | 62 | def new_func(*args, **kwargs): |
52 | web.header('Content-Type', 'application/xhtml+xml') | |
53 | return render.main(*func(*args, **kwargs)) | |
63 | title, content = func(*args, **kwargs) | |
64 | return Templates.main({'title': title, 'content': content}) | |
54 | 65 | new_func.__name__ = func.__name__ |
55 | 66 | return new_func |
56 | 67 | |
60 | 71 | def GET(self): |
61 | 72 | try: |
62 | 73 | pg = db.get_page('main') |
63 | return ('Frony Ritter Designs', | |
64 | markdown.markdown(pg.text)) | |
74 | return 'Frony Ritter Designs', markdown.markdown(pg.text) | |
65 | 75 | except: |
66 | 76 | raise web.notfound() |
67 | 77 | |
68 | 78 | |
69 | 79 | class contact: |
70 | ||
71 | 80 | @main |
72 | 81 | def GET(self): |
73 | 82 | return 'Contact Information', 'Send me an email at my email address.' |
74 | 83 | |
75 | 84 | |
76 | 85 | class design: |
77 | ||
78 | 86 | @main |
79 | 87 | 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() | |
88 | id = int(id) | |
89 | design = db.get_design(id) | |
90 | print(design) | |
91 | return 'Designs', Templates.design_page(design) | |
86 | 92 | |
87 | 93 | def POST(self, id, slug='electric-boogaloo'): |
88 | 94 | try: |
437 | 443 | |
438 | 444 | ) |
439 | 445 | |
440 |
|
|
446 | app = web.application(urls, globals()) | |
441 | 447 | # # web.wsgi.runwsgi = lambda func, addr=None: web.wsgi.runfcgi(func, addr) |
442 | 448 | |
443 | # if __name__ == '__main__': | |
444 | # app.run() | |
449 | if __name__ == '__main__': | |
450 | app.run() |
1 | import markdown | |
1 | 2 | import os |
2 | 3 | import web |
3 | 4 | from PIL import Image |
4 | 5 | |
6 | import typing | |
7 | ||
8 | class Image(typing.NamedTuple): | |
9 | image: str | |
10 | ||
11 | class PageContent: | |
12 | def __init__(self, source): | |
13 | self.source = source | |
14 | self.rendered = markdown.markdown(source) | |
15 | ||
16 | class Design(typing.NamedTuple): | |
17 | title: str | |
18 | images: typing.List[Image] | |
19 | description: PageContent | |
20 | category: str | |
21 | id: int | |
22 | ||
5 | 23 | THUMB_SIZE = (100, 100) |
6 | ||
7 | 24 | |
8 | 25 | class DB: |
9 | 26 | |
46 | 63 | return None |
47 | 64 | else: |
48 | 65 | 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 |
|
|
66 | return Design(title=d.title, | |
67 | images=self.get_all_photos_for_design(d.id), | |
68 | description=PageContent(d.description), | |
69 | category=self.get_category_name(d.category), | |
70 | id=d.id) | |
54 | 71 | |
55 | 72 | def put_design(self, id, title, description, category): |
56 | 73 | description = description.replace('\r\n', '\n') |
79 | 96 | def get_all_photos(self, offset=0, pp=None): |
80 | 97 | if not pp: |
81 | 98 | pp = self.per_page |
82 | ps = list(self._db.select('photos', | |
83 | offset=offset * pp, | |
84 | limit=pp, | |
85 | order='id DESC')) | |
86 |
return |
|
99 | return [ | |
100 | Image(l) for l | |
101 | in self._db.select('photos', | |
102 | offset=offset * pp, | |
103 | limit=pp, | |
104 | order='id DESC') | |
105 | ] | |
87 | 106 | |
88 | 107 | def get_all_photos_for_design(self, id): |
89 | return list(d.filename for d in self._db.where('photos', | |
90 | design_id=id)) | |
108 | return list(Image(d.filename) for d in self._db.where('photos', | |
109 | design_id=id)) | |
91 | 110 | |
92 | 111 | def get_all(self, offset=0, pp=None): |
93 | 112 | if not pp: |
1 | <div id="photo"> | |
2 | <img src="/static/frony.png" /> | |
3 | </div> | |
4 | <div id="about-block"> | |
5 | {{{text}}} | |
6 | </div> |
1 | <div class="design"> | |
2 | <div class="title">{{title}}</div> | |
3 | <div class="images"> | |
4 | {{#images}} | |
5 | <img src="/static/photos/{{image}}" /> | |
6 | {{/images}} | |
7 | </div> | |
8 | <div class="description">{{#description}}{{{ rendered }}}{{/description}}</div> | |
9 | <div class="related"> | |
10 | <a href="/category/{{category}}/">{{category}}</a> | |
11 | </div> | |
12 | </div> |
1 | <!DOCTYPE html> | |
2 | <html> | |
3 | <head> | |
4 | <script> | |
5 | /* Google Analytics malarkey */ | |
6 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ | |
7 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), | |
8 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) | |
9 | })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); | |
10 | ||
11 | ga('create', 'UA-43663896-1', 'fronyritterdesigns.com'); | |
12 | ga('send', 'pageview'); | |
13 | </script> | |
14 | <meta http-equiv="Content-Type" contents="application/xhtml+xml; charset=utf-8;" /> | |
15 | <meta name="description" content="Cross-Stitch Patterns and Other Crafts from Frony Ritter Designs" /> | |
16 | <link rel="stylesheet" type="text/css" href="/static/standard.css" /> | |
17 | <title>Frony Ritter Designs – $title</title> | |
18 | </head> | |
19 | <body> | |
20 | <div id="page"> | |
21 | <div id="header"> | |
22 | <p> | |
23 | <a href="/"><img src="/static/frd_logo.png"/></a> | |
24 | </p> | |
25 | <p>{{ title }}</p> | |
26 | </div> | |
27 | <div id="sidebyside"> | |
28 | <div class="topnav"> | |
29 | <ul> | |
30 | <a href="/"><li class="topitem">Home</li></a> | |
31 | <a href="http://blog.fronyritterdesigns.com/"><li class="topitem">Blog</li></a> | |
32 | ||
33 | <a href="/category/charts"><li class="topitem">Cross-Stitch Charts</li></a> | |
34 | ||
35 | <ul> | |
36 | <a href="/category/charts/tag/celtic"><li class="topitem">Celtic Series</li></a> | |
37 | <a href="/category/charts/tag/spring"><li class="topitem">Spring Series</li></a> | |
38 | <a href="/category/charts/tag/summer"><li class="topitem">Summer Series</li></a> | |
39 | <a href="/category/charts/tag/fall"><li class="topitem">Fall Series</li></a> | |
40 | <a href="/category/charts/tag/winter"><li class="topitem">Winter Series</li></a> | |
41 | <a href="/category/charts/tag/flags"><li class="topitem">Flags</li></a> | |
42 | <a href="/category/charts/tag/booklets"><li class="topitem">Booklets</li></a> | |
43 | <a href="/category/charts/tag/kits"><li class="topitem">Kits</li></a> | |
44 | <a href="/category/charts/tag/minis"><li class="topitem">Minis</li></a> | |
45 | <a href="/category/charts/tag/large"><li class="topitem">Large Charts</li></a> | |
46 | <a href="/category/charts/tag/recipe"><li class="topitem">Small-Batch Recipe</li></a> | |
47 | </ul> | |
48 | ||
49 | <a href="/category/papercrafting" ><li class="topitem" >Papercraft Ideas</li></a> | |
50 | <ul> | |
51 | <a href="/tag/cards"><li class="topitem">Cards</li></a> | |
52 | <a href="/tag/tags"><li class="topitem">Tags</li></a> | |
53 | <a href="/tag/placecards"><li class="topitem">Placecards</li></a> | |
54 | <a href="/tag/boxes"><li class="topitem">Boxes</li></a> | |
55 | <a href="/tag/bags"><li class="topitem">Bags</li></a> | |
56 | </ul> | |
57 | ||
58 | <a href="/category/beading" ><li class="topitem" >Beading</li></a> | |
59 | ||
60 | <a href="/category/paintings"><li class="topitem">Painting</li></a> | |
61 | ||
62 | <a href="/tag/free"><li class="topitem">Free Patterns</li></a> | |
63 | <ul> | |
64 | <a href="/category/charts/tag/free"><li class="topitem">Cross-Stitch</li></a> | |
65 | <a href="/category/paintings/tag/free"><li class="topitem">Painting</li></a> | |
66 | <a href="/category/beading/tag/free"><li class="topitem">Beading</li></a> | |
67 | </ul> | |
68 | ||
69 | <a href="/category/gallery"><li class="topitem">Stitchers' Gallery</li></a> | |
70 | ||
71 | <a href="/wheretobuy"><li class="topitem">Where To Buy</li></a> | |
72 | <a href="/retailinfo"><li class="topitem">Info For Retailers</li></a> | |
73 | <a href="/corrections/"><li class="topitem">Corrections</li></a> | |
74 | <a href="/about/"><li class="topitem">About</li></a> | |
75 | ||
76 | </ul> | |
77 | </div> | |
78 | <div id="contents"> | |
79 | {{{ content }}} | |
80 | </div> | |
81 | <div style="clear: both;"></div> | |
82 | </div> | |
83 | <div id="footer"> | |
84 | ©2020 | <a href="/about/">About</a> | <a href="/contact/">Contact</a> | |
85 | </div> | |
86 | </div> | |
87 | </body> | |
88 | </html> |