From 7d7642ead65f7f992cc2a6ec93f56993b05e933e Mon Sep 17 00:00:00 2001 From: Peder Bergebakken Sundt Date: Sun, 24 Sep 2017 18:07:50 +0200 Subject: [PATCH] Initial commit --- .gitignore | 4 ++ card.py | 30 +++++++++ cards/.folder | 0 common.py | 63 +++++++++++++++++++ config.py | 3 + requirements.txt | 3 + resources/cards/card.css | 114 +++++++++++++++++++++++++++++++++++ resources/cards/card.vm | 42 +++++++++++++ resources/cards/cardlist.css | 0 resources/cards/cardlist.vm | 11 ++++ server.py | 46 ++++++++++++++ 11 files changed, 316 insertions(+) create mode 100644 .gitignore create mode 100644 card.py create mode 100644 cards/.folder create mode 100644 common.py create mode 100644 config.py create mode 100644 requirements.txt create mode 100644 resources/cards/card.css create mode 100644 resources/cards/card.vm create mode 100644 resources/cards/cardlist.css create mode 100644 resources/cards/cardlist.vm create mode 100755 server.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c0b553e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/cards/ +__pycache__ +*.pyc + diff --git a/card.py b/card.py new file mode 100644 index 0000000..fa5e8c1 --- /dev/null +++ b/card.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +import glob, os +from yaml import load, dump +from common import Model +import config + +class Card(Model): + filename = "[[filename_without_file_extention]]" + title = "[[title]]" + figure = "code"#https://material.io/icons/ + description = "[[description]]" + steps = ["Do a", "Then do b"] + cost = "[[cost]]" + power = "[[power]]" + cp = "[[cp]]" + flags = [] + + def has_flag(self, flag): return flag in self.flags + +def from_file(filename, in_carddir=True):#yaml syntax + os.path.join(config.carddir, filename) if in_carddir else filename + ret = Card() + ret.filename = ".".join(os.path.basename(filename).split(".")[:-1]) + with open(os.path.join(config.carddir, filename) if in_carddir else filename, "r") as f: + for key, val in load(f.read()).items(): + setattr(ret, key, val) + return ret + +def from_dir(path): + return [from_file(i, in_carddir=False) for i in glob.glob(os.path.join(path, "*.yaml"))] diff --git a/cards/.folder b/cards/.folder new file mode 100644 index 0000000..e69de29 diff --git a/common.py b/common.py new file mode 100644 index 0000000..d81f0c9 --- /dev/null +++ b/common.py @@ -0,0 +1,63 @@ +import airspeed, config, os, html + +def readfile(path, binary=False): + with open(path, "rb" if binary else "r") as f: + return f.read() + +def escape_html(data): + return html.escape(str(data)) + +def escape_url(data): + return "ølailsf" + +class Model: + def __setattr__(self, name, value): + if not hasattr(self, name): + raise Exception(f"{self.__class__.__name__} has no attribute {name!r}") + super(Model, self).__setattr__(name, value) + def __repr__(self): + return "Card(%s)" % \ + ", ".join(f"{i}={getattr(self, i)!r}" for i in dir(self) if "_" not in i) + __str__ = __repr__ + +#decorators with parameters: +def withResource(path, binary=False): + data = readfile(os.path.join(config.resourcedir, path), binary) + + def decorator(func): + def newfunc(*args, **kwargs): + if not config.cache: + with open(os.path.join(config.resourcedir, path), "rb" if binary else "r") as f: + content = f.read() + else: + content = data + if "file" in kwargs: + kwargs["file"][os.path.basename(path)] = content + else: + kwargs["file"] = {os.path.basename(path): content} + return func(*args, **kwargs) + return newfunc + return decorator + +def withTemplate(path): + template = airspeed.Template(readfile(os.path.join(config.resourcedir, path))) + + def decorator(func): + def newfunc(*args, **kwargs): + if not config.cache: + t = airspeed.Template(readfile(os.path.join(config.resourcedir, path))) + else: + t = template + + class T: + def merge(self, objects): + objects.update({"escape_html":escape_html, "escape_url":escape_url}) + return t.merge(objects) + + if "template" in kwargs: + kwargs["template"][os.path.basename(path)] = T() + else: + kwargs["template"] = {os.path.basename(path): T()} + return func(*args, **kwargs) + return newfunc + return decorator diff --git a/config.py b/config.py new file mode 100644 index 0000000..832bae7 --- /dev/null +++ b/config.py @@ -0,0 +1,3 @@ +cache = False +resourcedir = "resources/" +carddir = "cards/" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c49b714 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +airspeed +sanic +PyYAML diff --git a/resources/cards/card.css b/resources/cards/card.css new file mode 100644 index 0000000..b9d08cc --- /dev/null +++ b/resources/cards/card.css @@ -0,0 +1,114 @@ +* { + margin: 0; padding: 0; border: 0; +} + +@page { + size: A4; + margin: 1.2cm; +} +:root { + font-family: sans-serif; + font-size: 3mm; + font-weight: 300; +} +body { + width: 21cm; + height: 29.7cm; +} + +article { + margin: 3mm 5mm; + display: block; + float: left; + width: 5.5cm; + height: 8.9cm; + + display: grid; + grid-template-columns: 2fr 1fr; + grid-template-rows: 2em 18mm 1.3em auto 1.5em; + grid-template-areas: + "header header" + "figure figure" + "cost cost" + "info info" + "power cp"; + + border-radius: 2mm; + grid-gap: 2mm 2mm; + border-style: solid; + border-width: 2mm; + border-color: #333; + background-color: #333; + +} + +article >* { + background-color: #ddd; + text-align: center; + line-height: 1.4em; + padding: 0 1mm; +} + +article h1 { + grid-area: header; + font-size: 1em; + padding: 1mm 0; + font-size: 1.1em; + font-weight: 600; +} + +article figure { + grid-area: figure; + background: #333; +} + +article figure img{ + height: 100%; + width: auto; +} +article figure .material-icons.figure { + font-size: 1.7cm; + color: #999; +} + +article main { + padding: 2mm 0; + grid-area: info; + position: relative; +} +article main ol { + margin-top: 1mm; + text-align: left; + padding-left: 6mm; +} +article main ol li + li{ + margin-top: 0.5mm; +} +article main big{ + font-size: 1.5em; +} +article main .bottom { + width: 100%; + position: absolute; + bottom: 2mm; +} + +article .power { + grid-area: power; + text-align: left; +} +article .power:before{ + content: "Power: "; + font-size: 0.7em; +} +article .cost { + grid-area: cost; +} +article .cp { + grid-area: cp; + text-align: right; +} +article .cp:after{ + content: " CP"; + font-size: 0.7em; +} diff --git a/resources/cards/card.vm b/resources/cards/card.vm new file mode 100644 index 0000000..837269d --- /dev/null +++ b/resources/cards/card.vm @@ -0,0 +1,42 @@ + + + + +#foreach($card in $cards) +
+

+ $escape_html($card.title) +

+
+ + $escape_html($card.figure) +
+
+ $escape_html($card.description) +
    + #foreach($item in $card.steps) +
  1. $escape_html($item)
  2. + #end +
+ #if($card.flags) +
+ - + #foreach($flag in $card.flags) + $escape_html($flag.capitalize()) - + #end + +
+ #if($card.has_flag("mastery")) + + ❏ ❏ ❏ ❏ ❏ + ❏ ❏ ❏ ❏ ❏ + + #end +
+ #end +
+
$escape_html($card.cost)
+
$escape_html($card.power)
+
$escape_html($card.cp)
+
+#end diff --git a/resources/cards/cardlist.css b/resources/cards/cardlist.css new file mode 100644 index 0000000..e69de29 diff --git a/resources/cards/cardlist.vm b/resources/cards/cardlist.vm new file mode 100644 index 0000000..64c6747 --- /dev/null +++ b/resources/cards/cardlist.vm @@ -0,0 +1,11 @@ + + + + +
+ #foreach($card in $cards) + $escape_html($card.title)
+ #end +
+ +
diff --git a/server.py b/server.py new file mode 100755 index 0000000..4cabba8 --- /dev/null +++ b/server.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +import sys, os, airspeed, glob +from sanic import Sanic, response +from common import withTemplate, withResource +import card +import config + +app = Sanic() + +@app.get("/") +async def root(request): + return response.redirect('/cards/') + +@app.get("/cards/") +@withTemplate("cards/cardlist.vm") +async def show_cardlist(request, template={}): + cards = card.from_dir(config.carddir) + + return response.html(template["cardlist.vm"].merge(locals())) + +@app.get('/cards/show') +@withTemplate("cards/card.vm") +async def show_cards(request, template={}): + if "card" not in request.args: + return response.redirect('/cards/') + + cards = [] + for i in request.args["card"]: + if "/" in i or "\\" in i: + return response.redirect('/cards/') + cards.append(card.from_file(i+".yaml")) + return response.html(template["card.vm"].merge(locals())) + + +#add static files: +for i in glob.iglob(os.path.join(config.resourcedir, "**","*"), recursive=True): + if i.split(".")[-1] in ("html", "css", "js"): + i = os.path.relpath(i, config.resourcedir) + print(i) + @app.get(f"/{i}") + @withResource(i) + async def card_style(request, file={}): + return response.text(tuple(file.values())[0], headers={"Content-Type": f"text/{i.split('.')[-1]}"}) + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=8000)