From a2ca047f5580115c19bb5646d203ad03823eb898 Mon Sep 17 00:00:00 2001 From: Jiri Kalvoda Date: Mon, 12 Sep 2022 21:54:42 +0200 Subject: [PATCH] =?UTF-8?q?Strategick=C3=A1:=20Dal=C5=A1=C3=AD=20kousek=20?= =?UTF-8?q?webu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/bin/control_game | 4 +- server/hra/web/html.py | 173 ++++++++++++++++++++++++++-------------- server/hra/web/pages.py | 117 +++++++++------------------ 3 files changed, 152 insertions(+), 142 deletions(-) diff --git a/server/bin/control_game b/server/bin/control_game index 5466d4d..a0fe2bc 100755 --- a/server/bin/control_game +++ b/server/bin/control_game @@ -17,6 +17,6 @@ parser.add_argument("--restore", action="store_true") args = parser.parse_args() if args.restore: - lib.game_restore_broken(args.game_id) + lib.game_restore_broken(args.game_id) if args.step: - lib.game_step(args.game_id) + lib.game_step(args.game_id) diff --git a/server/hra/web/html.py b/server/hra/web/html.py index 42f7725..6604e74 100644 --- a/server/hra/web/html.py +++ b/server/hra/web/html.py @@ -1,5 +1,6 @@ from flask import Markup, escape 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"] @@ -23,30 +24,33 @@ def escape_attribute_name(x:str) -> str: def escape_tag_name(x:str) -> str: return escape_attribute_name(x) -Element = Union[str, Markup, 'Tag'] +Element = Union[str, Markup, 'Bucket'] -class Tag: - name: str - attributes: List[Tuple[str, str]] +def indent_str(indent, s): + return " "*indent + s + "\n" if indent is not None else s + +class Bucket: content: List[Element] is_paired: bool builder: 'Builder' - - def __init__(self, builder, name: str, attributes: List[Tuple[str, str]]): + def __init__(self, builder): self.builder = builder - self.name = name - self.attributes = attributes self.is_paired = False self.content = [] self.before_tag = None def add(self, x: Element): - self.is_paired = True - self.content.append(x) - - def add_attribute(k, v): - self.attributes.append((k, v)) + 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.content.append(x) def __call__(self, *arg, **kvarg): for i in arg: @@ -60,27 +64,13 @@ class Tag: self.add(t) return t - def format_attributes(self): - return " ".join(f'{escape_attribute_name(i[0])}="{escape_attribute(i[1])}"' for i in self.attributes) + def line(self): + t = Line(self.builder) + self.add(t) + return t - def serialize_append_to_list(self, out, indent): - indent_str = " " - 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"\n") - else: - out.append(indent_str*indent + f"<{escape_tag_name(self.name)} {self.format_attributes()} \>\n") + def _line(self): + return Line(self) def print(self): out = [] @@ -95,42 +85,105 @@ class Tag: def __enter__(self): if self.before_tag is not None: raise RuntimeError("Duplicit __enter__") - self.before_tag = self.builder._current_tag - self.builder._current_tag = self + self.before_tag = self.builder.current_tag + self.builder.current_tag = self return self def __exit__(self, exc_type, exc_value, exc_traceback): if self.before_tag is None: raise RuntimeError("__exit__ before __enter__") - self.builder._current_tag = self.before_tag + self.builder.current_tag = self.before_tag 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"")) + 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: - _current_tag: Tag - _root_tag: Tag - def __init__(self, tag: Tag): - self._root_tag = tag - self._current_tag = tag + current_tag: Bucket + root_tag: Bucket + def __init__(self, tag: Bucket): + self.root_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]] = []): - return self._current_tag.add_tag(name, attributes) + def line(self): + return self.current_tag.line() + + def _line(self): + return Line(self) def __call__(self, *arg, **kvarg): - self._current_tag(*arg, **kvarg) + self.current_tag(*arg, **kvarg) return self - def _print(self): - return self._root_tag.print() + def print(self): + return self.root_tag.print() - def _print_file(self): - return self._root_tag.print_file() + def print_file(self): + return self.root_tag.print_file() for name in tag_names: def run(name): - def l(self, **kvarg): - return self._tag(name, [(remove_leading_underscore(k), v) for k, v in kvarg.items()]) - setattr(Builder, name, l) + def l1(self, *args, **kvarg): + return self.add_tag(name, [(remove_leading_underscore(k), v) for k, v in kvarg.items()])(*args) + 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) @@ -149,7 +202,7 @@ def remove_leading_underscore(s): class WrapAfterBuilder(Builder): def __init__(self, f): - super().__init__(Tag(self, "root", [])) + super().__init__(Bucket(self)) self._wrap_done = False self._wrap_function = f @@ -157,18 +210,18 @@ class WrapAfterBuilder(Builder): if self._wrap_done: return self._wrap_done = True - content = self._root_tag.content - self._root_tag = None - self._current_tag = None - self._root_tag = self._wrap_function(self, content, *arg, **kvarg) or self._root_tag + content = self.root_tag.content + self.root_tag = None + self.current_tag = None + 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() - return super()._print(*arg, **kvarg) + return super().print(*arg, **kvarg) - def _print_file(self, *arg, **kvarg): + def print_file(self, *arg, **kvarg): self._wrap() - return super()._print_file(*arg, **kvarg) + return super().print_file(*arg, **kvarg) def WrapAfterBuilder_decorator(f): diff --git a/server/hra/web/pages.py b/server/hra/web/pages.py index d3faac2..03b36d6 100644 --- a/server/hra/web/pages.py +++ b/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.validators import ValidationError from flask_wtf import FlaskForm @@ -24,8 +24,8 @@ from hra.util import hash_passwd @html.WrapAfterBuilder_decorator def BasePage(b, content): - b._current_tag = html.Tag(b, "html", []) - b._root_tag = b._current_tag + b.current_tag = html.Tag(b, "html", []) + b.root_tag = b.current_tag with b.head(): b.title()("KSP hra") b.meta(name="viewport", content="width=device-width, initial-scale=1.0") @@ -54,7 +54,7 @@ def BasePage(b, content): if messages: for category, message in messages: if category == "message": - category = warning + category = "warning" b.div(_class=f"alert alert-{category}", role="alert")(message) b(*content) @@ -107,13 +107,13 @@ def registration(): db.get_session().commit() except exc.IntegrityError: flash("Uživatelské jméno již existuje") - return render_template('registration.html', form=f) - flash("Přidán nový uživatel.", 'success') - return redirect("login") + else: + flash("Přidán nový uživatel.", 'success') + return redirect("login") b = BasePage() b(jinja_mac.quick_form(f, form_type='horizontal')) - return b._print_file() + return b.print_file() @app.route("/login", methods=['GET', 'POST']) def login(): @@ -131,7 +131,7 @@ def login(): b = BasePage() b(jinja_mac.quick_form(f, form_type='horizontal')) - return b._print_file() + return b.print_file() @app.route("/logout") @@ -167,84 +167,41 @@ def web_index(): if g.user: with b.p(): b(f"Váš token je: {g.user.token}") - return b._print_file() + return b.print_file() @app.route("/org/users") def web_users(): 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/", 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() - 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: raise werkzeug.exceptions.NotFound() - calc_point(user) - findings = db.get_session().query(Findings).filter_by(user=user.id, round=get_round_id()).order_by(Findings.time).all() - return render_template("org_user.html", user=user, findings=findings, form=f) - -@app.route("/org/admin", methods=['GET', 'POST']) -def web_admin(): - obj_round = get_round() - f_round = FormRound(obj=obj_round, prefix="r") - if f_round.validate_on_submit(): - f_round.populate_obj(obj_round) - db.get_session().commit() - - 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!") - 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 render_template("org_su.html", f=f) + b = BasePage() + b.line().h2("Uživatel ", user.username) + 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)): + b.button(_class="btn btn-default", type="submit", name="su", value="yes")("Převtělit") + return b.print_file() + +@app.route("/org/user//su", methods=['POST']) +def web_org_user_su(user_id): + user = db.get_session().query(db.User).filter_by(id=user_id).one_or_none() + session['uid'] = user.id + flash("Uživatel vtělen!") + return redirect('/')