From c4b2e11f686ede0cf853129333350728fbc86543 Mon Sep 17 00:00:00 2001 From: Brad Brown Date: Fri, 3 Jun 2022 13:37:25 -0500 Subject: [PATCH] Add breakout graph and local nas-hosted deploy --- docker.sh | 5 ++- docker_nas.sh | 4 ++ wordlinator/web/__init__.py | 86 ++++++++++++++++++++++++++++++++++++- 3 files changed, 92 insertions(+), 3 deletions(-) create mode 100755 docker_nas.sh diff --git a/docker.sh b/docker.sh index 00ab8d2..6ffe971 100755 --- a/docker.sh +++ b/docker.sh @@ -3,7 +3,8 @@ docker build -t wordlinator:latest . if [ "$1" = "--debug" ]; then - docker run -d --rm -p 8050:8050 -e DEBUG=true -e DB_PORT -e DB_HOST -e DB_PASS --name wordlinator wordlinator:latest + shift + docker run -d --rm -p 8050:8050 -e DEBUG=true -e DB_PORT -e DB_HOST -e DB_PASS "$@" --name wordlinator wordlinator:latest else - docker run -d --rm -p 8050:8050 -e DB_PORT -e DB_HOST -e DB_PASS --name wordlinator wordlinator:latest + docker run -d --rm -p 8050:8050 -e DB_PORT -e DB_HOST -e DB_PASS "$@" --name wordlinator wordlinator:latest fi diff --git a/docker_nas.sh b/docker_nas.sh new file mode 100755 index 0000000..231bdd9 --- /dev/null +++ b/docker_nas.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +DOCKER_CONTEXT=nas docker ps | grep wordlinator && DOCKER_CONTEXT=nas docker stop wordlinator || true +DOCKER_CONTEXT=nas DB_PORT=49420 DB_HOST="localhost" ./docker.sh "$@" --network=host diff --git a/wordlinator/web/__init__.py b/wordlinator/web/__init__.py index 7c17d63..a4c292b 100644 --- a/wordlinator/web/__init__.py +++ b/wordlinator/web/__init__.py @@ -5,10 +5,15 @@ import time import dash import dash.long_callback import diskcache +import plotly.graph_objs import wordlinator.db.pg as db import wordlinator.utils +################### +# Setup Functions # +################### + app = dash.Dash(name="WordleGolf") @@ -31,6 +36,11 @@ def scores_from_db(): return _scores_from_db(get_ttl_hash()) +################# +# Score Helpers # +################# + + def _golf_score(score_list): scores = [s.score for s in score_list] score_count = len(scores) @@ -126,6 +136,10 @@ def get_scores(): ) +################# +# Stats Helpers # +################# + SCORE_NAME_MAP = { 1: "Hole-in-1", 2: "Eagle", @@ -166,8 +180,9 @@ def _get_summary_rows(score_list): return [totals, averages] -def get_daily_stats(): +def _stats_dict(): score_list = scores_from_db() + scores_by_value = collections.defaultdict(list) for score in score_list: scores_by_value[score.score].append(score.hole_id.hole) @@ -177,6 +192,11 @@ def get_daily_stats(): table_rows.append(_get_score_breakdown(score, scores_by_value[score])) table_rows.extend(_get_summary_rows(score_list)) + return table_rows + + +def get_daily_stats(): + table_rows = _stats_dict() columns = [ {"name": n, "id": n} @@ -200,6 +220,55 @@ def get_daily_stats(): ) +################# +# Graph Helpers # +################# + + +SCORE_COLOR_DICT = { + "Hole-in-1": "black", + "Eagle": "darkgreen", + "Birdie": "lightgreen", + "Par": "white", + "Bogey": "palevioletred", + "Double Bogey": "orangered", + "Fail": "darkred", +} + + +def get_line_graph(): + rows = _stats_dict() + figure = plotly.graph_objs.Figure() + total = [r for r in rows if r["Score"] == "Total"][0] + rows = [r for r in rows if r["Score"] not in ("Total", "Daily Average")] + total.pop("Score") + for row in rows: + score = row.pop("Score") + y_values = [] + for k in row.keys(): + row_val = row.get(k) + total_val = total.get(k) + pct = row_val / total_val * 100 + y_values.append(pct) + figure.add_trace( + plotly.graph_objs.Scatter( + x=list(row.keys()), + y=y_values, + fill="tonexty", + name=score, + line={"color": SCORE_COLOR_DICT[score]}, + stackgroup="dailies", + ) + ) + figure.update_xaxes(tickvals=list(total.keys()), title_text="Days") + figure.update_yaxes(title_text="Percent") + return dash.dcc.Graph(figure=figure) + + +############# +# App Setup # +############# + app.layout = dash.html.Div( children=[ dash.html.H1("#WordleGolf", style={"textAlign": "center"}, id="title"), @@ -209,6 +278,12 @@ app.layout = dash.html.Div( dash.html.Div("Loading...", id="user-scores"), ] ), + dash.html.Div( + [ + dash.html.H2("Score Graph", style={"textAlign": "center"}), + dash.html.Div("Loading...", id="stats-graph"), + ] + ), dash.html.Div( [ dash.html.H2("Daily Stats", style={"textAlign": "center"}), @@ -237,6 +312,15 @@ def get_stats_chart(_): return get_daily_stats() +@app.long_callback( + output=dash.dependencies.Output("stats-graph", "children"), + inputs=dash.dependencies.Input("title", "children"), + manager=long_callback_manager, +) +def get_stats_graph(_): + return get_line_graph() + + server = app.server