From 3e9fa79305056a352ebee0d10832a7cfad89c0a3 Mon Sep 17 00:00:00 2001 From: Jiri Kalvoda Date: Sat, 17 Sep 2022 21:44:06 +0200 Subject: [PATCH] =?UTF-8?q?Startegick=C3=A1:=20Init=20logov=C3=A1n=C3=AD?= =?UTF-8?q?=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/bin/db_init | 1 + server/hra/db.py | 34 ++++++++++++++++++++++++++++ server/hra/web/__init__.py | 19 +++++++++++++++- server/hra/web/api.py | 39 ++++++++++++++++++++++++++++----- server/hra/web/pages.py | 45 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 132 insertions(+), 6 deletions(-) diff --git a/server/bin/db_init b/server/bin/db_init index d649f15..db846c5 100755 --- a/server/bin/db_init +++ b/server/bin/db_init @@ -29,6 +29,7 @@ if args.drop: x = db.get_session().execute(row[0]) print(row, x) x = db.get_session().execute("DROP TYPE IF EXISTS step_mode;") + x = db.get_session().execute("DROP TYPE IF EXISTS endpoint;") db.get_session().commit() db.metadata.bind = db.get_engine() diff --git a/server/hra/db.py b/server/hra/db.py index 7e0c998..b3e0d5b 100644 --- a/server/hra/db.py +++ b/server/hra/db.py @@ -114,6 +114,12 @@ class StepMode(MOEnum): automatic = auto() +class Endpoint(MOEnum): + state = auto() + action = auto() + step = auto() + error = auto() + class Game(Base): __tablename__ = 'games' @@ -187,6 +193,34 @@ class State(Base): game = relationship('Game', primaryjoin='State.game_id == Game.game_id') +class Log(Base): + __tablename__= 'logs' + + source_ip = Column(String(20)) + log_id = Column(Integer, primary_key=True) + user_id = Column(Integer, ForeignKey('users.id')) + game_id = Column(Integer, ForeignKey('games.game_id')) + round = Column(Integer) + team_id = Column(Integer) + + endpoint = Column(Enum(Endpoint, name="endpoint"), nullable=False) + url = Column(String(200), nullable=False) + get = Column(JSONB, nullable=False) + time = Column(DateTime, nullable=False) + + status = Column(String(80)) + data = Column(Integer, ForeignKey('bigdata.id')) + text = Column(String(500)) + + game = relationship('Game', primaryjoin='Log.game_id == Game.game_id') + user = relationship('User', primaryjoin='Log.user_id == User.id') + + def get_data(self): + if self.data is None: + return None + return get_big_data(self.data) + + class Move(Base): __tablename__ = 'moves' diff --git a/server/hra/web/__init__.py b/server/hra/web/__init__.py index f803179..a78596e 100644 --- a/server/hra/web/__init__.py +++ b/server/hra/web/__init__.py @@ -57,6 +57,8 @@ def init_request(): return user = None + g.user = None + g.org = False if path.startswith('/api/'): token = request.args.get('token') if token is not None: @@ -80,7 +82,8 @@ def init_request(): if g.org: g.menu += [ MenuItem(app.url_for(pages.web_org_games.__name__), "Hry"), - MenuItem(app.url_for(pages.web_org_users.__name__), "Uživatelé") + MenuItem(app.url_for(pages.web_org_users.__name__), "Uživatelé"), + MenuItem(app.url_for(pages.web_org_logs.__name__), "Logy"), ] else: g.menu += [ @@ -95,6 +98,8 @@ app.before_request(init_request) def handle_exception(e): path = request.path if path.startswith('/api/'): + ses = db.get_session() + ses.rollback() response = e.get_response() response.data = json.dumps({ "status": "error", @@ -103,6 +108,18 @@ def handle_exception(e): "http-name": e.name, }) response.content_type = "application/json" + x = db.Log( + source_ip=request.remote_addr, + user_id=g.user.id if g.user is not None else None, + endpoint=db.Endpoint.error, + status="http-error", + text=f"{e.code} ({e.name}): {e.description}", + url=request.path, + get=request.args, + time=datetime.now(), + ) + ses.add(x) + ses.commit() return response else: return e diff --git a/server/hra/web/api.py b/server/hra/web/api.py index f6f4728..ebecd8f 100644 --- a/server/hra/web/api.py +++ b/server/hra/web/api.py @@ -1,6 +1,7 @@ from flask import Flask, redirect, flash, render_template, session, g, request, get_flashed_messages import werkzeug.exceptions import json +import traceback from datetime import datetime import hra.config as config @@ -9,6 +10,23 @@ from hra.web import app, NeedLoginError import hra.web.jinja_mac as jinja_mac import hra.lib as lib +def log(team, endpoint, status, **kvarg): + x = db.Log( + source_ip=request.remote_addr, + user_id=g.user.id, + team_id=team.team_id, + game_id=team.game_id, + endpoint=endpoint, + status=status, + url=request.path, + get=request.args, + time=datetime.now(), + **kvarg + ) + db.get_session().add(x) + return x + + def json_endpoint(f): def l(*arg, **kvarg): x = f(*arg, **kvarg) @@ -57,6 +75,7 @@ def api_state(): t = datetime.now() state = game.current_state() if state is None: + log(team, db.Endpoint.state, "working", text="wait 1") ses.commit() return { "status": "working", @@ -67,13 +86,16 @@ def api_state(): else: time_to_end = None if min_round is not None and min_round > game.current_round: + wait = time_to_end + 1.0 if time_to_end is not None else 3.0 + log(team, db.Endpoint.state, "too_early", text=f"wait {wait}; min_round {min_round}", 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, + "wait": wait, } move = team.get_move(state.round) move.reads_count += 1 + log(team, db.Endpoint.state, "ok", round=game.current_round) ses.commit() return { "status": "ok", @@ -88,6 +110,8 @@ def api_state(): def api_action(): ses = db.get_session() def to_late(): + log(team, db.Endpoint.action, "too_late", round=round_id) + ses.commit() return { "status": "too_late", } @@ -106,19 +130,21 @@ def api_action(): 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 = team.get_move(state.round) move.err_pushs_count += 1 + description =f"{type(e).__name__}: {e}" + log(team, db.Endpoint.action, "error", round=round_id, text=description, data=db.new_big_data(traceback.format_exc())) ses.commit() return { "status": "error", - "description": f"{type(e).__name__}: {e}" + "description": description } - db.get_session().expire_all() + ses.expire_all() game = game.lock() if game.working_on_next_state or round_id < game.current_round: - db.get_session().commit() + ses.commit() return to_late() move = team.get_move(state.round) @@ -130,6 +156,7 @@ def api_action(): else: move.warnings_pushs_count += 1 + log(team, db.Endpoint.action, "warning" if warnings is not None else "ok", round=round_id, data=move.move) ses.commit() if warnings is not None: @@ -148,6 +175,8 @@ def api_step(): raise werkzeug.exceptions.Forbidden("Je zakázáno krokovat tuto hru") lib.game_step(game.game_id) + log(team, db.Endpoint.step, "ok") + db.get_session.commit() return { "status": "ok", diff --git a/server/hra/web/pages.py b/server/hra/web/pages.py index 9326732..b10dbd4 100644 --- a/server/hra/web/pages.py +++ b/server/hra/web/pages.py @@ -503,4 +503,49 @@ def web_org_user_su(user_id): flash("Uživatel vtělen!") return redirect('/') +@app.route("/org/log/", methods=['GET']) +def web_org_log(log_id): + l = db.get_session().query(db.Log).filter_by(log_id=log_id).one_or_none() + if l is None: + raise werkzeug.exceptions.NotFound() + b = BasePage() + b.line().h2("Log ", log_id) + b.p().pre(pprint.pformat(l.get_data())) + return b.print_file() + + +@app.route("/org/logs", methods=['GET']) +def web_org_logs(): + logs = db.get_session().query(db.Log).order_by(db.Log.log_id.desc()).all() + b = BasePage() + + b.line().h2("Logy") + with b.p().table(_class="data full"): + with b.thead(): + b.line().th()("Čas") + b.line().th()("Uživatel") + b.line().th()("URL") + b.line().th()("Hra") + b.line().th()("Tým") + b.line().th()("Status") + b.line().th()("Popis") + b.line().th()("GET") + b.line().th()("Akce") + for l in logs: + with b.tr(): + b.line().td(l.time.strftime("%H:%M:%S")) + b.line().td(l.user.print() if l.user else None, b._br(), l.source_ip) + b.line().td(l.endpoint._name_, b._br(), l.url) + b.line().td(l.game.print() if l.game else None) + b.line().td(l.team_id) + b.line().td(l.status) + b.line().td(l.text) + with b.line().td(): + for k,v in l.get.items(): + b(f"{k}: {v}").br() + with b.line().td(): + with b.div(_class="btn-group", role="group"): + b.a(href=app.url_for(web_org_log.__name__, log_id=l.log_id), _class="btn btn-xs btn-primary")("Podrobnosti") + return b.print_file() + from hra.web.game import get_wlogic, wlogic_by_mode