You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

262 lines
12 KiB

from flask import Flask, redirect, flash, session, g, request, get_flashed_messages, Markup
from wtforms import Form, BooleanField, StringField, PasswordField, validators, SubmitField, IntegerField, DateTimeField, RadioField, FloatField
from wtforms.validators import ValidationError
from flask_wtf import FlaskForm
from flask_bootstrap import Bootstrap
import time
from datetime import datetime
from sqlalchemy import exc, update
import werkzeug.exceptions
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
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, web_game_view
wlogic_by_mode = {}
def add_wlogic(cls):
wlogic_by_mode[cls.__name__.lower()] = cls
def get_wlogic(game):
if game.game_mode in wlogic_by_mode:
cls = wlogic_by_mode[game.game_mode]
else:
cls = WLogic
return cls(game)
class WLogic:
def __init__(self, game):
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"
clickable_down = "Podrobnosti na kliknutí pod mapou"
clickable_right = "Podrobnosti na kliknutí vpravo vedle mapy"
clickable_not = "Vypnout podrobnosti kliknutím (zmenší nároky na sít a procesor)"
status_minimalistic = "Minimalistické metadata"
status_up = "Matadata nahoře"
status_left = "Metadata vlevo od mapy"
font_size = FloatField("Velikost fontu", default=8.0)
refresh = RadioField("Aktualizace", choices=(refresh_none, refresh_meta), default=refresh_none)
clickable = RadioField("", choices=(clickable_not, clickable_down, clickable_right), default=clickable_down)
status = RadioField("", choices=(status_minimalistic, status_left, status_up), default=status_minimalistic)
big_fields = BooleanField("Široká políčka (trojciferné počty)")
submit = SubmitField("Refresh/Potvrdit")
show_data_only = SubmitField("Zobrazit stránku pouze s daty")
@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
team_id = team.team_id if team else None
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
def reaming_time(id="time"):
if time_reaming is not None:
b.b("Čas do konce kola: ", b._span(id=id)(int(time_reaming)), " sekund")
b.script(Markup(f"""
var intervalID = window.setInterval(update_time, 100);
var t = { time_reaming*1000 };
function update_time() {{
document.getElementById("{id}").innerHTML = Math.floor(t/1000);;
t -= 100;
}}
"""))
else:
b("Konec kola není stanoven")
def status():
members_counts = [0 for _ in teams]
occupied_counts = [0 for _ in teams]
for x in s["world"]:
for y in x:
occupied_by_team = y["occupied_by_team"]
members = y["members"]
if occupied_by_team is not None:
occupied_counts[occupied_by_team] += 1
members_counts[occupied_by_team] += len(members)
with b.div(_class="game_left_status button form-frame"):
b.b("Kolo ", state.round)
with b.p():
reaming_time(id="time_left")
with b.p():
for t, occupied_count, members_count in zip(teams, occupied_counts, members_counts):
with b.line().span(_class=f"game_team_{t.team_id}"):
b.b("Tým ", t.print())
b.br()
b("Bodů: ", t.get_move(state.round).points)
b.br()
b("Políček: ", occupied_count)
b.br()
b("Vojáků: ", members_count)
b.br()
def table():
with b.table(_class="game_table"):
for i, row in enumerate(s["world"]):
with b.tr():
for j, x in enumerate(row):
occupied_by_team = x["occupied_by_team"]
home_for_team = x["home_for_team"]
members = x["members"]
with b.td():
classes = []
with b.a(href=f"#cell_{i}_{j}") if conff.clickable.data != conff.clickable_not else b.bucket():
if x["protected_for_team"] is not None:
classes.append("game_protected")
if x["hill"]:
classes.append("game_hill")
b(Markup(" "))
else:
if home_for_team is not None:
classes.append(f'game_home')
if occupied_by_team is not None:
classes.append(f'game_occupied')
classes.append(f'game_occupied_by_{occupied_by_team}')
num = len(members)
if num:
b(num if num <= max_num else ("+++" if conff.big_fields.data else "++"))
else:
b(Markup("&nbsp;"))
b(_class=" ".join(classes))
def clickable():
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_{m['team']}")(f"Voják {m['id']} týmu {teams[m['team']].print()}")
b = BasePage()
if not conff.show_data_only.data:
b.h2(f"Hra {self.game.print()} kolo {state.round}")
if conff.clickable.data != conff.clickable_not:
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():
if team is not None:
b.line().b(_class=f"game_team_{team.team_id}")(f"Pohled týmu {team.print()}")
with b.line().b(_class="pull-right"):
reaming_time()
b.div(style="clear: both; margin-bottom: 1ex")("")
with b.div().div(style="text-align:center;", id="game_main"):
if conff.status.data == conff.status_left:
status()
table()
if conff.clickable.data == conff.clickable_right:
clickable()
b.div(style="clear: both; margin-bottom: 1ex")("")
if conff.clickable.data == conff.clickable_down:
clickable()
del conff.csrf_token
b.p()
if not conff.show_data_only.data:
b.div(_class="form-frame")(jinja_mac.quick_form(conff, form_type="horizontal", method="GET"))
def head(x):
style = ""
page_w = float("inf") if conff.show_data_only.data else 1000
refresh_time = time_reaming + 1 if time_reaming is not None else 15
font_size = float(conff.font_size.data or 8.0)
box_size = int(font_size * 1.5 + 1)
left_w = 0
if conff.status.data == conff.status_left:
left_w = 150
box_w = box_size
if conff.big_fields.data:
box_w *= 1.5
table_w = len(s["world"][0]) * box_w
margin_cmd = ""
main_w = table_w + left_w
if conff.clickable.data == conff.clickable_right:
right_w = 400
main_w += right_w
style += f".game_tab {{\n width: {right_w}px\n}}\n"
if main_w > page_w:
margin_cmd = f"margin-left: {(page_w-main_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_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;
}}
#game_main {{
{margin_cmd}
width: {main_w}px;
}}
.game_left_status {{
width: {left_w-20}px;
}}
{style}
"""))
b.wrap(
limited_size=False,
sticky_head=False,
head=head,
empty=conff.show_data_only.data
)
return b.print_file()