from flask import Flask, redirect, flash, render_template, session, g, request, get_flashed_messages import werkzeug.exceptions import json from datetime import datetime import hra.config as config import hra.db as db from hra.web import app, NeedLoginError import hra.web.jinja_mac as jinja_mac import hra.lib as lib def json_endpoint(f): def l(*arg, **kvarg): x = f(*arg, **kvarg) response = e.get_response() response.content_type = "application/json" response.data = json.dumps(x) return response l.__name__ = f.__name__ return l def args_get(name, type, optional=False, default=None): v = request.args.get(name) if v is None: if optional: return default else: raise werkzeug.exceptions.BadRequest(f"Missing mandatory option {name}.") try: return type(v) except ValueError: raise werkzeug.exceptions.BadRequest(f"Option {name} have wrong type.") def get_context(): if g.user is None: raise NeedLoginError game_name = args_get("game", str, True, "main") team = db.get_session().query(db.Team).filter_by(user_id=g.user.id, name=game_name).one_or_none() if team is None: raise werkzeug.exceptions.NotFound("No such game") return team @app.route("/api/state", methods=['GET']) @json_endpoint def api_state(): team = get_context() game = team.game team_id = team.team_id min_round = args_get("min_round", int, True) t = datetime.now() state = game.current_state() if state is None: return json.dumps({ "status": "working", "wait": 1.0, }) if game.step_mode == db.StepMode.automatic: time_to_end = max(1, game.step_every_s - (t - state.create_time).total_seconds()) else: time_to_end = None if min_round is not None and min_round > game.current_round: return json.dumps({ "status": "too_early", "wait": time_to_end + 1.0 if time_to_end is not None else 3.0, }) return { "status": "ok", "round": state.round, "team_id": team_id, "time_to_response": time_to_end, "state": game.get_logic().personalize_state(state.get_state(), team_id, state.round), } @app.route("/api/action", methods=['POST']) @json_endpoint def api_action(): def to_late(): return json.dumps({ "status": "too_late", }) team = get_context() game = team.game team_id = team.team_id j = request.get_json() state = game.current_state() round_id = args_get('round', int) if round_id < 0 or round_id > game.current_round: raise werkzeug.exceptions.BadRequest("Wrong round.") if game.working_on_next_state or round_id < game.current_round: return to_late() try: warnings = game.get_logic().validate_move(state.get_state(), team_id, j, round_id) except Exception as e: return { "status": "error", "description": f"{type(e).__name__}: {e}" } db.get_session().expire_all() game = game.lock() if game.working_on_next_state or round_id < game.current_round: db.get_session().commit() return to_late() move = db.get_session().query(db.Move).filter_by(game_id=game.game_id, team_id=team_id, round=round_id).one_or_none() if move is None: move = db.Move(game_id=game.game_id, team_id=team_id, round=round_id) db.get_session().add(move) move.move = db.new_big_data(j) db.get_session().commit() if warnings is not None: return warnings return { "status": "ok", } @app.route("/api/step", methods=['POST']) @json_endpoint def api_step(): team = get_context() game = team.game if game.step_mode != db.StepMode.user: raise werkzeug.exceptions.Forbidden("Je zakázáno krokovat tuto hru") lib.game_step(game.game_id) return { "status": "ok", }