Initial commit
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
/cards/
|
||||
__pycache__
|
||||
*.pyc
|
||||
|
||||
@@ -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"))]
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
airspeed
|
||||
sanic
|
||||
PyYAML
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<link rel="stylesheet" href="card.css">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||
|
||||
#foreach($card in $cards)
|
||||
<article>
|
||||
<h1>
|
||||
$escape_html($card.title)
|
||||
</h1>
|
||||
<figure>
|
||||
<!--https://material.io/icons/-->
|
||||
<i class="material-icons figure">$escape_html($card.figure)</i>
|
||||
</figure>
|
||||
<main>
|
||||
$escape_html($card.description)
|
||||
<ol>
|
||||
#foreach($item in $card.steps)
|
||||
<li>$escape_html($item)</li>
|
||||
#end
|
||||
</ol>
|
||||
#if($card.flags)
|
||||
<div class="bottom">
|
||||
<small>-
|
||||
#foreach($flag in $card.flags)
|
||||
$escape_html($flag.capitalize()) -
|
||||
#end
|
||||
</small>
|
||||
<br>
|
||||
#if($card.has_flag("mastery"))
|
||||
<big>
|
||||
❏ ❏ ❏ ❏ ❏
|
||||
❏ ❏ ❏ ❏ ❏
|
||||
</big>
|
||||
#end
|
||||
</div>
|
||||
#end
|
||||
</main>
|
||||
<div class="cost">$escape_html($card.cost)</div>
|
||||
<div class="power">$escape_html($card.power)</div>
|
||||
<div class="cp">$escape_html($card.cp)</div>
|
||||
</article>
|
||||
#end
|
||||
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<link rel="stylesheet" href="cardlist.css">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||
|
||||
<form action="show" method="get">
|
||||
#foreach($card in $cards)
|
||||
<input type="checkbox" name="card" value="$escape_html($card.filename)">$escape_html($card.title)<br>
|
||||
#end
|
||||
<br>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user