From 6756012f5376a86c8850b1131e407a23a73d0c95 Mon Sep 17 00:00:00 2001 From: John Gatward Date: Tue, 28 Jul 2026 11:19:25 +0100 Subject: [PATCH] [main]: add 27/07 --- data.csv | 58 +++++++++++++++++++++++---------------------- src/app.py | 9 ++++--- src/constants.py | 44 +++++++++++++--------------------- src/player_table.py | 5 ++-- src/stats.py | 2 +- 5 files changed, 56 insertions(+), 62 deletions(-) diff --git a/data.csv b/data.csv index 779503e..8196452 100644 --- a/data.csv +++ b/data.csv @@ -1,28 +1,30 @@ -Date,Absolute Position,Relative Position,Number of Players,Number of Teams,Points on Scattergories,Ciaran,Jay,Sam,Drew,Theo,Tom,Ellora,Chloe,Jamie,Christine,Mide,Ellie -17/03/2025,9,0.692,2,13,10,1,1,0,0,0,0,0,0,0,0,0,0 -24/03/2025,14,0.933,2,15,4,1,1,0,0,0,0,0,0,0,0,0,0 -31/03/2025,8,0.444,4,18,7,1,1,1,1,0,0,0,0,0,0,0,0 -07/04/2025,9,0.563,2,16,6,1,1,0,0,0,0,0,0,0,0,0,0 -28/04/2025,9,0.529,3,17,5,1,1,1,0,0,0,0,0,0,0,0,0 -26/05/2025,6,0.5,2,12,8,0,1,0,0,1,0,0,0,0,0,0,0 -02/06/2025,12,0.857,3,14,4,1,1,1,0,0,0,0,0,0,0,0,0 -16/06/2025,6,0.545,3,11,6,1,1,1,0,0,0,0,0,0,0,0,0 -30/06/2025,5,0.833,4,6,8,1,1,1,0,0,1,0,0,0,0,0,0 -14/07/2025,5,0.625,3,8,8,1,1,0,0,0,0,1,0,0,0,0,0 -21/07/2025,4,0.5,2,8,9,1,1,0,0,0,0,0,0,0,0,0,0 -28/07/2025,6,0.375,4,16,4,1,1,1,0,0,0,1,0,0,0,0,0 -04/08/2025,4,0.4,3,10,11,1,1,0,0,0,0,1,0,0,0,0,0 -15/09/2025,4,0.333,4,12,5,1,1,1,0,0,0,1,0,0,0,0,0 -22/09/2025,5,0.417,3,12,5,1,0,1,0,0,1,0,0,0,0,0,0 -29/09/2025,8,0.727,2,11,5,0,1,0,1,0,0,0,0,0,0,0,0 -24/11/2025,8,0.889,3,9,6,1,1,0,0,0,0,1,0,0,0,0,0 -05/01/2026,4,0.5,4,8,6,1,1,0,0,0,0,0,1,1,0,0,0 -26/01/2026,7,0.583,2,12,10,1,1,0,0,0,0,0,0,0,0,0,0 -02/02/2026,7,0.7,2,10,5,1,1,0,0,0,0,0,0,0,0,0,0 -16/02/2026,9,0.5,3,18,6,0,1,0,1,0,0,0,0,0,1,0,0 -23/02/2026,6,0.6,2,10,10,1,1,0,0,0,0,0,0,0,0,0,0 -09/03/2026,6,1,5,6,10,1,1,1,0,0,0,0,1,0,0,1,0 -30/03/2026,2,0.133,4,15,12,1,1,1,1,0,0,0,0,0,0,0,0 -27/04/2026,2,0.222,2,9,7,1,1,0,0,0,0,0,0,0,0,0,0 -11/05/2026,7,0.777,4,9,6,1,1,0,0,0,0,0,1,0,0,0,1 -18/05/2026,8,0.666,3,12,6,1,1,1,0,0,0,0,0,0,0,0,0 +Date,Absolute Position,Number of Players,Number of Teams,Points on Scattergories,Ciaran,Jay,Sam,Drew,Theo,Tom,Ellora,Chloe,Jamie,Christine,Mide,Ellie,Elliot,Kira +17/03/2025,9,2,13,10,1,1,0,0,0,0,0,0,0,0,0,0,0,0 +24/03/2025,14,2,15,4,1,1,0,0,0,0,0,0,0,0,0,0,0,0 +31/03/2025,8,4,18,7,1,1,1,1,0,0,0,0,0,0,0,0,0,0 +07/04/2025,9,2,16,6,1,1,0,0,0,0,0,0,0,0,0,0,0,0 +28/04/2025,9,3,17,5,1,1,1,0,0,0,0,0,0,0,0,0,0,0 +26/05/2025,6,2,12,8,0,1,0,0,1,0,0,0,0,0,0,0,0,0 +02/06/2025,12,3,14,4,1,1,1,0,0,0,0,0,0,0,0,0,0,0 +16/06/2025,6,3,11,6,1,1,1,0,0,0,0,0,0,0,0,0,0,0 +30/06/2025,5,4,6,8,1,1,1,0,0,1,0,0,0,0,0,0,0,0 +14/07/2025,5,3,8,8,1,1,0,0,0,0,1,0,0,0,0,0,0,0 +21/07/2025,4,2,8,9,1,1,0,0,0,0,0,0,0,0,0,0,0,0 +28/07/2025,6,4,16,4,1,1,1,0,0,0,1,0,0,0,0,0,0,0 +04/08/2025,4,3,10,11,1,1,0,0,0,0,1,0,0,0,0,0,0,0 +15/09/2025,4,4,12,5,1,1,1,0,0,0,1,0,0,0,0,0,0,0 +22/09/2025,5,3,12,5,1,0,1,0,0,1,0,0,0,0,0,0,0,0 +29/09/2025,8,2,11,5,0,1,0,1,0,0,0,0,0,0,0,0,0,0 +24/11/2025,8,3,9,6,1,1,0,0,0,0,1,0,0,0,0,0,0,0 +05/01/2026,4,4,8,6,1,1,0,0,0,0,0,1,1,0,0,0,0,0 +26/01/2026,7,2,12,10,1,1,0,0,0,0,0,0,0,0,0,0,0,0 +02/02/2026,7,2,10,5,1,1,0,0,0,0,0,0,0,0,0,0,0,0 +16/02/2026,9,3,18,6,0,1,0,1,0,0,0,0,0,1,0,0,0,0 +23/02/2026,6,2,10,10,1,1,0,0,0,0,0,0,0,0,0,0,0,0 +09/03/2026,6,5,6,10,1,1,1,0,0,0,0,1,0,0,1,0,0,0 +30/03/2026,2,4,15,12,1,1,1,1,0,0,0,0,0,0,0,0,0,0 +27/04/2026,2,2,9,7,1,1,0,0,0,0,0,0,0,0,0,0,0,0 +11/05/2026,7,4,9,6,1,1,0,0,0,0,0,1,0,0,0,1,0,0 +18/05/2026,8,3,12,6,1,1,1,0,0,0,0,0,0,0,0,0,0,0 +08/06/2026,8,5,11,4,1,1,1,0,0,0,0,0,0,0,0,0,1,1 +27/07/2026,12,3,13,5,1,1,1,0,0,0,0,0,0,0,0,0,0,0 diff --git a/src/app.py b/src/app.py index 7af6772..4703a1d 100644 --- a/src/app.py +++ b/src/app.py @@ -22,6 +22,9 @@ app = Flask(__name__) def get_data_frame(filename): df = pd.read_csv(filename) df["Date"] = pd.to_datetime(df["Date"], dayfirst=True) + # Relative Position is derived, not entered manually, so it can never + # drift out of sync with Absolute Position / Number of Teams. + df["Relative Position"] = df["Absolute Position"] / df["Number of Teams"] df = df.sort_values("Date").reset_index(drop=True) return df @@ -50,7 +53,7 @@ def generate_position_trend(df): df["Relative Percentile"] = df["Relative Position"] * 100 df["Rolling Avg (5)"] = df["Relative Percentile"].rolling( 5, min_periods=1).mean() - df["Attendees"] = build_hovertext(df, constants.PLAYER_NAME_COLUMNS) + df["Attendees"] = build_hovertext(df, constants.get_player_columns(df)) X = sm.add_constant(df["Date_ordinal"]) model = sm.OLS(df["Relative Percentile"], X).fit() @@ -146,7 +149,7 @@ def generate_player_impact(df): overall_percentile = df["Relative Position"].mean() * 100 rows = [] - for name in constants.PLAYER_NAME_COLUMNS: + for name in constants.get_player_columns(df): if name not in df.columns: continue attended = df[df[name] == 1] @@ -245,7 +248,7 @@ def generate_scattergories_chart(df): def generate_player_participation(df): """Heatmap of which player attended which game.""" - player_cols = [c for c in constants.PLAYER_NAME_COLUMNS if c in df.columns] + player_cols = [c for c in constants.get_player_columns(df) if c in df.columns] df_players = df[player_cols] fig = px.imshow( df_players.T, diff --git a/src/constants.py b/src/constants.py index 61a2e73..83f9928 100644 --- a/src/constants.py +++ b/src/constants.py @@ -8,39 +8,27 @@ def ordinal(n): return f"{n}{suffix}" -PLAYER_NAME_COLUMNS = [ - "Ciaran", - "Jay", - "Sam", - "Drew", - "Theo", - "Tom", - "Ellora", - "Chloe", - "Jamie", - "Christine", - "Mide", - "Ellie", -] - -FEATURE_COLUMNS = { +# Columns that describe the quiz night itself rather than an individual +# player's attendance. Any dataframe column not in this set is treated as a +# player name, so new players are picked up automatically from data.csv +# without needing a code change. +NON_PLAYER_COLUMNS = { + "Date", + "Absolute Position", + "Relative Position", "Number of Players", "Number of Teams", "Points on Scattergories", - "Ciaran", - "Jay", - "Sam", - "Drew", - "Theo", - "Tom", - "Ellora", - "Chloe", - "Jamie", - "Christine", - "Mide", - "Ellie", } + +def get_player_columns(df): + """Return the list of player-name columns present in df, in CSV order.""" + return [c for c in df.columns if c not in NON_PLAYER_COLUMNS] + + +FEATURE_COLUMNS = NON_PLAYER_COLUMNS - {"Date", "Relative Position"} + ATTENDANCE_COLORSCHEME = [ [0, "#E4ECF6"], [1, "#1e3a8a"] ] diff --git a/src/player_table.py b/src/player_table.py index 8a7460f..d15b507 100644 --- a/src/player_table.py +++ b/src/player_table.py @@ -1,11 +1,12 @@ -from constants import PLAYER_NAME_COLUMNS, ordinal +import constants +from constants import ordinal def generate_player_table(df): header = [["Player", "Appearances", "Avg. Relative Percentile", "Spent"]] player_stats = [] - for name in PLAYER_NAME_COLUMNS: + for name in constants.get_player_columns(df): if name in df.columns: attended = df[df[name] == 1] n = len(attended) diff --git a/src/stats.py b/src/stats.py index a6dc8bf..60cde27 100644 --- a/src/stats.py +++ b/src/stats.py @@ -14,7 +14,7 @@ def _max_team_streak(dates): def _max_player_streak(df): - names = [col for col in df.columns if col in set(constants.PLAYER_NAME_COLUMNS)] + names = constants.get_player_columns(df) max_streak, max_name = 1, names[0] for name in names: local_max = current = 0