Strategická: Další kousek webu
This commit is contained in:
parent
df1a6518c4
commit
a2ca047f55
3 changed files with 152 additions and 142 deletions
|
@ -17,6 +17,6 @@ parser.add_argument("--restore", action="store_true")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.restore:
|
if args.restore:
|
||||||
lib.game_restore_broken(args.game_id)
|
lib.game_restore_broken(args.game_id)
|
||||||
if args.step:
|
if args.step:
|
||||||
lib.game_step(args.game_id)
|
lib.game_step(args.game_id)
|
||||||
|
|
|
@ -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,30 +24,33 @@ 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):
|
||||||
self.is_paired = True
|
if isinstance(x, types.FunctionType):
|
||||||
self.content.append(x)
|
before_current_tag = self.builder.current_tag
|
||||||
|
self.builder.current_tag = self
|
||||||
def add_attribute(k, v):
|
try:
|
||||||
self.attributes.append((k, v))
|
x(self)
|
||||||
|
finally:
|
||||||
|
self.builder.current_tag = before_current_tag
|
||||||
|
else:
|
||||||
|
self.is_paired = True
|
||||||
|
self.content.append(x)
|
||||||
|
|
||||||
def __call__(self, *arg, **kvarg):
|
def __call__(self, *arg, **kvarg):
|
||||||
for i in arg:
|
for i in arg:
|
||||||
|
@ -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 Builder:
|
|
||||||
_current_tag: Tag
|
|
||||||
_root_tag: Tag
|
|
||||||
def __init__(self, tag: Tag):
|
|
||||||
self._root_tag = tag
|
|
||||||
self._current_tag = tag
|
|
||||||
|
|
||||||
def _tag(self, name: str, attributes: List[Tuple[str, str]] = []):
|
class Tag(Bucket):
|
||||||
return self._current_tag.add_tag(name, attributes)
|
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:
|
||||||
|
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 line(self):
|
||||||
|
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):
|
||||||
|
|
|
@ -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)):
|
||||||
|
b.button(_class="btn btn-default", type="submit", name="su", value="yes")("Převtělit")
|
||||||
|
return b.print_file()
|
||||||
|
|
||||||
@app.route("/org/admin", methods=['GET', 'POST'])
|
@app.route("/org/user/<int:user_id>/su", methods=['POST'])
|
||||||
def web_admin():
|
def web_org_user_su(user_id):
|
||||||
obj_round = get_round()
|
user = db.get_session().query(db.User).filter_by(id=user_id).one_or_none()
|
||||||
f_round = FormRound(obj=obj_round, prefix="r")
|
session['uid'] = user.id
|
||||||
if f_round.validate_on_submit():
|
flash("Uživatel vtělen!")
|
||||||
f_round.populate_obj(obj_round)
|
return redirect('/')
|
||||||
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)
|
|
||||||
|
|
Loading…
Reference in a new issue