8 Commits

Author SHA1 Message Date
8fb2a22c2a another one 2025-06-13 00:32:13 +02:00
f96a3ed1f1 fix 2025-06-12 23:42:55 +02:00
00bbe05b1c feat: include club's full name in matches view 2025-06-12 22:35:04 +02:00
e373c6b274 hotfix: fixes /mecze endpoint not working (cause of a wrong API call) 2025-06-12 20:43:22 +02:00
e80ee6bf8f feat: return matches with respect to received sportsman id
also include a comment in the database model to include a relation
2025-06-12 20:36:10 +02:00
a25be482b0 matches from DB 2025-06-12 16:23:14 +02:00
4e8cb48c82 fix: safe_traverse renaming issue 2025-06-11 00:38:55 +02:00
bc82fcd6b8 chore: depend on safe_traverse from lewy_globals, add scraper cron job 2025-06-11 00:35:48 +02:00
9 changed files with 118 additions and 35 deletions

View File

@@ -1,23 +1,13 @@
from flask import session from flask import session
from lewy_db import baza as ldb from lewy_db import baza as ldb
from lewy_globals import colors as c from lewy_globals import colors as c
from lewy_globals import safeTraverse as safe_traverse
import json import json
import lewy_globals import lewy_globals
import requests import requests
import time import time
from sqlalchemy import func from sqlalchemy import func
def safe_traverse(obj: dict, path: list, default=None):
result = obj
try:
for x in path:
result = result[x]
except (KeyError, TypeError):
result = default
print(f"safe_traverse: error reading {' -> '.join(path)} - returning: {default}")
finally:
return result
class scraper: class scraper:
headers = { headers = {

View File

@@ -1,7 +1,7 @@
from argparse import ArgumentParser from argparse import ArgumentParser
from flask import Flask, Response, render_template from flask import Flask, Response, render_template
from flask_apscheduler import APScheduler from flask_apscheduler import APScheduler
from fs_scraper import scraper as scr from fs_scraper import scraper
from lewy_globals import colors as c from lewy_globals import colors as c
import lewy_api import lewy_api
import lewy_db import lewy_db
@@ -13,7 +13,7 @@ import time
app = Flask(__name__) app = Flask(__name__)
app_host = "None" app_host = "None"
app_port = "None" app_port = "None"
scrape = None scr = None
def setup(): def setup():
# sanity check: make sure config is set # sanity check: make sure config is set
@@ -79,7 +79,7 @@ def setup():
app.add_url_rule('/api/<path:received_request>', view_func=lewy_api.api_global_catchall) app.add_url_rule('/api/<path:received_request>', view_func=lewy_api.api_global_catchall)
db = lewy_globals.setupDb(app, config) db = lewy_globals.setupDb(app, config)
scraper = scr() scr = scraper()
with app.app_context(): with app.app_context():
db.create_all() db.create_all()
@@ -97,8 +97,7 @@ def every5seconds():
def every2hours(): def every2hours():
# zaktualizuj bazę danych scrapując FS # zaktualizuj bazę danych scrapując FS
# ... scr.aktualizuj_dane()
# scraper.aktualizuj_dane()
return return
@app.route('/<string:val>', methods=['GET']) @app.route('/<string:val>', methods=['GET'])

View File

@@ -50,6 +50,16 @@ def not_implemented(data):
# TODO: change list to string -> data, not data[0] # TODO: change list to string -> data, not data[0]
return 501, f"not recognised/implemented: {data[0]}", [] return 501, f"not recognised/implemented: {data[0]}", []
def __czy_x_istnieje(typ, **id):
rekord = getDb().simple_select_all(typ, **id)
if rekord is not None and rekord != []:
return True
else:
return False
def czy_sportowiec_istnieje(id_zawodnika: str):
return __czy_x_istnieje("sportowcy", id_zawodnika=id_zawodnika)
# GET /api/v1 # GET /api/v1
def stub_hello(): def stub_hello():
""" """
@@ -85,15 +95,36 @@ def stats():
return 200, "ok", data_to_send return 200, "ok", data_to_send
# GET /api/v1/matches # GET /api/v1/matches
def get_matches(r): def get_matches(r = None, id_zawodnika: str | None = None):
""" """
TODO: Zwraca mecze. Zwraca mecze.
Przykład wywołania:
get_matches(r, id_zawodnika=1), tożsame z GET /api/v1/matches?id_zawodnika=1
get_matches(r), tożsame z GET /api/v1/matches
""" """
response_json = [] response_json = []
mecze = getDb().simple_select_all("mecze") mecze = None
if id_zawodnika is None:
# Gdy nie podano id wprost, sprawdź, czy podano je przez parametr.
id_zawodnika = r.args.get('id_zawodnika', -1)
# Sprawdź, czy podano jakiekolwiek ID sportowca. Jeżeli nie, wypisz wszystkie mecze.
if id_zawodnika == -1:
mecze = getDb().simple_select_all("mecze")
# Sprawdź, czy sportowiec o podanym (lub niepodanym) id istnieje.
# Jeśli nie istnieje, wypisz wszystkie mecze.
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 mecze.
else:
mecze = getDb().get_sportsman_matches(id_zawodnika=id_zawodnika)
for mecz in mecze: for mecz in mecze:
response_json.append(mecz.jsonify()) response_json.append(mecz.jsonify())
print(response_json) # print(f"zwracam mecze: {response_json}")
return 200, "ok", response_json return 200, "ok", response_json
# GET /api/v1/debugger_halt?token=XXX... # GET /api/v1/debugger_halt?token=XXX...

View File

@@ -95,7 +95,7 @@ class baza():
id_zawodnika: Mapped[ int] = mapped_column(ForeignKey(f"{tnp}sportowcy.id_zawodnika")) id_zawodnika: Mapped[ int] = mapped_column(ForeignKey(f"{tnp}sportowcy.id_zawodnika"))
zawodnik: Mapped[ "sportowcy"] = relationship(back_populates="mecze_zawodnika") zawodnik: Mapped[ "sportowcy"] = relationship(back_populates="mecze_zawodnika")
zewnetrzne_id_meczu: Mapped[ str] = mapped_column(ForeignKey(f"{tnp}mecze.zewnetrzne_id_meczu")) zewnetrzne_id_meczu: Mapped[ str] = mapped_column(ForeignKey(f"{tnp}mecze.zewnetrzne_id_meczu"))
mecz: Mapped[ "mecze"] = relationship() mecz: Mapped[ "mecze"] = relationship() # back_populates="zawodnicy_w_meczu", foreign_keys=[zewnetrzne_id_meczu])
czas_gry: Mapped[ int] = mapped_column() czas_gry: Mapped[ int] = mapped_column()
goli: Mapped[ int] = mapped_column() goli: Mapped[ int] = mapped_column()
asyst: Mapped[ int] = mapped_column() asyst: Mapped[ int] = mapped_column()
@@ -159,6 +159,8 @@ class baza():
skrocona_nazwa_turnieju: Mapped[ str] = mapped_column() skrocona_nazwa_turnieju: Mapped[ str] = mapped_column()
flaga: Mapped[ int] = mapped_column() flaga: Mapped[ int] = mapped_column()
# zawodnicy_w_meczu: Mapped[ List["sportowcy_w_meczach"]] = relationship(back_populates="mecz")
def __repr__(self): def __repr__(self):
return f"<Mecz #{self.id_meczu} ({self.zewnetrzne_id_meczu}, {self.gospodarze.skrocona_nazwa} vs. {self.goscie.skrocona_nazwa})>" return f"<Mecz #{self.id_meczu} ({self.zewnetrzne_id_meczu}, {self.gospodarze.skrocona_nazwa} vs. {self.goscie.skrocona_nazwa})>"
@@ -169,8 +171,10 @@ class baza():
"data": self.data.strftime("%Y-%m-%d"), "data": self.data.strftime("%Y-%m-%d"),
"gospodarze_id": self.gospodarze_id, "gospodarze_id": self.gospodarze_id,
"gospodarze": self.gospodarze.skrocona_nazwa, "gospodarze": self.gospodarze.skrocona_nazwa,
"gospodarze_pelna_nazwa": self.gospodarze.pelna_nazwa,
"goscie_id": self.goscie_id, "goscie_id": self.goscie_id,
"goscie": self.goscie.skrocona_nazwa, "goscie": self.goscie.skrocona_nazwa,
"goscie_pelna_nazwa": self.goscie.pelna_nazwa,
"gosp_wynik": self.gosp_wynik, "gosp_wynik": self.gosp_wynik,
"gosc_wynik": self.gosc_wynik, "gosc_wynik": self.gosc_wynik,
"sezon": self.sezon, "sezon": self.sezon,
@@ -494,18 +498,49 @@ class baza():
if zewnetrzne_id_zawodnika is not None: if zewnetrzne_id_zawodnika is not None:
id_zawodnika = self.get_id_zawodnika_by_zewnetrzne_id(zewnetrzne_id_zawodnika) id_zawodnika = self.get_id_zawodnika_by_zewnetrzne_id(zewnetrzne_id_zawodnika)
# Aliasy (dla czytelności)
SportowcyWMeczach = self.entities["sportowcy_w_meczach"]
Sportowcy = self.entities["sportowcy"]
query = self.session.query( query = self.session.query(
self.entities["sportowcy_w_meczach"] SportowcyWMeczach
).join( ).join(
self.entities["sportowcy_w_meczach"].zawodnik SportowcyWMeczach.zawodnik
).filter( ).filter(
self.entities["sportowcy"].id_zawodnika == id_zawodnika Sportowcy.id_zawodnika == id_zawodnika
) )
#print(query) # print(f"get_sportowcy_w_meczach_by_sportsman_id: {query}")
if order.lower() == "desc": if order.lower() == "desc":
query = query.order_by(self.entities["sportowcy_w_meczach"].id_rekordu.desc()) query = query.order_by(SportowcyWMeczach.id_rekordu.desc())
return query.all()
@exit_gracefully
def get_sportsman_matches(self, id_zawodnika = None, zewnetrzne_id_zawodnika = None, order = "DESC"):
if zewnetrzne_id_zawodnika is not None:
id_zawodnika = self.get_id_zawodnika_by_zewnetrzne_id(zewnetrzne_id_zawodnika)
# Aliasy
Mecze = self.entities["mecze"]
SportowcyWMeczach = self.entities["sportowcy_w_meczach"]
Sportowcy = self.entities["sportowcy"]
query = self.session.query(
Mecze
).join(
SportowcyWMeczach, Mecze.zewnetrzne_id_meczu == SportowcyWMeczach.zewnetrzne_id_meczu
).join(
Sportowcy, SportowcyWMeczach.id_zawodnika == Sportowcy.id_zawodnika
).filter(
Sportowcy.id_zawodnika == id_zawodnika
)
# print(f"get_sportsman_matches: {query}")
if order.lower() == "desc":
query = query.order_by(Mecze.data.desc())
return query.all() return query.all()

View File

@@ -23,9 +23,9 @@ def safeTraverse(obj: dict, path: list, default=None):
try: try:
for x in path: for x in path:
result = result[x] result = result[x]
except KeyError: except (KeyError, TypeError):
result = default result = default
# print(f"error reading: {' -> '.join(path)} - returning: {default}") print(f"error reading: {' -> '.join(path)} - returning: {default}")
finally: finally:
return result return result

View File

@@ -3,7 +3,7 @@ import lewy_api_v1
import lewy_db import lewy_db
import lewy_globals import lewy_globals
import json import json
from lewy_api_v1 import get_matches
def get_lewy_stats(): def get_lewy_stats():
return { return {
'all_time_stats': { 'all_time_stats': {
@@ -60,9 +60,9 @@ def index():
def mecze(): def mecze():
# Możesz dostarczyć szczegóły dotyczące meczów # Możesz dostarczyć szczegóły dotyczące meczów
selected_date = request.args.get("date", '2025') selected_date = request.args.get("date", '2025')
with open("static/lewandowski_matches.json", "r") as file: #with open("static/lewandowski_matches.json", "r") as file:
data = json.load(file) # data = json.load(file)
matches = data["data"] status, msg, matches = get_matches(None, id_zawodnika=1)
return render_template('matches.html', matches=matches, selected_date=selected_date) return render_template('matches.html', matches=matches, selected_date=selected_date)

View File

@@ -878,4 +878,32 @@ select
background-color: var(--barca-blue); background-color: var(--barca-blue);
border-radius: 10px; border-radius: 10px;
border: 2px solid white; border: 2px solid white;
}
@media (max-width: 600px){
.sectionmatches
{
font-size: 10px;
}
.sectionmatches th{
padding: 3px;
}
.club-stats-grid
{
grid-template-columns: 1fr 1fr !important;
}
.club-stats-grid .stat-box
{
width: 100%;
}
.section-stats-center .section-stats .stats
{
display: flex;
flex-direction: column;
align-items: center;
}
.section-stats-center .section-stats .stats .stat-box
{
width: 100%;
padding: 0;
}
} }

View File

@@ -51,7 +51,7 @@
</div> </div>
<div class="stat-box"> <div class="stat-box">
<h3>{{ player.assists}}</h3> <h3>{{ player.matches}}</h3>
</div> </div>
</div> </div>

View File

@@ -40,9 +40,9 @@
<tr> <tr>
<td>{{ match.data }}</td> <td>{{ match.data }}</td>
<td>{{ match.gospodarze_id }}</td> <td>{{ match.gospodarze_pelna_nazwa }}</td>
<td>{{ match.gosp_wynik }} : {{ match.gosc_wynik }}</td> <td>{{ match.gosp_wynik }} : {{ match.gosc_wynik }}</td>
<td>{{ match.goscie_id }}</td> <td>{{ match.goscie_pelna_nazwa }}</td>
<!-- <!--
<td>{{ match.goals }}</td> <td>{{ match.goals }}</td>
<td>{{ match.assists }}</td> <td>{{ match.assists }}</td>