add leaderboard race lines
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import collections
|
import collections
|
||||||
|
import itertools
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
import wordlinator.db.pg
|
import wordlinator.db.pg
|
||||||
@@ -76,6 +77,15 @@ class UserRow(ScoreRow):
|
|||||||
def golf_score(self) -> int:
|
def golf_score(self) -> int:
|
||||||
return self.total - (self.count * 4)
|
return self.total - (self.count * 4)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def progressive_score_list(self) -> typing.List[int]:
|
||||||
|
score_progress = list(
|
||||||
|
itertools.accumulate(
|
||||||
|
self.sorted_scores(), func=lambda t, e: t + (e.score - 4), initial=0
|
||||||
|
)
|
||||||
|
)[1:]
|
||||||
|
return score_progress
|
||||||
|
|
||||||
def sorted_scores(self):
|
def sorted_scores(self):
|
||||||
yield from sorted(self._scores, key=lambda s: s.hole_id.hole)
|
yield from sorted(self._scores, key=lambda s: s.hole_id.hole)
|
||||||
|
|
||||||
@@ -189,3 +199,19 @@ class ScoreMatrix(ScoreContainer):
|
|||||||
def user_rows(self, wordle_day):
|
def user_rows(self, wordle_day):
|
||||||
hole_no = wordle_day.golf_hole.hole_no
|
hole_no = wordle_day.golf_hole.hole_no
|
||||||
return [u.user_row(hole_no=hole_no) for u in self.by_user().values()]
|
return [u.user_row(hole_no=hole_no) for u in self.by_user().values()]
|
||||||
|
|
||||||
|
def top_by_day(self):
|
||||||
|
user_dict = {u: r.progressive_score_list for u, r in self.by_user().items()}
|
||||||
|
days = max(map(len, user_dict.values()))
|
||||||
|
|
||||||
|
rankings = collections.defaultdict(list)
|
||||||
|
for day_idx in range(days):
|
||||||
|
day_scores = {
|
||||||
|
u: v[day_idx] for u, v in user_dict.items() if len(v) >= day_idx + 1
|
||||||
|
}
|
||||||
|
tops = [(u, v) for u, v in sorted(day_scores.items(), key=lambda t: t[1])][
|
||||||
|
:20
|
||||||
|
]
|
||||||
|
for (user, score) in tops:
|
||||||
|
rankings[user].append((day_idx + 1, score))
|
||||||
|
return rankings
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import collections
|
||||||
import functools
|
import functools
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
@@ -16,7 +17,7 @@ import wordlinator.utils
|
|||||||
import wordlinator.utils.scores
|
import wordlinator.utils.scores
|
||||||
import wordlinator.utils.web
|
import wordlinator.utils.web
|
||||||
|
|
||||||
TTL_TIME = 30 if os.getenv("DEBUG") else 600
|
TTL_TIME = 30 if os.getenv("DEBUG") else 90
|
||||||
LEADERBOARD_COUNT = 20
|
LEADERBOARD_COUNT = 20
|
||||||
|
|
||||||
###################
|
###################
|
||||||
@@ -251,6 +252,38 @@ def get_line_graph(round_id):
|
|||||||
return dash.dcc.Graph(figure=figure)
|
return dash.dcc.Graph(figure=figure)
|
||||||
|
|
||||||
|
|
||||||
|
#####################
|
||||||
|
# Line Race Helpers #
|
||||||
|
#####################
|
||||||
|
|
||||||
|
|
||||||
|
def line_race_graph(round_id):
|
||||||
|
score_matrix = scores_from_db(round_id)
|
||||||
|
tops_by_day = score_matrix.top_by_day()
|
||||||
|
|
||||||
|
figure = plotly.graph_objs.Figure()
|
||||||
|
figure.update_yaxes(autorange="reversed")
|
||||||
|
figure.update_xaxes(tickmode="linear", tick0=1, dtick=1)
|
||||||
|
annotation_names = collections.defaultdict(list)
|
||||||
|
for name, entries in tops_by_day.items():
|
||||||
|
figure.add_trace(
|
||||||
|
plotly.graph_objs.Scatter(
|
||||||
|
name=name,
|
||||||
|
mode="lines+markers",
|
||||||
|
x=[e[0] for e in entries],
|
||||||
|
y=[e[1] for e in entries],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
annotation_names[entries[-1]].append(name)
|
||||||
|
|
||||||
|
annotations = [
|
||||||
|
{"x": k[0], "y": k[1], "text": ", ".join(v)}
|
||||||
|
for k, v in annotation_names.items()
|
||||||
|
]
|
||||||
|
figure.update_layout(annotations=annotations)
|
||||||
|
return dash.dcc.Graph(figure=figure)
|
||||||
|
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# App Setup #
|
# App Setup #
|
||||||
#############
|
#############
|
||||||
@@ -277,6 +310,18 @@ app.layout = dash.html.Div(
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
dash.html.Div(
|
||||||
|
[
|
||||||
|
dash.html.H2(
|
||||||
|
f"Leaderboard - Top {LEADERBOARD_COUNT}",
|
||||||
|
style={"textAlign": "center"},
|
||||||
|
),
|
||||||
|
dash.dcc.Loading(
|
||||||
|
id="leaderboard-race-loading",
|
||||||
|
children=dash.html.Div("Loading...", id="leaderboard-race"),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
dash.html.Div(
|
dash.html.Div(
|
||||||
[
|
[
|
||||||
dash.html.H2("User Scores", style={"textAlign": "center"}),
|
dash.html.H2("User Scores", style={"textAlign": "center"}),
|
||||||
@@ -320,6 +365,18 @@ def get_leaderboard_table(_, round_id):
|
|||||||
return get_leaderboard(round_id)
|
return get_leaderboard(round_id)
|
||||||
|
|
||||||
|
|
||||||
|
@app.long_callback(
|
||||||
|
output=dash.dependencies.Output("leaderboard-race", "children"),
|
||||||
|
inputs=[
|
||||||
|
dash.dependencies.Input("title", "children"),
|
||||||
|
dash.dependencies.Input("round-selector-dropdown", "value"),
|
||||||
|
],
|
||||||
|
manager=long_callback_manager,
|
||||||
|
)
|
||||||
|
def get_leaderboard_race(_, round_id):
|
||||||
|
return line_race_graph(round_id)
|
||||||
|
|
||||||
|
|
||||||
@app.long_callback(
|
@app.long_callback(
|
||||||
output=dash.dependencies.Output("user-scores", "children"),
|
output=dash.dependencies.Output("user-scores", "children"),
|
||||||
inputs=[
|
inputs=[
|
||||||
|
|||||||
Reference in New Issue
Block a user