From 48937151185417a81b63619f231eeb24c0b5f455 Mon Sep 17 00:00:00 2001 From: sherl Date: Tue, 27 May 2025 01:28:40 +0200 Subject: [PATCH] fix: db revamp, add relations, annotate the db also allows for request retrieval, and adds decorator for authed access --- FlaskWebProject/FlaskWebProject/lewy.py | 2 +- FlaskWebProject/FlaskWebProject/lewy_api.py | 2 +- .../FlaskWebProject/lewy_api_v1.py | 42 ++++- FlaskWebProject/FlaskWebProject/lewy_db.py | 162 ++++++++++-------- .../FlaskWebProject/lewy_globals.py | 11 +- 5 files changed, 139 insertions(+), 80 deletions(-) diff --git a/FlaskWebProject/FlaskWebProject/lewy.py b/FlaskWebProject/FlaskWebProject/lewy.py index cdacfd5..6f569e2 100644 --- a/FlaskWebProject/FlaskWebProject/lewy.py +++ b/FlaskWebProject/FlaskWebProject/lewy.py @@ -72,7 +72,7 @@ def setup(): app.add_url_rule('/api/', view_func=lewy_api.api_greeting) app.add_url_rule('/api/', view_func=lewy_api.api_global_catchall) - db = lewy_db.initDB(app, config) + db = lewy_globals.setupDb(app, config) scrape = scraper() with app.app_context(): diff --git a/FlaskWebProject/FlaskWebProject/lewy_api.py b/FlaskWebProject/FlaskWebProject/lewy_api.py index d718cdb..5581570 100644 --- a/FlaskWebProject/FlaskWebProject/lewy_api.py +++ b/FlaskWebProject/FlaskWebProject/lewy_api.py @@ -27,7 +27,7 @@ def api_global_catchall(received_request): if request_list == [''] or request_list == []: resp = api_greeting() try: - status, received, data = lewy_api_v1.lookup(request_list) + status, received, data = lewy_api_v1.lookup(request_list, request) except Exception as e: lewy_globals.apiFailedRequests += 1 stripped_filename = __file__[max(__file__.rfind("/"), __file__.rfind("\\")) + 1:] diff --git a/FlaskWebProject/FlaskWebProject/lewy_api_v1.py b/FlaskWebProject/FlaskWebProject/lewy_api_v1.py index 01754e6..16c6985 100644 --- a/FlaskWebProject/FlaskWebProject/lewy_api_v1.py +++ b/FlaskWebProject/FlaskWebProject/lewy_api_v1.py @@ -2,19 +2,40 @@ # - HTTP status code, # - human-readable status message, # - json with appropriate data +from flask_sqlalchemy import SQLAlchemy +from functools import wraps +from lewy_globals import getDb, colors as c import flask, json, time +import lewy_db import lewy_globals -def incrementBadRequests(): +def require_authentication(func): + @wraps(func) + def wrapper(*args, **kwargs): + token = kwargs["r"].args.get('token') + if token == lewy_globals.config['api']['api_key']: + try: + status, received, data = func(*args, **kwargs) + return status, received, data + except: + raise AssertionError(f"Function \"{func.__name__}\" does not return status, code, and data as it should!") + else: + return 401, "error", {'error_msg': "Unauthorized"} + return wrapper + +def increment_bad_requests(): lewy_globals.apiFailedRequests += 1 -def notImplemented(data): +def not_implemented(data): # TODO: change list to string -> data, not data[0] return 501, f"not recognised/implemented: {data[0]}", [] def stub_hello(): return 200, 'hello from v1! stats are at /api/v1/stats', [] +def epochToDate(epoch): + return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(epoch)) + def stats(): data_to_send = { "start_time": lewy_globals.starttime, @@ -25,11 +46,18 @@ def stats(): "outside_api_requests": lewy_globals.outsideApiHits, "local_api_requests": lewy_globals.apiRequests - lewy_globals.outsideApiHits } - return 200, "OK", data_to_send + return 200, "ok", data_to_send +def get_matches(): + pass +@require_authentication +def debugger_halt(r): + print(f"{c.WARNING}[{epochToDate(time.time())}]{c.ENDC} {r.remote_addr} triggered a debugger halt!") + breakpoint() + return 200, "ok", [] -def lookup(data): +def lookup(data, request): if data == []: return stub_hello() match data[0].lower(): @@ -39,6 +67,8 @@ def lookup(data): return stub_hello() case 'info': return stub_hello() + case 'halt': + return debugger_halt(r = request) case _: - incrementBadRequests() - return notImplemented(data) \ No newline at end of file + increment_bad_requests() + return not_implemented(data) \ No newline at end of file diff --git a/FlaskWebProject/FlaskWebProject/lewy_db.py b/FlaskWebProject/FlaskWebProject/lewy_db.py index 05356a9..a3c23a2 100644 --- a/FlaskWebProject/FlaskWebProject/lewy_db.py +++ b/FlaskWebProject/FlaskWebProject/lewy_db.py @@ -1,86 +1,106 @@ +from datetime import datetime from flask_sqlalchemy import SQLAlchemy +from sqlalchemy import ForeignKey +from sqlalchemy.orm import Mapped, mapped_column, DeclarativeBase, relationship +from typing import List import toml global db def initDB(app, config): - tablenameprefix = config['general']['db_prefix'] + "_lewangoalski_" + global sportowcy, trofea, sportowcy_w_meczach, statystyki_sportowcow, kluby, mecze + tnp = config['general']['db_prefix'] + "_lewangoalski_" - db = SQLAlchemy(app) + class Base(DeclarativeBase): + pass - class sportowcy(db.Model): - __tablename__ = tablenameprefix + "sportowcy" - id_zawodnika = db.Column(db.Integer, primary_key=True) - data_urodzenia = db.Column(db.String(10)) - czy_aktywny = db.Column(db.Boolean) - klub = db.Column(db.String(63)) - narodowosc = db.Column(db.String(3)) - ilosc_trofeow = db.Column(db.Integer) - ostatnie_trofeum = db.Column(db.Integer) - pierwszy_mecz = db.Column(db.Integer) - # ostatni_mecz = db.Column(db.Integer) # statystyki_sportowcow już to przechowuje - wycena = db.Column(db.BigInteger) - ostatni_gol_dla = db.Column(db.String(3)) - statystyka = db.Column(db.Integer) + db = SQLAlchemy(app, model_class=Base) - class trofea(db.Model): - __tablename__ = tablenameprefix + "trofea" - id_trofeum = db.Column(db.Integer, primary_key=True) - id_zawodnika = db.Column(db.Integer) # != None - nazwa = db.Column(db.String(127)) - sezon = db.Column(db.String(9)) - rok = db.Column(db.String(4)) + class sportowcy(Base): + __tablename__ = tnp + "sportowcy" + id_zawodnika: Mapped[ int] = mapped_column(primary_key=True) + zewnetrzne_id_zawodnika: Mapped[ str] = mapped_column() + data_urodzenia: Mapped[ str] = mapped_column() + czy_aktywny: Mapped[ bool] = mapped_column() + klub_id: Mapped[ List[str]] = mapped_column(ForeignKey(f"{tnp}kluby.id_klubu")) + klub: Mapped[ List["kluby"]] = relationship(back_populates="sportowcy_w_klubie", foreign_keys=[klub_id]) + narodowosc: Mapped[ str] = mapped_column() + ilosc_trofeow: Mapped[ int] = mapped_column() + ostatnie_trofeum_id: Mapped[ int] = mapped_column(ForeignKey(f"{tnp}trofea.id_trofeum")) + ostatnie_trofeum: Mapped[ "trofea"] = relationship(back_populates="zawodnik", foreign_keys=[ostatnie_trofeum_id]) + pierwszy_mecz: Mapped[ int] = mapped_column() + wycena: Mapped[ int] = mapped_column() + ostatni_gol_dla_id: Mapped[ str] = mapped_column(ForeignKey(f"{tnp}kluby.id_klubu")) + ostatni_gol_dla: Mapped[ "kluby"] = relationship(back_populates="sportowcy_ostatni_gol", foreign_keys=[ostatni_gol_dla_id]) + statystyki_id: Mapped[ List[int]] = mapped_column(ForeignKey(f"{tnp}statystyki_sportowcow.id_statystyki")) + statystyki: Mapped[List["statystyki_sportowcow"]] = relationship(back_populates="sportowiec") + trofea: Mapped[ List["trofea"]] = relationship(back_populates="zawodnik", foreign_keys="[trofea.id_zawodnika]") - class sportowcy_w_meczach(db.Model): - __tablename__ = tablenameprefix + "sportowcy_w_meczach" - id_rekordu = db.Column(db.Integer, primary_key=True) - id_zawodnika = db.Column(db.Integer) # != None - zewnetrzne_id_meczu = db.Column(db.Integer) # != None - czas_gry = db.Column(db.Integer) - goli = db.Column(db.Integer) - asyst = db.Column(db.Integer) - interwencje_bramkarza = db.Column(db.Integer) - suma_interwencji_na_bramke = db.Column(db.Integer) - zolte_kartki = db.Column(db.Integer) - czerwone_kartki = db.Column(db.Integer) - wygrana = db.Column(db.Integer) - wynik = db.Column(db.Float) + class trofea(Base): + __tablename__ = tnp + "trofea" + id_trofeum: Mapped[ int] = mapped_column(primary_key=True) + id_zawodnika: Mapped[ int] = mapped_column(ForeignKey(f"{tnp}sportowcy.id_zawodnika")) + zawodnik: Mapped[ "sportowcy"] = relationship(back_populates="trofea", foreign_keys=[id_zawodnika]) + nazwa: Mapped[ str] = mapped_column() + sezon: Mapped[ str] = mapped_column() + rok: Mapped[ str] = mapped_column() - class statystyki_sportowcow(db.Model): - __tablename__ = tablenameprefix + "statystyki_sportowcow" - id_statystyki = db.Column(db.Integer, primary_key=True) - ostatni_mecz = db.Column(db.Integer) - ilosc_wystapien = db.Column(db.Integer) - minut_gry = db.Column(db.BigInteger) - gier_sum = db.Column(db.Integer) - goli_sum = db.Column(db.Integer) - asyst_sum = db.Column(db.Integer) - interwencji_sum = db.Column(db.Integer) - nieobronionych_interwencji_sum = db.Column(db.Integer) - zoltych_kartek_sum = db.Column(db.Integer) - czerwonych_kartek_sum = db.Column(db.Integer) - wygranych_sum = db.Column(db.Integer) - wynik_sum = db.Column(db.Integer) - meczow_do_wynikow_sum = db.Column(db.Integer) + class sportowcy_w_meczach(Base): + __tablename__ = tnp + "sportowcy_w_meczach" + id_rekordu: Mapped[ int] = mapped_column(primary_key=True) + id_zawodnika: Mapped[ int] = mapped_column(ForeignKey(f"{tnp}sportowcy.id_zawodnika")) + zawodnik: Mapped[ "sportowcy"] = relationship() + zewnetrzne_id_meczu: Mapped[ str] = mapped_column(ForeignKey(f"{tnp}mecze.zewnetrzne_id_meczu")) + czas_gry: Mapped[ int] = mapped_column() + goli: Mapped[ int] = mapped_column() + asyst: Mapped[ int] = mapped_column() + interwencje_bramkarza: Mapped[ int] = mapped_column() + suma_interwencji_na_bramke: Mapped[ int] = mapped_column() + zolte_kartki: Mapped[ int] = mapped_column() + czerwone_kartki: Mapped[ int] = mapped_column() + wygrana: Mapped[ int] = mapped_column() + wynik: Mapped[ float] = mapped_column() - class kluby(db.Model): - __tablename__ = tablenameprefix + "kluby" - id_klubu = db.Column(db.String(63), primary_key=True) - pelna_nazwa = db.Column(db.String(63)) - skrocona_nazwa = db.Column(db.String(3)) + class statystyki_sportowcow(Base): + __tablename__ = tnp + "statystyki_sportowcow" + id_statystyki: Mapped[ int] = mapped_column(primary_key=True) + sportowiec: Mapped[ "sportowcy"] = relationship(back_populates="statystyki") + ostatni_mecz: Mapped[ int] = mapped_column(ForeignKey(f"{tnp}mecze.id_meczu")) + ilosc_wystapien: Mapped[ int] = mapped_column() + minut_gry: Mapped[ int] = mapped_column() + gier_sum: Mapped[ int] = mapped_column() + goli_sum: Mapped[ int] = mapped_column() + asyst_sum: Mapped[ int] = mapped_column() + interwencji_sum: Mapped[ int] = mapped_column() + nieobronionych_interwencji_sum: Mapped[ int] = mapped_column() + zoltych_kartek_sum: Mapped[ int] = mapped_column() + czerwonych_kartek_sum: Mapped[ int] = mapped_column() + wygranych_sum: Mapped[ int] = mapped_column() + wynik_sum: Mapped[ int] = mapped_column() + meczow_do_wynikow_sum: Mapped[ int] = mapped_column() - class mecze(db.Model): - __tablename__ = tablenameprefix + "mecze" - id_meczu = db.Column(db.Integer, primary_key=True) - zewnetrzne_id_meczu = db.Column(db.String(15)) # != None - data = db.Column(db.DateTime) - gospodarze = db.Column(db.String(3)) - goscie = db.Column(db.String(3)) - gosp_wynik = db.Column(db.Integer) - gosc_wynik = db.Column(db.Integer) - sezon = db.Column(db.String(9)) - nazwa_turnieju = db.Column(db.String(127)) - skrocona_nazwa_turnieju = db.Column(db.String(15)) - flaga = db.Column(db.Integer) + class kluby(Base): + __tablename__ = tnp + "kluby" + id_klubu: Mapped[ str] = mapped_column(primary_key=True) + pelna_nazwa: Mapped[ str] = mapped_column() + skrocona_nazwa: Mapped[ str] = mapped_column() + sportowcy_w_klubie: Mapped[ List["sportowcy"]] = relationship(back_populates="klub", foreign_keys="[sportowcy.klub_id]") + sportowcy_ostatni_gol: Mapped[ "sportowcy"] = relationship(back_populates="ostatni_gol_dla", foreign_keys="[sportowcy.ostatni_gol_dla_id]") + + class mecze(Base): + __tablename__ = tnp + "mecze" + id_meczu: Mapped[ int] = mapped_column(primary_key=True) + zewnetrzne_id_meczu: Mapped[ str] = mapped_column(unique=True) + data: Mapped[ datetime] = mapped_column() + gospodarze_id: Mapped[ str] = mapped_column(ForeignKey(f"{tnp}kluby.id_klubu")) + gospodarze: Mapped[ "kluby"] = relationship(foreign_keys=[gospodarze_id]) + goscie_id: Mapped[ str] = mapped_column(ForeignKey(f"{tnp}kluby.id_klubu")) + goscie: Mapped[ "kluby"] = relationship(foreign_keys=[goscie_id]) + gosp_wynik: Mapped[ int] = mapped_column() + gosc_wynik: Mapped[ int] = mapped_column() + sezon: Mapped[ str] = mapped_column() + nazwa_turnieju: Mapped[ str] = mapped_column() + skrocona_nazwa_turnieju: Mapped[ str] = mapped_column() + flaga: Mapped[ int] = mapped_column() return db \ No newline at end of file diff --git a/FlaskWebProject/FlaskWebProject/lewy_globals.py b/FlaskWebProject/FlaskWebProject/lewy_globals.py index 3866d62..93efd02 100644 --- a/FlaskWebProject/FlaskWebProject/lewy_globals.py +++ b/FlaskWebProject/FlaskWebProject/lewy_globals.py @@ -2,8 +2,9 @@ from git import Repo # hash ostatniego commitu import os import time import toml +import lewy_db -global config, randomly_generated_passcode +global db, config, randomly_generated_passcode class colors: HEADER = '\033[95m' @@ -81,6 +82,14 @@ def getConfig(configfile): else: return toml.load(configfile) +def setupDb(app, config): + global db + db = lewy_db.initDB(app, config) + return db + +def getDb(): + return db + def setConfig(configfile): global config config = getConfig(configfile)