Created statistic cache

This commit is contained in:
Yarne Coppens 2024-08-25 10:49:54 +02:00
parent 0b412b8951
commit 93c5e65af5
2 changed files with 157 additions and 32 deletions

View file

@ -1,4 +1,4 @@
from typing import Union from typing import Union, Dict
from pydantic import BaseModel from pydantic import BaseModel
from sqlmodel import Session from sqlmodel import Session
from threading import Thread from threading import Thread
@ -25,6 +25,7 @@ def refresh_data():
data_connection.delete_database() data_connection.delete_database()
data_connection.create_db_and_tables() data_connection.create_db_and_tables()
with Session(data_connection.get_db_engine()) as session: with Session(data_connection.get_db_engine()) as session:
statistic_creator.clear_cache()
data_connection.get_user_collection(session) data_connection.get_user_collection(session)
data_connection.get_user_owned_collection(session) data_connection.get_user_owned_collection(session)
data_connection.get_user_wishlist_collection(session) data_connection.get_user_wishlist_collection(session)
@ -219,8 +220,8 @@ def get_winrate(player_name: str | None = None, session: Session = Depends(get_s
return statistic_to_return return statistic_to_return
@app.get('/statistics/winrate_over_time', response_model=statistic_classes.TimeLineStatistic) @app.get('/statistics/winrate_over_time', response_model=Union[statistic_classes.TimeLineStatistic, Dict[str,statistic_classes.TimeLineStatistic]])
def get_winrate_over_time(player_name: str, day_step: int = 1, session: Session=Depends(get_session)): def get_winrate_over_time(player_name: str | None = None, day_step: int = 1, session: Session=Depends(get_session)):
statistic_to_return = statistic_creator.get_winrate_over_time(session, player_name, day_step) statistic_to_return = statistic_creator.get_winrate_over_time(session, player_name, day_step)
return statistic_to_return return statistic_to_return

View file

@ -1,14 +1,44 @@
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING, Union, Dict
if TYPE_CHECKING: if TYPE_CHECKING:
from src.main import BoardgameFilterParams, PlayFilterParams from src.main import BoardgameFilterParams, PlayFilterParams
import hashlib
from src.classes import statistic_classes from src.classes import statistic_classes
from src.modules import data_connection from src.modules import data_connection
from datetime import date, timedelta, datetime from datetime import date, timedelta, datetime
from sqlmodel import Session from sqlmodel import Session
cached_statistics = {}
def clear_cache():
global cached_statistics
cached_statistics = {}
def get_from_cache(argument_dict: dict):
#Session is random for each request
if 'session' in argument_dict:
del argument_dict['session']
md5hash = hashlib.md5(str(argument_dict).encode('utf-8')).hexdigest()
if md5hash in cached_statistics:
return md5hash, cached_statistics[md5hash]
else:
return md5hash, None
def get_total_owned_games(session: Session, filtering_query: BoardgameFilterParams = None) -> statistic_classes.NumberStatistic: def get_total_owned_games(session: Session, filtering_query: BoardgameFilterParams = None) -> statistic_classes.NumberStatistic:
statistic_name = 'Amount of games in owned collection'
md5hash, cached_statistic = get_from_cache({**locals()})
if cached_statistic != None:
return cached_statistic
owned_collection = data_connection.get_user_owned_collection(session) owned_collection = data_connection.get_user_owned_collection(session)
if filtering_query != None: if filtering_query != None:
@ -17,16 +47,27 @@ def get_total_owned_games(session: Session, filtering_query: BoardgameFilterPara
total_owned_games = len(owned_collection) total_owned_games = len(owned_collection)
statistic_dict = { statistic_dict = {
"name":"Amount of games in owned collection", "name":statistic_name,
"result":total_owned_games "result":total_owned_games
} }
statistic_to_return = statistic_classes.NumberStatistic.model_validate(statistic_dict) statistic_to_return = statistic_classes.NumberStatistic.model_validate(statistic_dict)
cached_statistics[md5hash] = statistic_to_return
return statistic_to_return return statistic_to_return
def get_total_owned_collection_cost(session: Session, filtering_query: BoardgameFilterParams = None) -> statistic_classes.NumberStatistic: def get_total_owned_collection_cost(session: Session, filtering_query: BoardgameFilterParams = None) -> statistic_classes.NumberStatistic:
statistic_name = 'Total cost of the owned collection'
md5hash, cached_statistic = get_from_cache({**locals()})
if cached_statistic != None:
return cached_statistic
owned_collection = data_connection.get_user_owned_collection(session) owned_collection = data_connection.get_user_owned_collection(session)
if filtering_query != None: if filtering_query != None:
@ -36,16 +77,27 @@ def get_total_owned_collection_cost(session: Session, filtering_query: Boardgame
total_cost = sum([boardgame.owned_info.price_paid for boardgame in owned_collection]) total_cost = sum([boardgame.owned_info.price_paid for boardgame in owned_collection])
statistic_dict = { statistic_dict = {
"name":"Total cost of the owned collection", "name":statistic_name,
"result":total_cost "result":total_cost
} }
statistic_to_return = statistic_classes.NumberStatistic.model_validate(statistic_dict) statistic_to_return = statistic_classes.NumberStatistic.model_validate(statistic_dict)
cached_statistics[md5hash] = statistic_to_return
return statistic_to_return return statistic_to_return
def get_amount_of_games_over_time(session: Session, filtering_query: BoardgameFilterParams = None, day_step: int = 1) -> statistic_classes.TimeLineStatistic: def get_amount_of_games_over_time(session: Session, filtering_query: BoardgameFilterParams = None, day_step: int = 1) -> statistic_classes.TimeLineStatistic:
statistic_name = 'Amount of games in owned collection over time'
md5hash, cached_statistic = get_from_cache({**locals()})
if cached_statistic != None:
return cached_statistic
def daterange(start_date: date, end_date: date, day_step): def daterange(start_date: date, end_date: date, day_step):
days = int((end_date - start_date).days) days = int((end_date - start_date).days)
for n in range(0, days, day_step): for n in range(0, days, day_step):
@ -65,16 +117,27 @@ def get_amount_of_games_over_time(session: Session, filtering_query: BoardgameFi
timeline_dict[current_date] = len(games_in_collection_at_date) timeline_dict[current_date] = len(games_in_collection_at_date)
statistic_dict = { statistic_dict = {
"name":"Amount of games in owned collection over time", "name":statistic_name,
"result":timeline_dict "result":timeline_dict
} }
statistic_to_return = statistic_classes.TimeLineStatistic.model_validate(statistic_dict) statistic_to_return = statistic_classes.TimeLineStatistic.model_validate(statistic_dict)
cached_statistics[md5hash] = statistic_to_return
return statistic_to_return return statistic_to_return
def get_amount_of_games_played_per_year(session: Session, filtering_query: PlayFilterParams = None) -> statistic_classes.TimeLineStatistic: def get_amount_of_games_played_per_year(session: Session, filtering_query: PlayFilterParams = None) -> statistic_classes.TimeLineStatistic:
statistic_name = 'Amount of games played per year'
md5hash, cached_statistic = get_from_cache({**locals()})
if cached_statistic != None:
return cached_statistic
all_plays = data_connection.get_plays(session) all_plays = data_connection.get_plays(session)
all_plays.sort(key= lambda x: x.play_date) all_plays.sort(key= lambda x: x.play_date)
@ -99,15 +162,26 @@ def get_amount_of_games_played_per_year(session: Session, filtering_query: PlayF
statistic_dict = { statistic_dict = {
"name":"Amount of games played per year", "name":statistic_name,
"result":years_plays_dict "result":years_plays_dict
} }
statistic_to_return = statistic_classes.TimeLineStatistic.model_validate(statistic_dict) statistic_to_return = statistic_classes.TimeLineStatistic.model_validate(statistic_dict)
cached_statistics[md5hash] = statistic_to_return
return statistic_to_return return statistic_to_return
def get_most_expensive_games(session: Session, filtering_query: BoardgameFilterParams = None, top_amount: int = 10) -> statistic_classes.GamesStatistic: def get_most_expensive_games(session: Session, filtering_query: BoardgameFilterParams = None, top_amount: int = 10) -> statistic_classes.GamesStatistic:
statistic_name = 'Most expensive games'
md5hash, cached_statistic = get_from_cache({**locals()})
if cached_statistic != None:
return cached_statistic
most_expensive_games = data_connection.get_user_owned_collection(session) most_expensive_games = data_connection.get_user_owned_collection(session)
most_expensive_games = filtering_query.do_filtering(most_expensive_games) most_expensive_games = filtering_query.do_filtering(most_expensive_games)
@ -117,16 +191,27 @@ def get_most_expensive_games(session: Session, filtering_query: BoardgameFilterP
most_expensive_games = most_expensive_games[0:top_amount] most_expensive_games = most_expensive_games[0:top_amount]
statistic_dict = { statistic_dict = {
"name":"Most expensive games", "name":statistic_name,
"result":most_expensive_games "result":most_expensive_games
} }
statistic_to_return = statistic_classes.GamesStatistic.model_validate(statistic_dict) statistic_to_return = statistic_classes.GamesStatistic.model_validate(statistic_dict)
cached_statistics[md5hash] = statistic_to_return
return statistic_to_return return statistic_to_return
def get_shelf_of_shame(session: Session, filtering_query: BoardgameFilterParams = None) -> statistic_classes.GamesStatistic: def get_shelf_of_shame(session: Session, filtering_query: BoardgameFilterParams = None) -> statistic_classes.GamesStatistic:
statistic_name = "Shelf of Shame"
md5hash, cached_statistic = get_from_cache({**locals()})
if cached_statistic != None:
return cached_statistic
boardgames_in_collection = data_connection.get_user_collection(session) boardgames_in_collection = data_connection.get_user_collection(session)
owned_boardgames = data_connection.get_user_owned_collection(session) owned_boardgames = data_connection.get_user_owned_collection(session)
@ -145,24 +230,33 @@ def get_shelf_of_shame(session: Session, filtering_query: BoardgameFilterParams
owned_boardgames_no_plays.sort(key=lambda x: x.name) owned_boardgames_no_plays.sort(key=lambda x: x.name)
statistic_dict = { statistic_dict = {
"name":"Shelf of Shame", "name":statistic_name,
"result":owned_boardgames_no_plays "result":owned_boardgames_no_plays
} }
statistic_to_return = statistic_classes.GamesStatistic.model_validate(statistic_dict) statistic_to_return = statistic_classes.GamesStatistic.model_validate(statistic_dict)
cached_statistics[md5hash] = statistic_to_return
return statistic_to_return return statistic_to_return
def get_winrate(session: Session, player_name: str | None = None): def get_winrate(session: Session, player_name: str | None = None):
statistic_name = 'Player winrate'
md5hash, cached_statistic = get_from_cache({**locals()})
if cached_statistic != None:
return cached_statistic
if player_name == None: if player_name == None:
players_to_calculate = data_connection.get_all_players(session) players_to_calculate = data_connection.get_all_players(session)
else: else:
players_to_calculate = [data_connection.get_player(player_name.title(), session)] players_to_calculate = [data_connection.get_player(player_name.title(), session)]
statistic_dict = { statistic_dict = {
'name': 'Player winrate', 'name': statistic_name,
'result': {} 'result': {}
} }
@ -177,43 +271,73 @@ def get_winrate(session: Session, player_name: str | None = None):
statistic_to_return = statistic_classes.PlayerStatistic.model_validate(statistic_dict) statistic_to_return = statistic_classes.PlayerStatistic.model_validate(statistic_dict)
cached_statistics[md5hash] = statistic_to_return
return statistic_to_return return statistic_to_return
def get_winrate_over_time(session: Session, player_name: str, day_step = 1): def get_winrate_over_time(session: Session, player_name: str | None = None, day_step = 1) -> Dict[str,statistic_classes.TimeLineStatistic]:
statistic_name = 'Player winrate over time'
md5hash, cached_statistic = get_from_cache({**locals()})
if cached_statistic != None:
return cached_statistic
def daterange(start_date: date, end_date: date, day_step): def daterange(start_date: date, end_date: date, day_step):
days = int((end_date - start_date).days) days = int((end_date - start_date).days)
for n in range(0, days, day_step): for n in range(0, days, day_step):
yield start_date + timedelta(n) yield start_date + timedelta(n)
if player_name == None:
wanted_players = data_connection.get_all_players(session)
else:
wanted_players = [data_connection.get_player(player_name, session)]
wanted_player = data_connection.get_player(player_name, session) dict_to_return = {}
all_playplayers = [playplayer for playplayer in wanted_player.playplayers]
all_playplayers.sort(key=lambda x: x.play.play_date)
start_date = all_playplayers[0].play.play_date start_date = datetime.today().date()
timeline_dict = {} for wanted_player in wanted_players:
for current_date in daterange(start_date, date.today(), day_step): all_playplayers = [playplayer for playplayer in wanted_player.playplayers]
playplayers_at_date = list(filter(lambda playplayer: playplayer.play.play_date <= current_date, all_playplayers)) all_playplayers.sort(key=lambda x: x.play.play_date)
total_games_played = len(playplayers_at_date)
total_games_won = 0 if start_date > all_playplayers[0].play.play_date:
for playplayer in playplayers_at_date: start_date = all_playplayers[0].play.play_date
if playplayer.has_won:
total_games_won += 1 for wanted_player in wanted_players:
all_playplayers = [playplayer for playplayer in wanted_player.playplayers]
timeline_dict[current_date] = total_games_won / total_games_played all_playplayers.sort(key=lambda x: x.play.play_date)
timeline_dict = {}
for current_date in daterange(start_date, date.today(), day_step):
playplayers_at_date = list(filter(lambda playplayer: playplayer.play.play_date <= current_date, all_playplayers))
total_games_played = len(playplayers_at_date)
total_games_won = 0
for playplayer in playplayers_at_date:
if playplayer.has_won:
total_games_won += 1
if total_games_played != 0:
timeline_dict[current_date] = total_games_won / total_games_played
else:
timeline_dict[current_date] = 0
statistic_dict = { statistic_dict = {
'name': 'Player winrate over time', 'name': statistic_name,
'result': timeline_dict 'result': timeline_dict
} }
statistic_to_return = statistic_classes.TimeLineStatistic.model_validate(statistic_dict) statistic_to_return = statistic_classes.TimeLineStatistic.model_validate(statistic_dict)
return statistic_to_return dict_to_return[wanted_player.name] = statistic_to_return
cached_statistics[md5hash] = dict_to_return
return dict_to_return