Browse Source

Strategická: Další kousek webu

master
Jiří Kalvoda 2 years ago
parent
commit
a2ca047f55
  1. 169
      server/hra/web/html.py
  2. 109
      server/hra/web/pages.py

169
server/hra/web/html.py

@ -1,5 +1,6 @@
from flask import Markup, escape from flask import Markup, escape
from typing import List, Optional, Union, Tuple from typing import List, Optional, Union, Tuple
import types
tag_names = ["a", "abbr", "acronym", "address", "applet", "area", "article", "aside", "audio", "b", "base", "basefont", "bdi", "bdo", "big", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "data", "datalist", "dd", "del", "details", "dfn", "dialog", "dir", "div", "dl", "dt", "em", "embed", "fieldset", "figcaption", "figure", "font", "footer", "form", "frame", "frameset", "head", "header", "hgroup", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "html", "i", "iframe", "img", "input", "ins", "kbd", "keygen", "label", "legend", "li", "link", "main", "map", "mark", "menu", "menuitem", "meta", "meter", "nav", "noframes", "noscript", "object", "ol", "optgroup", "option", "output", "p", "param", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "script", "section", "select", "small", "source", "span", "strike", "strong", "style", "sub", "summary", "sup", "svg", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "tt", "u", "ul", "var", "video", "wbr"] tag_names = ["a", "abbr", "acronym", "address", "applet", "area", "article", "aside", "audio", "b", "base", "basefont", "bdi", "bdo", "big", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "data", "datalist", "dd", "del", "details", "dfn", "dialog", "dir", "div", "dl", "dt", "em", "embed", "fieldset", "figcaption", "figure", "font", "footer", "form", "frame", "frameset", "head", "header", "hgroup", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "html", "i", "iframe", "img", "input", "ins", "kbd", "keygen", "label", "legend", "li", "link", "main", "map", "mark", "menu", "menuitem", "meta", "meter", "nav", "noframes", "noscript", "object", "ol", "optgroup", "option", "output", "p", "param", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "script", "section", "select", "small", "source", "span", "strike", "strong", "style", "sub", "summary", "sup", "svg", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "tt", "u", "ul", "var", "video", "wbr"]
@ -23,31 +24,34 @@ def escape_attribute_name(x:str) -> str:
def escape_tag_name(x:str) -> str: def escape_tag_name(x:str) -> str:
return escape_attribute_name(x) return escape_attribute_name(x)
Element = Union[str, Markup, 'Tag'] Element = Union[str, Markup, 'Bucket']
class Tag: def indent_str(indent, s):
name: str return " "*indent + s + "\n" if indent is not None else s
attributes: List[Tuple[str, str]]
class Bucket:
content: List[Element] content: List[Element]
is_paired: bool is_paired: bool
builder: 'Builder' builder: 'Builder'
def __init__(self, builder):
def __init__(self, builder, name: str, attributes: List[Tuple[str, str]]):
self.builder = builder self.builder = builder
self.name = name
self.attributes = attributes
self.is_paired = False self.is_paired = False
self.content = [] self.content = []
self.before_tag = None self.before_tag = None
def add(self, x: Element): def add(self, x: Element):
if isinstance(x, types.FunctionType):
before_current_tag = self.builder.current_tag
self.builder.current_tag = self
try:
x(self)
finally:
self.builder.current_tag = before_current_tag
else:
self.is_paired = True self.is_paired = True
self.content.append(x) self.content.append(x)
def add_attribute(k, v):
self.attributes.append((k, v))
def __call__(self, *arg, **kvarg): def __call__(self, *arg, **kvarg):
for i in arg: for i in arg:
self.add(i) self.add(i)
@ -60,27 +64,13 @@ class Tag:
self.add(t) self.add(t)
return t return t
def format_attributes(self): def line(self):
return " ".join(f'{escape_attribute_name(i[0])}="{escape_attribute(i[1])}"' for i in self.attributes) t = Line(self.builder)
self.add(t)
return t
def serialize_append_to_list(self, out, indent): def _line(self):
indent_str = " " return Line(self)
if self.is_paired:
out.append(indent_str*indent + f"<{escape_tag_name(self.name)} {self.format_attributes()}>\n")
indent += 1
for i in self.content:
if isinstance(i, Tag):
i.serialize_append_to_list(out, indent)
elif isinstance(i, Markup):
for j in i.__html__().split("\n"):
out.append(indent_str*indent + j + "\n")
else:
for j in escape(str(i)).split("\n"):
out.append(indent_str*indent + j + "\n")
indent -= 1
out.append(indent_str*indent + f"</{escape_tag_name(self.name)}>\n")
else:
out.append(indent_str*indent + f"<{escape_tag_name(self.name)} {self.format_attributes()} \>\n")
def print(self): def print(self):
out = [] out = []
@ -95,42 +85,105 @@ class Tag:
def __enter__(self): def __enter__(self):
if self.before_tag is not None: if self.before_tag is not None:
raise RuntimeError("Duplicit __enter__") raise RuntimeError("Duplicit __enter__")
self.before_tag = self.builder._current_tag self.before_tag = self.builder.current_tag
self.builder._current_tag = self self.builder.current_tag = self
return self return self
def __exit__(self, exc_type, exc_value, exc_traceback): def __exit__(self, exc_type, exc_value, exc_traceback):
if self.before_tag is None: if self.before_tag is None:
raise RuntimeError("__exit__ before __enter__") raise RuntimeError("__exit__ before __enter__")
self.builder._current_tag = self.before_tag self.builder.current_tag = self.before_tag
self.before_tag = None self.before_tag = None
class Tag(Bucket):
name: str
attributes: List[Tuple[str, str]]
def __init__(self, builder, name: str, attributes: List[Tuple[str, str]]):
super().__init__(builder)
self.name = name
self.attributes = attributes
def add_attribute(k, v):
self.attributes.append((k, v))
def format_attributes(self):
return " ".join(f'{escape_attribute_name(i[0])}="{escape_attribute(i[1])}"' for i in self.attributes)
def serialize_append_to_list(self, out, indent):
if self.is_paired:
out.append(indent_str(indent, f"<{escape_tag_name(self.name)} {self.format_attributes()}>"))
if indent is not None:
indent += 1
for i in self.content:
if isinstance(i, Bucket):
i.serialize_append_to_list(out, indent)
elif isinstance(i, Markup):
for j in i.__html__().split("\n"):
out.append(indent_str(indent, j))
else:
for j in escape(str(i)).split("\n"):
out.append(indent_str(indent, j))
if indent is not None:
indent -= 1
out.append(indent_str(indent, f"</{escape_tag_name(self.name)}>"))
else:
out.append(indent_str(indent, f"<{escape_tag_name(self.name)} {self.format_attributes()} />"))
class Line(Bucket):
def serialize_append_to_list(self, out, indent):
out.append(indent_str(indent,"")[:-1])
for i in self.content:
if isinstance(i, Bucket):
i.serialize_append_to_list(out, None)
elif isinstance(i, Markup):
out.append(i.__html__())
out.append(escape(str(i)))
out.append("\n")
class Builder: class Builder:
_current_tag: Tag current_tag: Bucket
_root_tag: Tag root_tag: Bucket
def __init__(self, tag: Tag): def __init__(self, tag: Bucket):
self._root_tag = tag self.root_tag = tag
self._current_tag = tag self.current_tag = tag
def add_tag(self, name: str, attributes: List[Tuple[str, str]] = []):
return self.current_tag.add_tag(name, attributes)
def _tag(self, name: str, attributes: List[Tuple[str, str]] = []): def line(self):
return self._current_tag.add_tag(name, attributes) return self.current_tag.line()
def _line(self):
return Line(self)
def __call__(self, *arg, **kvarg): def __call__(self, *arg, **kvarg):
self._current_tag(*arg, **kvarg) self.current_tag(*arg, **kvarg)
return self return self
def _print(self): def print(self):
return self._root_tag.print() return self.root_tag.print()
def _print_file(self): def print_file(self):
return self._root_tag.print_file() return self.root_tag.print_file()
for name in tag_names: for name in tag_names:
def run(name): def run(name):
def l(self, **kvarg): def l1(self, *args, **kvarg):
return self._tag(name, [(remove_leading_underscore(k), v) for k, v in kvarg.items()]) return self.add_tag(name, [(remove_leading_underscore(k), v) for k, v in kvarg.items()])(*args)
setattr(Builder, name, l) def l2(self, *args, **kvarg):
return Tag(self, name, [(remove_leading_underscore(k), v) for k, v in kvarg.items()])(*args)
def l3(self, *args, **kvarg):
return Tag(self.builder, name, [(remove_leading_underscore(k), v) for k, v in kvarg.items()])(*args)
setattr(Builder, name, l1)
setattr(Builder, f"_{name}", l2)
setattr(Bucket, name, l1)
setattr(Bucket, f"_{name}", l3)
run(name) run(name)
@ -149,7 +202,7 @@ def remove_leading_underscore(s):
class WrapAfterBuilder(Builder): class WrapAfterBuilder(Builder):
def __init__(self, f): def __init__(self, f):
super().__init__(Tag(self, "root", [])) super().__init__(Bucket(self))
self._wrap_done = False self._wrap_done = False
self._wrap_function = f self._wrap_function = f
@ -157,18 +210,18 @@ class WrapAfterBuilder(Builder):
if self._wrap_done: if self._wrap_done:
return return
self._wrap_done = True self._wrap_done = True
content = self._root_tag.content content = self.root_tag.content
self._root_tag = None self.root_tag = None
self._current_tag = None self.current_tag = None
self._root_tag = self._wrap_function(self, content, *arg, **kvarg) or self._root_tag self.root_tag = self._wrap_function(self, content, *arg, **kvarg) or self.root_tag
def _print(self, *arg, **kvarg): def print(self, *arg, **kvarg):
self._wrap() self._wrap()
return super()._print(*arg, **kvarg) return super().print(*arg, **kvarg)
def _print_file(self, *arg, **kvarg): def print_file(self, *arg, **kvarg):
self._wrap() self._wrap()
return super()._print_file(*arg, **kvarg) return super().print_file(*arg, **kvarg)
def WrapAfterBuilder_decorator(f): def WrapAfterBuilder_decorator(f):

109
server/hra/web/pages.py

@ -1,4 +1,4 @@
from flask import Flask, redirect, flash, render_template, session, g, request, get_flashed_messages from flask import Flask, redirect, flash, session, g, request, get_flashed_messages
from wtforms import Form, BooleanField, StringField, PasswordField, validators, SubmitField, IntegerField, DateTimeField from wtforms import Form, BooleanField, StringField, PasswordField, validators, SubmitField, IntegerField, DateTimeField
from wtforms.validators import ValidationError from wtforms.validators import ValidationError
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
@ -24,8 +24,8 @@ from hra.util import hash_passwd
@html.WrapAfterBuilder_decorator @html.WrapAfterBuilder_decorator
def BasePage(b, content): def BasePage(b, content):
b._current_tag = html.Tag(b, "html", []) b.current_tag = html.Tag(b, "html", [])
b._root_tag = b._current_tag b.root_tag = b.current_tag
with b.head(): with b.head():
b.title()("KSP hra") b.title()("KSP hra")
b.meta(name="viewport", content="width=device-width, initial-scale=1.0") b.meta(name="viewport", content="width=device-width, initial-scale=1.0")
@ -54,7 +54,7 @@ def BasePage(b, content):
if messages: if messages:
for category, message in messages: for category, message in messages:
if category == "message": if category == "message":
category = warning category = "warning"
b.div(_class=f"alert alert-{category}", role="alert")(message) b.div(_class=f"alert alert-{category}", role="alert")(message)
b(*content) b(*content)
@ -107,13 +107,13 @@ def registration():
db.get_session().commit() db.get_session().commit()
except exc.IntegrityError: except exc.IntegrityError:
flash("Uživatelské jméno již existuje") flash("Uživatelské jméno již existuje")
return render_template('registration.html', form=f) else:
flash("Přidán nový uživatel.", 'success') flash("Přidán nový uživatel.", 'success')
return redirect("login") return redirect("login")
b = BasePage() b = BasePage()
b(jinja_mac.quick_form(f, form_type='horizontal')) b(jinja_mac.quick_form(f, form_type='horizontal'))
return b._print_file() return b.print_file()
@app.route("/login", methods=['GET', 'POST']) @app.route("/login", methods=['GET', 'POST'])
def login(): def login():
@ -131,7 +131,7 @@ def login():
b = BasePage() b = BasePage()
b(jinja_mac.quick_form(f, form_type='horizontal')) b(jinja_mac.quick_form(f, form_type='horizontal'))
return b._print_file() return b.print_file()
@app.route("/logout") @app.route("/logout")
@ -167,84 +167,41 @@ def web_index():
if g.user: if g.user:
with b.p(): with b.p():
b(f"Váš token je: {g.user.token}") b(f"Váš token je: {g.user.token}")
return b._print_file() return b.print_file()
@app.route("/org/users") @app.route("/org/users")
def web_users(): def web_users():
users = db.get_session().query(db.User).all() users = db.get_session().query(db.User).all()
return render_template("org_users.html", users=users) b = BasePage()
with b.p().table(_class="data full"):
with b.thead():
b.line().th()("Username")
b.line().th()("Hry")
b.line().th()("Akce")
for u in users:
with b.tr():
b.line().th()(u.username)
b.line().th()
with b.th():
b.a(_class="btn btn-xs btn-primary", href=app.url_for(web_org_user.__name__, user_id=u.id))("Detail")
return b.print_file()
@app.route("/org/user/<int:user_id>", methods=['GET', 'POST']) @app.route("/org/user/<int:user_id>", methods=['GET', 'POST'])
def web_orgf_user(user_id): def web_org_user(user_id):
user = db.get_session().query(db.User).filter_by(id=user_id).one_or_none() user = db.get_session().query(db.User).filter_by(id=user_id).one_or_none()
f = FindingForm()
del f.user
if f.validate_on_submit():
f.fill_empty()
find = Findings(user=user.id, code=f.f_code, time=f.f_time, round=get_round_id())
db.get_session().add(find)
db.get_session().commit()
flash(f"Kód {find.code}… přijat", 'success')
f.code.data = ""
return redirect(f"/org/user/{user_id}")
if not user: if not user:
raise werkzeug.exceptions.NotFound() raise werkzeug.exceptions.NotFound()
calc_point(user) b = BasePage()
findings = db.get_session().query(Findings).filter_by(user=user.id, round=get_round_id()).order_by(Findings.time).all() b.line().h2("Uživatel ", user.username)
return render_template("org_user.html", user=user, findings=findings, form=f) with b.div(_class="btn-group", role="group"):
with b.form(method="POST", _class="btn-group", action=app.url_for(web_org_user_su.__name__, user_id=user.id)):
@app.route("/org/admin", methods=['GET', 'POST']) b.button(_class="btn btn-default", type="submit", name="su", value="yes")("Převtělit")
def web_admin(): return b.print_file()
obj_round = get_round()
f_round = FormRound(obj=obj_round, prefix="r") @app.route("/org/user/<int:user_id>/su", methods=['POST'])
if f_round.validate_on_submit(): def web_org_user_su(user_id):
f_round.populate_obj(obj_round) user = db.get_session().query(db.User).filter_by(id=user_id).one_or_none()
db.get_session().commit() session['uid'] = user.id
return render_template("org_admin.html", f_round=f_round)
@app.route("/org/act", methods=['GET', 'POST'])
def web_act():
obj_act_round = db.get_session().query(ActualRound).one()
f_act_round = FormActRound(obj=obj_act_round, prefix="act")
if f_act_round.validate_on_submit():
f_act_round.populate_obj(obj_act_round)
db.get_session().commit()
return render_template("org_act.html", f_act_round=f_act_round)
class SuForm(FlaskForm):
username = StringField('Uživatel')
time_move = OptionalIntField('Časový posun')
round = OptionalIntField('Kolo')
submit = SubmitField("Změnit")
def validate_username(form, field):
form.f_user = db.get_session().query(db.User).filter_by(username=field.data).one_or_none()
if form.f_user == None and field.data:
raise ValidationError("Uživatel neexistuje.")
@app.route("/org/su", methods=['GET', 'POST'])
def web_su():
f = SuForm()
if not f.is_submitted():
f.username.data = g.user.username
if f.validate_on_submit():
if not f.f_user or session['uid'] != f.f_user.id:
session['uid'] = f.f_user.id if f.f_user else None
flash("Uživatel vtělen!") flash("Uživatel vtělen!")
if f.time_move.data is not None:
session['time_move'] = f.time_move.data * 1000
flash("Čas vtělen!")
if f.round.data is not None:
session['round'] = f.round.data
flash("Kolo vtěleno!")
return redirect('/') return redirect('/')
return render_template("org_su.html", f=f)

Loading…
Cancel
Save