Strategická: Další kousek webu
This commit is contained in:
parent
cf6377b21b
commit
7eee3cfcff
8 changed files with 202 additions and 54 deletions
|
@ -7,8 +7,6 @@ import argparse
|
||||||
from sqlalchemy import exc, update
|
from sqlalchemy import exc, update
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
g = db.Game(game_mode="", configuration={}, teams_count=1)
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("game_id")
|
parser.add_argument("game_id")
|
||||||
parser.add_argument("--step", action="store_true")
|
parser.add_argument("--step", action="store_true")
|
||||||
|
|
|
@ -6,20 +6,32 @@ import hra.lib as lib
|
||||||
import sys
|
import sys
|
||||||
from sqlalchemy import exc, update
|
from sqlalchemy import exc, update
|
||||||
|
|
||||||
|
ses = db.get_session()
|
||||||
|
|
||||||
mode = "occupy"
|
mode = "occupy"
|
||||||
teams_count = 6
|
teams_count = 6
|
||||||
configuration = {}
|
configuration = {
|
||||||
|
"teams_width": 2,
|
||||||
|
"teams_height": 3,
|
||||||
|
"width_per_team": 10,
|
||||||
|
"height_per_team": 10,
|
||||||
|
}
|
||||||
g = db.Game(game_mode=mode, configuration=configuration, teams_count=teams_count)
|
g = db.Game(game_mode=mode, configuration=configuration, teams_count=teams_count)
|
||||||
|
|
||||||
db.get_session().add(g)
|
ses.add(g)
|
||||||
db.get_session().commit()
|
ses.commit()
|
||||||
|
|
||||||
|
g.lock()
|
||||||
|
|
||||||
s = db.State(game_id=g.game_id, round=0, state=g.get_logic().zero_state())
|
s = db.State(game_id=g.game_id, round=0, state=g.get_logic().zero_state())
|
||||||
|
ses.add(s)
|
||||||
|
|
||||||
|
for i in range(teams_count):
|
||||||
|
t = db.Team(team_id=i, game_id=g.game_id, name="")
|
||||||
|
ses.add(t)
|
||||||
|
|
||||||
db.get_session().add(s)
|
|
||||||
g.current_round = 0
|
g.current_round = 0
|
||||||
g.working_on_next_state = False
|
g.working_on_next_state = False
|
||||||
db.get_session().commit()
|
ses.commit()
|
||||||
|
|
||||||
print(f"Přidána hra {g.game_id}. ")
|
print(f"Přidána hra {g.game_id}. ")
|
||||||
|
|
|
@ -78,7 +78,25 @@ class Game(Base):
|
||||||
return hra.game.logic_by_mode[self.game_mode](self.teams_count, self.configuration)
|
return hra.game.logic_by_mode[self.game_mode](self.teams_count, self.configuration)
|
||||||
|
|
||||||
def lock(self) -> 'Game':
|
def lock(self) -> 'Game':
|
||||||
return get_session().query(Game).filter_by(game_id=self.game_id).with_for_update().first()
|
ses = get_session()
|
||||||
|
ses.expire_all()
|
||||||
|
return ses.query(Game).filter_by(game_id=self.game_id).with_for_update().first()
|
||||||
|
|
||||||
|
|
||||||
|
class Team(Base):
|
||||||
|
__tablename__ = 'base'
|
||||||
|
__table_args__ = (
|
||||||
|
UniqueConstraint('game_id', 'team_id'),
|
||||||
|
UniqueConstraint('user_id', 'name'),
|
||||||
|
)
|
||||||
|
|
||||||
|
game_id = Column(Integer, ForeignKey('games.game_id'), nullable=False, primary_key=True)
|
||||||
|
team_id = Column(Integer, nullable=False, primary_key=True)
|
||||||
|
user_id = Column(Integer, ForeignKey('users.id'), nullable=True)
|
||||||
|
name = Column(String(80), nullable=False)
|
||||||
|
|
||||||
|
game = relationship('Game', primaryjoin='Team.game_id == Game.game_id')
|
||||||
|
user = relationship('User', primaryjoin='Team.user_id == User.id')
|
||||||
|
|
||||||
|
|
||||||
class State(Base):
|
class State(Base):
|
||||||
|
|
|
@ -70,22 +70,18 @@ def init_request():
|
||||||
if not user or not user.org:
|
if not user or not user.org:
|
||||||
raise werkzeug.exceptions.Forbidden()
|
raise werkzeug.exceptions.Forbidden()
|
||||||
g.user = user
|
g.user = user
|
||||||
|
g.org = g.user and g.user.org
|
||||||
|
|
||||||
g.menu = [
|
g.menu = [
|
||||||
MenuItem('/', "Domů"),
|
MenuItem('/', "Domů"),
|
||||||
MenuItem('/bonuses', "Bonusy"),
|
|
||||||
]
|
]
|
||||||
if g.user and g.user.org:
|
if g.org:
|
||||||
g.menu += [
|
g.menu += [
|
||||||
MenuItem('/org/ranking', "Výsledky"),
|
MenuItem(app.url_for(pages.web_org_games.__name__), "Hry"),
|
||||||
MenuItem('/org/act', "Aktuální kolo"),
|
MenuItem(app.url_for(pages.web_org_users.__name__), "Uživatelé")
|
||||||
MenuItem('/org/admin', "Admin"),
|
|
||||||
MenuItem('/org/su', "Vtělování se"),
|
|
||||||
MenuItem('/org/users', "Uživatelé"),
|
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
g.menu += [
|
g.menu += [
|
||||||
MenuItem('/submitted', "Odevzdané kódy"),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -95,5 +91,5 @@ app.before_request(init_request)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import hra.web.pages
|
import hra.web.pages as pages
|
||||||
import hra.web.api
|
import hra.web.api
|
||||||
|
|
|
@ -23,17 +23,18 @@ def args_get(name, type, optional=False, default=None):
|
||||||
def get_context():
|
def get_context():
|
||||||
if g.user is None:
|
if g.user is None:
|
||||||
raise NeedLoginError
|
raise NeedLoginError
|
||||||
game_id = args_get("game", int, True, 1)
|
game_name = args_get("game", str, True, "main")
|
||||||
team_id = args_get('team', int, True, 0)
|
team = db.get_session().query(db.Team).filter_by(user_id=g.user.id, name=game_name).one_or_none()
|
||||||
game = db.get_session().query(db.Game).filter_by(game_id=game_id).first()
|
if team is None:
|
||||||
if game is None:
|
|
||||||
raise werkzeug.exceptions.NotFound("No such game")
|
raise werkzeug.exceptions.NotFound("No such game")
|
||||||
return game, team_id
|
return team
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/state", methods=['GET'])
|
@app.route("/api/state", methods=['GET'])
|
||||||
def api_state():
|
def api_state():
|
||||||
game, team_id = get_context()
|
team = get_context()
|
||||||
|
game = team.game
|
||||||
|
team_id = team.team_id
|
||||||
state = game.current_state()
|
state = game.current_state()
|
||||||
if state is None:
|
if state is None:
|
||||||
return json.dumps({
|
return json.dumps({
|
||||||
|
@ -53,8 +54,9 @@ def api_action():
|
||||||
return json.dumps({
|
return json.dumps({
|
||||||
"status": "too-late",
|
"status": "too-late",
|
||||||
})
|
})
|
||||||
|
team = get_context()
|
||||||
game, team_id = get_context()
|
game = team.game
|
||||||
|
team_id = team.team_id
|
||||||
j = request.get_json()
|
j = request.get_json()
|
||||||
state = game.current_state()
|
state = game.current_state()
|
||||||
round_id = args_get('round', int)
|
round_id = args_get('round', int)
|
||||||
|
|
|
@ -95,6 +95,17 @@ class Bucket:
|
||||||
self.builder.current_tag = self.before_tag
|
self.builder.current_tag = self.before_tag
|
||||||
self.before_tag = None
|
self.before_tag = None
|
||||||
|
|
||||||
|
def serialize_append_to_list(self, out, indent):
|
||||||
|
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))
|
||||||
|
|
||||||
|
|
||||||
class Tag(Bucket):
|
class Tag(Bucket):
|
||||||
name: str
|
name: str
|
||||||
|
@ -114,19 +125,7 @@ class Tag(Bucket):
|
||||||
def serialize_append_to_list(self, out, indent):
|
def serialize_append_to_list(self, out, indent):
|
||||||
if self.is_paired:
|
if self.is_paired:
|
||||||
out.append(indent_str(indent, f"<{escape_tag_name(self.name)} {self.format_attributes()}>"))
|
out.append(indent_str(indent, f"<{escape_tag_name(self.name)} {self.format_attributes()}>"))
|
||||||
if indent is not None:
|
super().serialize_append_to_list(out, indent + 1 if indent is not None else 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)}>"))
|
out.append(indent_str(indent, f"</{escape_tag_name(self.name)}>"))
|
||||||
else:
|
else:
|
||||||
out.append(indent_str(indent, f"<{escape_tag_name(self.name)} {self.format_attributes()} />"))
|
out.append(indent_str(indent, f"<{escape_tag_name(self.name)} {self.format_attributes()} />"))
|
||||||
|
@ -134,21 +133,21 @@ class Tag(Bucket):
|
||||||
|
|
||||||
class Line(Bucket):
|
class Line(Bucket):
|
||||||
def serialize_append_to_list(self, out, indent):
|
def serialize_append_to_list(self, out, indent):
|
||||||
out.append(indent_str(indent,"")[:-1])
|
if indent is None:
|
||||||
for i in self.content:
|
super().serialize_append_to_list(out, None)
|
||||||
if isinstance(i, Bucket):
|
else:
|
||||||
i.serialize_append_to_list(out, None)
|
out.append(indent_str(indent,"")[:-1])
|
||||||
elif isinstance(i, Markup):
|
super().serialize_append_to_list(out, None)
|
||||||
out.append(i.__html__())
|
out.append("\n")
|
||||||
out.append(escape(str(i)))
|
|
||||||
out.append("\n")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Builder:
|
class Builder:
|
||||||
current_tag: Bucket
|
current_tag: Bucket
|
||||||
root_tag: Bucket
|
root_tag: Bucket
|
||||||
def __init__(self, tag: Bucket):
|
def __init__(self, tag: Bucket = None):
|
||||||
|
if tag is None:
|
||||||
|
tag = Bucket(self)
|
||||||
self.root_tag = tag
|
self.root_tag = tag
|
||||||
self.current_tag = tag
|
self.current_tag = tag
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
from hra.web import app
|
from hra.web import app
|
||||||
|
|
||||||
quick_form = app.jinja_env.get_template("bootstrap/wtf.html").module.quick_form
|
quick_form = app.jinja_env.get_template("bootstrap/wtf.html").module.quick_form
|
||||||
|
form_field = app.jinja_env.get_template("bootstrap/wtf.html").module.form_field
|
||||||
|
|
|
@ -71,7 +71,18 @@ class OptionalIntField(wtforms.IntegerField):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise wtforms.ValidationError('Nejedná se o číslo.')
|
raise wtforms.ValidationError('Nejedná se o číslo.')
|
||||||
|
|
||||||
|
def user_link(user):
|
||||||
|
b = html.Builder()
|
||||||
|
with b.line():
|
||||||
|
if user is None:
|
||||||
|
b("-")
|
||||||
|
else:
|
||||||
|
if g.org:
|
||||||
|
b.a(href=app.url_for(web_org_user.__name__, user_id=user.id))(user.username)
|
||||||
|
else:
|
||||||
|
b(user.username)
|
||||||
|
return b.root_tag
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RegistrationForm(FlaskForm):
|
class RegistrationForm(FlaskForm):
|
||||||
|
@ -169,10 +180,120 @@ def web_index():
|
||||||
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("/game/<int:game_id>", methods=['GET'])
|
||||||
|
def web_game(game_id):
|
||||||
|
ses = db.get_session()
|
||||||
|
game = ses.query(db.Game).filter_by(game_id=game_id).one_or_none()
|
||||||
|
teams = ses.query(db.Team).filter_by(game_id=game_id).order_by(db.Team.team_id).all()
|
||||||
|
if game is None:
|
||||||
|
raise werkzeug.exceptions.NotFound()
|
||||||
|
|
||||||
|
b = BasePage()
|
||||||
|
with b.p().table(_class="data full"):
|
||||||
|
with b.thead():
|
||||||
|
b.line().th()("Id")
|
||||||
|
b.line().th()("User")
|
||||||
|
if g.org:
|
||||||
|
b.line().th()("Akce")
|
||||||
|
for team in teams:
|
||||||
|
with b.tr():
|
||||||
|
b.line().td()(team.team_id)
|
||||||
|
b.line().td()(user_link(team.user),": ", team.name)
|
||||||
|
if g.org:
|
||||||
|
with b.td():
|
||||||
|
with b.div(_class="btn-group", role="group"):
|
||||||
|
b.a(_class="btn btn-xs btn-primary", href=app.url_for(web_org_game_userchange.__name__, game_id=game.game_id, team_id=team.team_id))("Změnit uživatele")
|
||||||
|
#b.a(_class="btn btn-xs btn-primary", href=app.url_for(web_game.__name__, game_id=g.game_id))("Detail")
|
||||||
|
|
||||||
|
return b.print_file()
|
||||||
|
|
||||||
|
@app.route("/org/games")
|
||||||
|
def web_org_games():
|
||||||
|
games = db.get_session().query(db.Game).order_by(db.Game.game_id).all()
|
||||||
|
b = BasePage()
|
||||||
|
with b.p().table(_class="data full"):
|
||||||
|
with b.thead():
|
||||||
|
b.line().th()("Id")
|
||||||
|
b.line().th()("Kolo")
|
||||||
|
b.line().th()("Akce")
|
||||||
|
for g in games:
|
||||||
|
with b.tr():
|
||||||
|
b.line().td()(g.game_id)
|
||||||
|
with b.line().td():
|
||||||
|
if g.working_on_next_state:
|
||||||
|
b.b()(g.current_round, "++")
|
||||||
|
else:
|
||||||
|
b(g.current_round)
|
||||||
|
with b.td():
|
||||||
|
b.a(_class="btn btn-xs btn-primary", href=app.url_for(web_game.__name__, game_id=g.game_id))("Detail")
|
||||||
|
return b.print_file()
|
||||||
|
|
||||||
|
class GameUserchangeForm(FlaskForm):
|
||||||
|
name = StringField("Jméno hry z pohledu týmu")
|
||||||
|
set_no_user = SubmitField("Bez uživatele")
|
||||||
|
submit_no_change = wtforms.SubmitField("Bez změny", render_kw={"style": "display: none"})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/org/game/<int:game_id>/team/<int:team_id>/change_user", methods=['GET', 'POST'])
|
||||||
|
def web_org_game_userchange(game_id, team_id):
|
||||||
|
ses = db.get_session()
|
||||||
|
game = ses.query(db.Game).filter_by(game_id=game_id).one_or_none()
|
||||||
|
team_to_edit = ses.query(db.Team).filter_by(game_id=game_id, team_id=team_id).one_or_none()
|
||||||
|
teams = ses.query(db.Team).filter_by(game_id=game_id).order_by(db.Team.team_id).all()
|
||||||
|
users = db.get_session().query(db.User).order_by(db.User.id).all()
|
||||||
|
if game is None or team_to_edit is None:
|
||||||
|
raise werkzeug.exceptions.NotFound()
|
||||||
|
teams_by_user = {u.id:[] for u in users}
|
||||||
|
for t in teams:
|
||||||
|
if t.user_id is not None:
|
||||||
|
teams_by_user[t.user_id].append(t)
|
||||||
|
|
||||||
|
form = GameUserchangeForm(obj=team_to_edit)
|
||||||
|
if form.validate_on_submit():
|
||||||
|
team_to_edit.name = form.name.data
|
||||||
|
if "submit_no_change" not in request.form:
|
||||||
|
team_to_edit.user_id = None
|
||||||
|
for u in users:
|
||||||
|
if f"set_user_{u.id}" in request.form:
|
||||||
|
team_to_edit.user_id = u.id
|
||||||
|
try:
|
||||||
|
ses.commit()
|
||||||
|
except exc.IntegrityError:
|
||||||
|
flash("Duplicitní přiřazení", 'danger')
|
||||||
|
ses.rollback()
|
||||||
|
else:
|
||||||
|
flash("Uživatel změněn", 'success')
|
||||||
|
return redirect(app.url_for(web_game.__name__, game_id=game_id))
|
||||||
|
|
||||||
|
b = BasePage()
|
||||||
|
with b.form(action="", method="POST", _class="form form-horizontal", role="form"):
|
||||||
|
b(form.csrf_token)
|
||||||
|
b(form.submit_no_change)
|
||||||
|
with b.div(_class="form-row"):
|
||||||
|
b(jinja_mac.form_field(form.name, size=8))
|
||||||
|
with b.p().table(_class="data full"):
|
||||||
|
with b.thead():
|
||||||
|
b.line().th()("Username")
|
||||||
|
b.line().th()("Přiřazení")
|
||||||
|
b.line().th()("Akce")
|
||||||
|
for u in users:
|
||||||
|
with b.tr():
|
||||||
|
b.line().td(user_link(u))
|
||||||
|
with b.td():
|
||||||
|
if len(teams_by_user[u.id]):
|
||||||
|
with b.ul():
|
||||||
|
for t in teams_by_user[u.id]:
|
||||||
|
b.li(f"{t.team_id} -> {t.name}")
|
||||||
|
b.line().td().input(_class="btn btn-danger" if u.id == team_to_edit.user_id else "btn btn-primary", _id="set_participation_state", name=f"set_user_{u.id}", type="submit", value="Nastavit stav účasti")
|
||||||
|
b(jinja_mac.form_field(form.set_no_user))
|
||||||
|
return b.print_file()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/org/users")
|
@app.route("/org/users")
|
||||||
def web_users():
|
def web_org_users():
|
||||||
users = db.get_session().query(db.User).all()
|
users = db.get_session().query(db.User).order_by(db.User.id).all()
|
||||||
b = BasePage()
|
b = BasePage()
|
||||||
with b.p().table(_class="data full"):
|
with b.p().table(_class="data full"):
|
||||||
with b.thead():
|
with b.thead():
|
||||||
|
@ -181,10 +302,11 @@ def web_users():
|
||||||
b.line().th()("Akce")
|
b.line().th()("Akce")
|
||||||
for u in users:
|
for u in users:
|
||||||
with b.tr():
|
with b.tr():
|
||||||
b.line().th()(u.username)
|
b.line().td()(u.username)
|
||||||
b.line().th()
|
b.line().td()
|
||||||
with b.th():
|
with b.td():
|
||||||
b.a(_class="btn btn-xs btn-primary", href=app.url_for(web_org_user.__name__, user_id=u.id))("Detail")
|
with b.div(_class="btn-group", role="group"):
|
||||||
|
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()
|
return b.print_file()
|
||||||
|
|
||||||
@app.route("/org/user/<int:user_id>", methods=['GET', 'POST'])
|
@app.route("/org/user/<int:user_id>", methods=['GET', 'POST'])
|
||||||
|
|
Loading…
Reference in a new issue