diff --git a/server/hra/web/game.py b/server/hra/web/game.py index e6fc55e..3748126 100644 --- a/server/hra/web/game.py +++ b/server/hra/web/game.py @@ -1,5 +1,5 @@ from flask import Flask, redirect, flash, session, g, request, get_flashed_messages, Markup -from wtforms import Form, BooleanField, StringField, PasswordField, validators, SubmitField, IntegerField, DateTimeField +from wtforms import Form, BooleanField, StringField, PasswordField, validators, SubmitField, IntegerField, DateTimeField, RadioField from wtforms.validators import ValidationError from flask_wtf import FlaskForm from flask_bootstrap import Bootstrap @@ -11,6 +11,7 @@ import wtforms from wtforms.fields import EmailField from wtforms.widgets import NumberInput from typing import Optional, Any, List +from datetime import datetime import hra.config as config import hra.web.html as html @@ -18,7 +19,7 @@ import hra.db as db from hra.web import app import hra.web.jinja_mac as jinja_mac from hra.util import hash_passwd -from hra.web.pages import BasePage +from hra.web.pages import BasePage, web_game_view wlogic_by_mode = {} @@ -39,17 +40,49 @@ class WLogic: self.game = game self.logic = game.get_logic() +class OccupyViewConfig(FlaskForm): + refresh_none = "Bez automatické aktualizace" + refresh_meta = "Aktualizace pomocí HTML META" + refresh_js = "Aktualizace pomocí java scriptu" + + font_size = IntegerField("Velikost fontu", default=8) + refresh = RadioField("Aktualizace", choices=(refresh_none, refresh_meta), default=refresh_none) + not_clickable = BooleanField("Vypnout podrobnosti kliknutím (zmenší nároky na sít a procesor)") + big_fields = BooleanField("Široká políčka (trojciferné počty)") + + submit = SubmitField("Refresh/Potvrdit") @add_wlogic class Occupy(WLogic): def view(self, state: db.State, team: Optional[db.Team], teams: List[db.Team]): + meta_refresh_without_reaming = 15 + + game = state.game + if game.step_mode == db.StepMode.automatic: + time_reaming = (state.create_time - datetime.now()).total_seconds() + game.step_every_s + else: + time_reaming = None s = state.get_state() - if team is not None: - s = self.logic.personalize_state(s, team.team_id, state.round) + conff = OccupyViewConfig(formdata=request.args, csrf_enabled=False) + q = db.get_session().query(db.Log).order_by(db.Log.log_id.desc()) + conff.validate() + + max_num = 999 if conff.big_fields.data else 99 + b = BasePage() b.h2(f"Hra {self.game.print()} kolo {state.round}") - b.p().b(_class=f"game_team_{team.team_id}")(f"Pohled týmu {team.print()}") - with b.table(_class="game_table"): + if not conff.not_clickable.data: + b.p("Po kliknutí na buňku se zobrazí další informace.") + if conff.refresh.data == conff.refresh_meta and time_reaming is None: + b.p(f"Není známo, kdy nastane další kolo, proto může být aktualizace až o {meta_refresh_without_reaming} sekund opožděna.") + with b.p(): + b.line().b(_class=f"game_team_{team.team_id}")(f"Pohled týmu {team.print()}") + with b.line().b(_class="pull-right"): + if time_reaming is not None: + b("Čas do konce kola: ", b._span(id="time")(int(time_reaming)), " sekund") + else: + b("Konec kola není stanoven") + with b.p(style="text-align:center;").table(_class="game_table"): for i, row in enumerate(s["world"]): with b.tr(): for j, x in enumerate(row): @@ -58,7 +91,7 @@ class Occupy(WLogic): members = x["members"] with b.td(): classes = [] - with b.a(href=f"#cell_{i}_{j}"): + with b.a(href=f"#cell_{i}_{j}") if not conff.not_clickable.data else b.bucket(): if x["protected_for_team"] is not None: classes.append("game_protected") if x["hill"]: @@ -70,38 +103,80 @@ class Occupy(WLogic): if occupied_by_team is not None: classes.append(f'game_occupied') classes.append(f'game_occupied_by_{occupied_by_team}') - if len(members): - b(len(members)) + num = len(members) + if num: + b(num if num <= max_num else ("+++" if conff.big_fields.data else "++")) else: b(Markup(" ")) b(_class=" ".join(classes)) - for i, row in enumerate(s["world"]): - for j, x in enumerate(row): - occupied_by_team = x["occupied_by_team"] - protected_for_team = x["protected_for_team"] - home_for_team = x["home_for_team"] - members = x["members"] - with b.div(id=f"cell_{i}_{j}", _class="game_tab"): - b.h4(f"Políčko {i} {j}") - if x["hill"]: - b.p().b("Pohoří") - else: - if occupied_by_team is not None: - b.p(_class=f"game_team_{occupied_by_team}").b(f"Obsazeno týmem: {teams[occupied_by_team].print()}") - if protected_for_team is not None: - b.p(_class=f"game_team_{protected_for_team}").b(f"Ochranné území týmu: {teams[protected_for_team].print()}") - if home_for_team is not None: - b.p(_class=f"game_team_{home_for_team}").b(f"Domov týmu: {teams[home_for_team].print()}") - b.p().b(f"Počet osob: {len(members)}") - with b.ul(): - for m in members: - b.li(_class=f"game_team_{home_for_team}")(f"Voják {m['id']} týmu {teams[m['team']].print()}") + if not conff.not_clickable.data: + for i, row in enumerate(s["world"]): + for j, x in enumerate(row): + occupied_by_team = x["occupied_by_team"] + protected_for_team = x["protected_for_team"] + home_for_team = x["home_for_team"] + members = x["members"] + with b.div(id=f"cell_{i}_{j}", _class="game_tab form-frame"): + b.h4(f"Políčko {i} {j}", b._a(href=f"#", _class="btn btn-primary pull-right")("Skrýt")) + if x["hill"]: + b.p().b("Pohoří") + else: + if occupied_by_team is not None: + b.p(_class=f"game_team_{occupied_by_team}").b(f"Obsazeno týmem: {teams[occupied_by_team].print()}") + if protected_for_team is not None: + b.p(_class=f"game_team_{protected_for_team}").b(f"Ochranné území týmu: {teams[protected_for_team].print()}") + if home_for_team is not None: + b.p(_class=f"game_team_{home_for_team}").b(f"Domov týmu: {teams[home_for_team].print()}") + b.p().b(f"Počet osob: {len(members)}") + with b.ul(): + for m in members: + b.li(_class=f"game_team_{home_for_team}")(f"Voják {m['id']} týmu {teams[m['team']].print()}") + + del conff.csrf_token + b.p() + b.div(_class="form-frame")(jinja_mac.quick_form(conff, form_type="horizontal", method="GET")) + + b.script(Markup(f""" + var intervalID = window.setInterval(update_time, 100); + var t = { time_reaming*1000 }; + function update_time() {{ + document.getElementById("time").innerHTML = Math.floor(t/1000);; + t -= 100; + }} + """)) + + def head(x): + refresh_time = time_reaming + 1 if time_reaming is not None else 15 + font_size = int(conff.font_size.data) + box_size = int(font_size * 1.3 + 1) + box_w = box_size + if conff.big_fields.data: + box_w *= 1.5 + table_w = len(s["world"][0]) * box_w + margin_cmd = "" + if table_w > 1000: + margin_cmd = f"margin-left: {(1000-table_w) / 2}px;" + if conff.refresh.data == conff.refresh_meta: + b.meta(**{"HTTP-EQUIV": "refresh", "CONTENT": f"{refresh_time};{app.url_for(web_game_view.__name__, game_id=game.game_id, team_id=team.team_id, **request.args)}"}) + b.link(rel="stylesheet", href=app.url_for('static', filename='occupy.css'), type='text/css', media="all") + b.style(Markup(f""" + table.game_table tr td {{ + width: {box_w}px; + max-width: {box_w}px; + height: {box_size}px; + font-size: {font_size}px; + }} + table.game_table {{ + width: {table_w}px; + {margin_cmd} + }} + """)) b.wrap( limited_size=False, sticky_head=False, - head=lambda x:x.link(rel="stylesheet", href=app.url_for('static', filename='occupy.css'), type='text/css', media="all") + head=head, ) return b.print_file() diff --git a/server/static/ksp-mhd.css b/server/static/ksp-mhd.css index 9a3d271..6dc0821 100644 --- a/server/static/ksp-mhd.css +++ b/server/static/ksp-mhd.css @@ -12,6 +12,7 @@ body { margin: 0 auto; padding: 0 1em; width: 100%; + max-width: 1000px; } .content_limited, main { diff --git a/server/static/occupy.css b/server/static/occupy.css index d4b58c7..2d0d0ae 100644 --- a/server/static/occupy.css +++ b/server/static/occupy.css @@ -1,15 +1,17 @@ +table.game_table{ + margin-left: auto; + margin-right: auto; +} table.game_table, table.game_table tr, table.game_table tr td { border: thin solid black; border-collapse: collapse; table-layout: fixed; + overflow: hidden; } table.game_table tr td { - width: 10pt; - height: 10px; - font-size: 8px; text-align: end; }