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 = app.response_class( response=json.dumps(x), status=200, mimetype='application/json' ) 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(): ses = db.get_session() team = get_context() game = team.game game = game.lock() team_id = team.team_id min_round = args_get("min_round", int, True) t = datetime.now() state = game.current_state() if state is None: ses.commit() return { "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: ses.commit() return { "status": "too_early", "wait": time_to_end + 1.0 if time_to_end is not None else 3.0, } move = team.get_move(state.round) move.reads_count += 1 ses.commit() 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(): ses = db.get_session() def to_late(): return { "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: game = game.lock() move = team.get_move(state.round_id) move.err_pushs_count += 1 ses.commit() 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 = team.get_move(state.round) move.move = db.new_big_data(j) if warnings is None: move.ok_pushs_count += 1 else: move.warnings_pushs_count += 1 ses.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", }