diff --git a/FlaskWebProject/FlaskWebProject/lewy_api_v1.py b/FlaskWebProject/FlaskWebProject/lewy_api_v1.py index a4e0418..cef716c 100644 --- a/FlaskWebProject/FlaskWebProject/lewy_api_v1.py +++ b/FlaskWebProject/FlaskWebProject/lewy_api_v1.py @@ -133,6 +133,116 @@ def get_matches(r = None, id_zawodnika: str | None = None, rok: int | None = Non # print(f"zwracam mecze: {response_json}") return 200, "ok", response_json +# GET /api/v1/player_stats +def player_stats(r = None, id_zawodnika: str | None = None): + """ + Zwraca statystyki gracza. + Przykład wywołania: + player_stats(r, id_zawodnika=1), tożsame z GET /api/v1/player_stats?id_zawodnika=1 + player_stats(r), tożsame z GET /api/v1/player_stats + """ + response_json = [] + + if id_zawodnika is None: + # Gdy nie podano id wprost, sprawdź, czy podano je przez parametr. + id_zawodnika = r.args.get('id_zawodnika', 0) + + # Sprawdź, czy sportowiec o podanym (lub niepodanym) id istnieje. + elif not czy_sportowiec_istnieje(id_zawodnika=id_zawodnika): + return 404, "error", {"error_msg": "This sportsman has not been found in the database. Try: id_zawodnika=1"} + + # Gdy sportowiec istnieje, wypisz jego statystyki. + else: + staty = getDb().get_basic_stats(id_zawodnika=id_zawodnika) + + # for stat in staty: + response_json.append({ + 'unique_items': staty[0], + 'time_played': staty[1], + 'goals': staty[2], + 'assists': staty[3], + 'yellow_cards': staty[4], + 'red_cards': staty[5], + 'avg_score': staty[6], + 'wins': staty[7], + 'draws': staty[8], + 'losses': staty[9] + } + ) + print(f"zwracam staty: {response_json}") + return 200, "ok", response_json + +# GET /api/v1/robert_stats +def robert_stats(r = None, id_klubu: str | None = None): + """ + Zwraca statystyki Roberta w danym klubie. + Przykład wywołania: + robert_stats(r, id_klubu="barcelona"), tożsame z GET /api/v1/robert_stats?id_klubu=barcelona + """ + response_json = [] + + if id_klubu is None: + # Gdy nie podano id wprost, sprawdź, czy podano je przez parametr. + id_klubu = r.args.get('id_klubu', "non-existent-club-id") + + # Sprawdź, czy klub o podanym (lub niepodanym) id istnieje. + if not czy_klub_istnieje(id_klubu=id_klubu): + return 404, "error", {"error_msg": "This club has not been found in the database. Try: id_klubu=barcelona"} + + # Gdy klub istnieje, wypisz statystyki z tego klubu. + else: + staty = getDb().get_sportsman_club_stats(id_zawodnika=1, id_klubu=id_klubu) + + # for stat in staty: + print(staty) + response_json.append({ + 'unique_items': staty[0], + 'time_played': staty[1], + 'goals': staty[2], + 'assists': staty[3], + 'yellow_cards': staty[4], + 'red_cards': staty[5], + 'avg_score': staty[6], + 'wins': staty[7], + 'draws': staty[8], + 'losses': staty[9] + } + ) + print(f"zwracam staty roberta: {response_json}") + return 200, "ok", response_json + +# GET /api/v1/clubs +def clubs(r, id_klubu: str = None): + """ + Zwraca informacje o klubach. + Przykład wywołania: + clubs(r, id_klubu="barcelona"), tożsame z GET /api/v1/clubs?id_klubu=barcelona + clubs(r), tożsame z GET /api/v1/clubs + """ + response_json = [] + + if id_klubu is None: + # Gdy nie podano id wprost, sprawdź, czy podano je przez parametr. + id_klubu = r.args.get('id_klubu', -1) + + if id_klubu == -1: + kluby = getDb().simple_select_all("kluby") + + # Sprawdź, czy sportowiec o podanym (lub niepodanym) id istnieje. + # Jeśli nie istnieje, wypisz wszystkie mecze. + elif not czy_klub_istnieje(id_klubu=id_klubu): + return 404, "error", {"error_msg": "This club has not been found in the database. Try: id_klubu=barcelona"} + + # Gdy sportowiec istnieje, wypisz jego mecze. + else: + kluby = getDb().simple_select_all("kluby", id_klubu=id_klubu) + + for klub in kluby: + response_json.append(klub.jsonify()) + + print(f"zwracam kluby: {response_json}") + return 200, "ok", response_json + # GET /api/v1/debugger_halt?token=XXX... @require_authentication def debugger_halt(r): @@ -185,6 +295,12 @@ def lookup(data, request): return debugger_halt(r = request) case 'matches': return get_matches(r = request) + case 'player_stats': + return player_stats(r = request) + case 'robert_stats': + return robert_stats(r = request) + case 'clubs': + return clubs(r = request) case _: increment_bad_requests() - return not_implemented(data) \ No newline at end of file + return not_implemented(data) diff --git a/FlaskWebProject/FlaskWebProject/lewy_db.py b/FlaskWebProject/FlaskWebProject/lewy_db.py index 7c04692..e45a6de 100644 --- a/FlaskWebProject/FlaskWebProject/lewy_db.py +++ b/FlaskWebProject/FlaskWebProject/lewy_db.py @@ -1,7 +1,7 @@ from datetime import datetime from flask_sqlalchemy import SQLAlchemy from functools import wraps -from sqlalchemy import ForeignKey, select, insert, update, extract + from sqlalchemy.orm import Mapped, mapped_column, DeclarativeBase, Session, relationship from typing import List import time @@ -105,6 +105,8 @@ class baza(): czerwone_kartki: Mapped[ int] = mapped_column() wygrana: Mapped[ int] = mapped_column() wynik: Mapped[ float] = mapped_column() + klub_id: Mapped[ int] = mapped_column(ForeignKey(f"{tnp}kluby.id_klubu")) + klub: Mapped[ "kluby"] = relationship() def __repr__(self): return f"<{self.zawodnik.imie} {self.zawodnik.nazwisko} w meczu {self.mecz.gospodarze.skrocona_nazwa} vs. {self.mecz.goscie.skrocona_nazwa}>" @@ -143,6 +145,13 @@ class baza(): def __repr__(self): return f"" + def jsonify(self): + return { + "id_klubu": self.id_klubu, + "pelna_nazwa": self.pelna_nazwa, + "skrocona_nazwa": self.skrocona_nazwa + } + class mecze(Base): __tablename__ = tnp + "mecze" id_meczu: Mapped[ int] = mapped_column(primary_key=True, autoincrement=True) @@ -201,12 +210,12 @@ class baza(): with self.app.app_context(): self.session = Session(self.db.engine) - def exit_gracefully(func): - @wraps(func) + def exit_gracefully(fun): + @wraps(fun) def wrapper(self, *args, **kwargs): return_val = None try: - return_val = func(self, *args, **kwargs) + return_val = fun(self, *args, **kwargs) except: print(f"{c.FAIL}" f"Wystąpił błąd podczas wykonywania zapytania SQL:" @@ -553,6 +562,133 @@ class baza(): return query.all() + @exit_gracefully + def get_basic_stats(self, id_zawodnika = None, zewnetrzne_id_zawodnika = None): + + # Spróbuj otrzymać id zawodnika z zewnętrznego id. + if zewnetrzne_id_zawodnika is not None: + id_zawodnika = self.get_id_zawodnika_by_zewnetrzne_id(zewnetrzne_id_zawodnika) + + if id_zawodnika is None: + return [] + + # Aliasy + SportowcyWMeczach = self.entities["sportowcy_w_meczach"] + + # Zlicz liczbę zwycięstw, remisów i przegranych. + # https://stackoverflow.com/a/3975328 + # https://stackoverflow.com/a/49064561 + # Czy da się to zrobić prościej? Nie wiem. ~ sherl + query = self.session.query( + func.count(SportowcyWMeczach.id_rekordu ).label('unique_items'), + func.sum( SportowcyWMeczach.czas_gry ).label('time_played'), + func.sum( SportowcyWMeczach.goli ).label('goals'), + func.sum( SportowcyWMeczach.asyst ).label('assists'), + func.sum( SportowcyWMeczach.zolte_kartki ).label('yellow_cards'), + func.sum( SportowcyWMeczach.czerwone_kartki).label('red_cards'), + func.avg( SportowcyWMeczach.wynik ).label('avg_score'), + + # Kompatybilne z każdą bazą. + # https://docs.sqlalchemy.org/en/20/core/sqlelement.html#sqlalchemy.sql.expression.case + # https://modern-sql.com/feature/filter + func.sum( + case( + (SportowcyWMeczach.wygrana == 1, 1), + else_ = 0 + ) + ).label('wins'), + + func.sum( + case( + (SportowcyWMeczach.wygrana == 0, 1), + else_ = 0 + ) + ).label('draws'), + + func.sum( + case( + (SportowcyWMeczach.wygrana == -1, 1), + else_ = 0 + ) + ).label('losses'), + + ).where( + SportowcyWMeczach.czas_gry > 0 + ).where( + SportowcyWMeczach.id_zawodnika == id_zawodnika + ) + + return query.all()[0] + + @exit_gracefully + def get_sportsman_club_stats(self, id_zawodnika = None, zewnetrzne_id_zawodnika = None, id_klubu = None): + + # Spróbuj otrzymać id zawodnika z zewnętrznego id. + if zewnetrzne_id_zawodnika is not None: + id_zawodnika = self.get_id_zawodnika_by_zewnetrzne_id(zewnetrzne_id_zawodnika) + + if id_zawodnika is None: + return [] + + if id_klubu is None: + return self.get_basic_stats(id_zawodnika=id_zawodnika) + else: + if self.simple_select_all("kluby", id_klubu=id_klubu) == []: + return [] + + # Aliasy + SportowcyWMeczach = self.entities["sportowcy_w_meczach"] + + # Zlicz liczbę zwycięstw, remisów i przegranych. + # https://stackoverflow.com/a/3975328 + # https://stackoverflow.com/a/49064561 + # Czy da się to zrobić prościej? Nie wiem. ~ sherl + query = self.session.query( + func.count(SportowcyWMeczach.id_rekordu ).label('unique_items'), + func.sum( SportowcyWMeczach.czas_gry ).label('time_played'), + func.sum( SportowcyWMeczach.goli ).label('goals'), + func.sum( SportowcyWMeczach.asyst ).label('assists'), + func.sum( SportowcyWMeczach.zolte_kartki ).label('yellow_cards'), + func.sum( SportowcyWMeczach.czerwone_kartki).label('red_cards'), + func.avg( SportowcyWMeczach.wynik ).label('avg_score'), + + # Kompatybilne z każdą bazą. + # https://docs.sqlalchemy.org/en/20/core/sqlelement.html#sqlalchemy.sql.expression.case + # https://modern-sql.com/feature/filter + func.sum( + case( + (SportowcyWMeczach.wygrana == 1, 1), + else_ = 0 + ) + ).label('wins'), + + # UWAGA! Jeśli jest remis, to nie wiemy dla którego klubu grał sportowiec! + # Poniższe wyrażenie jest poprawne, to dane zwracane przez flashscore są + # niewystarczające, aby stwierdzić w którym klubie grał sportowiec! + func.sum( + case( + (SportowcyWMeczach.wygrana == 0, 1), + else_ = 0 + ) + ).label('draws'), + + func.sum( + case( + (SportowcyWMeczach.wygrana == -1, 1), + else_ = 0 + ) + ).label('losses'), + + ).where( + SportowcyWMeczach.czas_gry > 0 + ).where( + SportowcyWMeczach.id_zawodnika == id_zawodnika + ).where( + SportowcyWMeczach.klub_id == id_klubu + ) + + return query.all()[0] + @exit_gracefully def sample_data_init(self, override_safety_check=False): """ diff --git a/FlaskWebProject/FlaskWebProject/lewy_routes.py b/FlaskWebProject/FlaskWebProject/lewy_routes.py index 7634d2d..e3b0c2f 100644 --- a/FlaskWebProject/FlaskWebProject/lewy_routes.py +++ b/FlaskWebProject/FlaskWebProject/lewy_routes.py @@ -3,39 +3,47 @@ import lewy_api_v1 import lewy_db import lewy_globals import json -from lewy_api_v1 import get_matches +from lewy_api_v1 import get_matches, player_stats, robert_stats, clubs as get_clubs def get_lewy_stats(): - return { - 'all_time_stats': { - 'goals': 589+85, - 'assists':154+35, - 'matches': 791+158, - }, - 'club_stats': { - 'goals': 589, - 'assists': 154, - 'matches': 791, - }, - 'nation_stats': { - 'goals': 85, - 'assists': 35, - 'matches': 158, - }, - 'international_cups': { - 'goals': 110, - 'assists': 19, - 'matches': 152, - }, - 'national_cups': { - 'goals': 58, - 'assists': 4, - 'matches': 74, - }, - 'cards': { - 'yellow': 86, - 'red': 2, - } - } + stats = player_stats(id_zawodnika=1)[2][0] + polska = robert_stats(id_klubu="polska")[2][0] + barcelona = robert_stats(id_klubu="barcelona")[2][0] + return { + 'all_time_stats': { + 'goals': stats["goals"], # 589+85 + 'assists': stats["assists"], # 154+35 + 'matches': stats["unique_items"] # 791+158 + }, + 'club_stats': { + 'goals': barcelona["goals"], # 589 + 'assists': barcelona["assists"], # 154 + 'matches': barcelona["unique_items"] # 791 # to trochę na wyrost, bo w części meczy był kontuzjowany + }, + 'nation_stats': { + 'goals': polska["goals"], # 85 + 'assists': polska["assists"], # 35 + 'matches': polska["unique_items"] # 158 + }, + 'international_cups': { + 'goals': 110, + 'assists': 19, + 'matches': 152, + }, + 'national_cups': { + 'goals': 58, + 'assists': 4, + 'matches': 74, + }, + 'cards': { + 'yellow': stats["yellow_cards"], # 86 + 'red': stats["red_cards"], # 2 + }, + 'wins_losses': { + 'wins': stats["wins"], + 'draws': stats["draws"], + 'losses': stats["losses"] + } + } def index(): dark_mode = request.cookies.get('darkMode', 'disabled') @@ -75,28 +83,68 @@ def statystyki(): return render_template('stats.html', **dane) def clubs(): - selected_club = request.args.get("club","FC Barcelona") - clubs = [ - {'club': 'FC Barcelona', 'goals': 101,'assist':20, 'matches':147,'minutes_played': 11684,'yellow_card':12,'red_card': 1, 'wins':101, 'draws': 14,'lost': 32}, - {'club': 'Bayern Monachium', 'goals': 344,'assist':73,'matches':375,'minutes_played': 31759,'yellow_card':36,'red_card': 0, 'wins':307, 'draws': 35,'lost': 33}, - {'club': 'Borussia Dortmund', 'goals': 103,'assist':42,'matches':187,'minutes_played': 14374,'yellow_card':19,'red_card': 1, 'wins':120, 'draws': 40,'lost': 27}, - {'club': 'Lech Poznan', 'goals': 41,'assist':19,'matches':82,'minutes_played': 6858,'yellow_card':9,'red_card': 0, 'wins':'-', 'draws': '-','lost': '-'}, - ] - return render_template('club.html', clubs=clubs, selected_club=selected_club) + selected_club = request.args.get("club", "barcelona") + #clubs = [ + # {'club': 'FC Barcelona', 'goals': 101,'assist':20, 'matches':147,'minutes_played': 11684,'yellow_card':12,'red_card': 1, 'wins':101, 'draws': 14,'lost': 32}, + # {'club': 'Bayern Monachium', 'goals': 344,'assist':73,'matches':375,'minutes_played': 31759,'yellow_card':36,'red_card': 0, 'wins':307, 'draws': 35,'lost': 33}, + # {'club': 'Borussia Dortmund', 'goals': 103,'assist':42,'matches':187,'minutes_played': 14374,'yellow_card':19,'red_card': 1, 'wins':120, 'draws': 40,'lost': 27}, + # {'club': 'Lech Poznan', 'goals': 41,'assist':19,'matches':82,'minutes_played': 6858,'yellow_card':9,'red_card': 0, 'wins':'-', 'draws': '-','lost': '-'}, + #] + response_json = { + "club": "Błędny klub", + "goals": 0, + "assist": 0, + "matches": 0, + "minutes_played": 0, + "yellow_card": 0, + "red_card": 0, + "wins": 0, + "draws": 0, + "lost": 0 + } + + club = get_clubs(request, id_klubu=selected_club) + if club[1] == "ok": # gdy nie wystąpił bład, bo jest taki klub + club = club[2][0] + staty = robert_stats(id_klubu=club["id_klubu"])[2][0] + response_json["club"] = club["pelna_nazwa"] + response_json["goals"] = staty["goals"] + response_json["assist"] = staty["assists"] + response_json["matches"] = staty["unique_items"] + response_json["minutes_played"] = staty["time_played"] + response_json["yellow_card"] = staty["yellow_cards"] + response_json["red_card"] = staty["red_cards"] + response_json["wins"] = staty["wins"] + response_json["draws"] = staty["draws"] + response_json["lost"] = staty["losses"] + + return render_template('club.html', clubs=clubs, selected_club=selected_club, resp_json=response_json) def representation(): - nation_stats = { - 'goals': 85, - 'assists': 35, - 'matches': 158, - 'minutes_played': 12108, - 'yellow_card':10, - 'red_card': 0, - 'wins':75, - 'draws': 35, - 'lost': 48 - } - return render_template('representation.html', nation_stats=nation_stats) + # nation_stats = { + # 'goals': 85, + # 'assists': 35, + # 'matches': 158, + # 'minutes_played': 12108, + # 'yellow_card':10, + # 'red_card': 0, + # 'wins':75, + # 'draws': 35, + # 'lost': 48 + # } + response_json = {} + staty = robert_stats(id_klubu="polska")[2][0] + response_json["goals"] = staty["goals"] + response_json["assist"] = staty["assists"] + response_json["matches"] = staty["unique_items"] + response_json["minutes_played"] = staty["time_played"] + response_json["yellow_card"] = staty["yellow_cards"] + response_json["red_card"] = staty["red_cards"] + response_json["wins"] = staty["wins"] + response_json["draws"] = staty["draws"] + response_json["lost"] = staty["losses"] + return render_template('representation.html', nation_stats=response_json) + def compare(): selected_player = request.args.get("player","Leo Messi") lewy=get_lewy_stats() diff --git a/FlaskWebProject/FlaskWebProject/templates/club.html b/FlaskWebProject/FlaskWebProject/templates/club.html index 1000f31..f7ae429 100644 --- a/FlaskWebProject/FlaskWebProject/templates/club.html +++ b/FlaskWebProject/FlaskWebProject/templates/club.html @@ -4,59 +4,59 @@ {% block content %}
- + - + - + - +
-{% for stats in clubs %} - {% if stats.club == selected_club %} +{#{% for stats in clubs %}#} + {#{% if stats.club == selected_club %}#}
-

Statystyki dla {{selected_club}}

-
+

Statystyki dla {{resp_json.club}}

+
-

Gole:

{{ stats.goals }} +

Gole:

{{ resp_json.goals }}
-

Asysty:

{{ stats.assist }} +

Asysty:

{{ resp_json.assist }}
-

Występy:

{{ stats.matches }} +

Występy:

{{ resp_json.matches }}
-

Łączny czas gry:

{{ stats.minutes_played }} +

Łączny czas gry:

{{ resp_json.minutes_played }}
-

Żółte kartki:

{{ stats.yellow_card }} +

Żółte kartki:

{{ resp_json.yellow_card }}
-

Czerwone kartki:

{{ stats.red_card }} +

Czerwone kartki:

{{ resp_json.red_card }}
-

Zwycięstwa:

{{ stats.wins }} +

Zwycięstwa:

{{ resp_json.wins }}
-

Remisy:

{{ stats.draws }} +

Remisy:

{{ resp_json.draws }}
-

Porażki:

{{ stats.lost }} +

Porażki:

{{ resp_json.lost }}
- {% endif %} -{% endfor %} + {#{% endif %}#} +{#{% endfor %}#} {% endblock %} {% block footer %} diff --git a/FlaskWebProject/FlaskWebProject/templates/representation.html b/FlaskWebProject/FlaskWebProject/templates/representation.html index 7ba64d0..36ebe12 100644 --- a/FlaskWebProject/FlaskWebProject/templates/representation.html +++ b/FlaskWebProject/FlaskWebProject/templates/representation.html @@ -11,7 +11,7 @@

Gole:

{{ nation_stats.goals }}
-

Asysty:

{{ nation_stats.assists }} +

Asysty:

{{ nation_stats.assist }}

Występy:

{{ nation_stats.matches }} diff --git a/FlaskWebProject/FlaskWebProject/templates/stats.html b/FlaskWebProject/FlaskWebProject/templates/stats.html index eb263cf..0c344a9 100644 --- a/FlaskWebProject/FlaskWebProject/templates/stats.html +++ b/FlaskWebProject/FlaskWebProject/templates/stats.html @@ -56,7 +56,7 @@
-

Puchary międzynarodowe

+

(!) Puchary międzynarodowe

{{ international_cups.goals }}

@@ -73,7 +73,7 @@
-

Puchary krajowe

+

(!) Puchary krajowe

{{ national_cups.goals }}

@@ -103,4 +103,21 @@
+
+

Wygrane, remisy i przegrane

+
+
+

{{ wins_losses.wins }}

+

Wygrane

+
+
+

{{ wins_losses.draws }}

+

Remisy

+
+
+

{{ wins_losses.losses }}

+

Przegrane

+
+
+
{% endblock %} \ No newline at end of file