Browse Source

Strategická: Další kousek webu

master
Jiří Kalvoda 2 years ago
parent
commit
7eee3cfcff
  1. 2
      server/bin/control_game
  2. 22
      server/bin/create_game
  3. 20
      server/hra/db.py
  4. 14
      server/hra/web/__init__.py
  5. 18
      server/hra/web/api.py
  6. 43
      server/hra/web/html.py
  7. 1
      server/hra/web/jinja_mac.py
  8. 136
      server/hra/web/pages.py

2
server/bin/control_game

@ -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")

22
server/bin/create_game

@ -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}. ")

20
server/hra/db.py

@ -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):

14
server/hra/web/__init__.py

@ -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

18
server/hra/web/api.py

@ -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)

43
server/hra/web/html.py

@ -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
server/hra/web/jinja_mac.py

@ -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

136
server/hra/web/pages.py

@ -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…
Cancel
Save