speed up load and display logic

This commit is contained in:
2022-06-13 17:19:47 -05:00
parent 2df45a15f8
commit d8240aeae0
3 changed files with 161 additions and 69 deletions

View File

@@ -128,8 +128,17 @@ class WordleDb:
def get_scores(self, round_no):
round = self.get_or_create_round(round_no)
res = (
Score.select(Score, Player.game_id)
Score.select(
Score,
Hole.hole,
User.username,
Player.game_id,
)
.join(Player, on=(Score.user_id == Player.user_id))
.switch(Score)
.join(Hole, on=(Score.hole_id == Hole.hole_id))
.switch(Score)
.join(User, on=(Score.user_id == User.user_id))
.filter(Player.game_id == round.game_id)
.filter(Score.game_id == round.game_id)
)

View File

@@ -1,23 +1,142 @@
import collections
import typing
import wordlinator.db.pg
def golf_score(score_list: typing.List) -> int:
scores = [s.score for s in score_list]
score_count = len(scores)
score = sum(scores) - (score_count * 4)
return score
############
# Mappings #
############
SCORE_NAME_MAP = {
1: "Hole-in-1",
2: "Eagle",
3: "Birdie",
4: "Par",
5: "Bogey",
6: "Double Bogey",
7: "Fail",
}
def get_user_scorelist(
username: str, scores: typing.List
) -> typing.Dict[str, typing.Any]:
scores = list(sorted(scores, key=lambda s: s.hole_id.hole))
return {
"Name": username,
"Score": golf_score(scores),
**{f"Hole {s.hole_id.hole}": s.score for s in scores},
}
###############
# ScoreMatrix #
###############
T = typing.TypeVar("T", bound="ScoreContainer")
class ScoreContainer:
def __init__(self, scores: typing.List[wordlinator.db.pg.Score]):
self._scores = scores
@staticmethod
def _get_attribute(
score: wordlinator.db.pg.Score, attribute_path: typing.List[str]
):
attribute = score
for path_part in attribute_path:
attribute = getattr(attribute, path_part)
return attribute
def dict_by(
self, attribute_path: str, container_class: typing.Type[T]
) -> typing.Dict[typing.Any, T]:
data_dict = collections.defaultdict(list)
path_parts = attribute_path.split(".")
for score in self._scores:
data_dict[self._get_attribute(score, path_parts)].append(score)
return {k: container_class(v) for k, v in data_dict.items()}
class ScoreRow(ScoreContainer):
@property
def total(self) -> int:
return sum(s.score for s in self._scores)
@property
def count(self) -> int:
return len(self._scores)
@property
def average(self) -> float:
return round(self.total / len(self._scores), 2)
class UserRow(ScoreRow):
def __init__(self, scores, username=None):
super().__init__(scores)
self.username = username or scores[0].user_id.username
@property
def golf_score(self) -> int:
return self.total - (self.count * 4)
def sorted_scores(self):
yield from sorted(self._scores, key=lambda s: s.hole_id.hole)
def raw_values(self):
yield from (s.score for s in self.sorted_scores())
def _present_format(self, score):
if score.tweet_id:
return (
f"[{score.score}]"
f"(https://twitter.com/{self.username}/status/{score.tweet_id})"
)
return score.score
def presentation_values(self, hole_no=None):
res = {s.hole_id.hole: self._present_format(s) for s in self.sorted_scores()}
if hole_no:
for i in range(1, hole_no + 1):
if i not in res:
res[i] = ""
return res
def user_row(self, hole_no=None):
return {
"Name": self.username,
"Score": self.golf_score,
**self.presentation_values(hole_no=hole_no),
}
class ScoreMatrix(ScoreContainer):
def by_user(self):
return self.dict_by("user_id.username", UserRow)
def for_user(self, username):
user_scores = [s for s in self._scores if s.user_id.username == username]
return UserRow(scores=user_scores, username=username)
def by_hole(self):
return self.dict_by("hole_id.hole", ScoreRow)
def for_hole(self, hole_no):
hole_scores = [s for s in self._scores if s.hole_id.hole == hole_no]
return ScoreRow(hole_scores)
def _level_counts(self, level_scores: "ScoreMatrix"):
hole_dict = level_scores.by_hole()
return {k: v.count for k, v in sorted(hole_dict.items())}
def score_breakdown(self):
by_score_dict = self.dict_by("score", ScoreMatrix)
return {
SCORE_NAME_MAP[k]: self._level_counts(v)
for k, v in sorted(by_score_dict.items())
}
def user_rows(self, wordle_day):
hole_no = wordle_day.golf_hole.hole_no
return [u.user_row(hole_no=hole_no) for u in self.by_user().values()]
######################
# Formatting Helpers #
######################
def format_string(col, condition):
@@ -60,17 +179,13 @@ def column_formats(col, pct):
},
"backgroundColor": "white",
},
]
def table_rows(score_list):
scores_by_user = collections.defaultdict(list)
for score in score_list:
scores_by_user[score.user_id.username].append(score)
return [
get_user_scorelist(username, scores)
for username, scores in scores_by_user.items()
{
"if": {
"column_id": col["id"],
"filter_query": format_string(col, "= ''"),
},
"backgroundColor": "white",
},
]

View File

@@ -1,4 +1,3 @@
import collections
import datetime
import functools
import time
@@ -56,7 +55,7 @@ def _scores_from_db(ttl_hash=None):
def scores_from_db():
return _scores_from_db(get_ttl_hash())
return wordlinator.utils.web.ScoreMatrix(_scores_from_db(get_ttl_hash()))
#################
@@ -65,11 +64,11 @@ def scores_from_db():
def get_scores():
score_list = scores_from_db()
table_rows = wordlinator.utils.web.table_rows(score_list)
score_matrix = scores_from_db()
table_rows = score_matrix.user_rows(wordle_today())
hole_columns = [
{"name": f"Hole {i}", "id": f"Hole {i}", "type": "numeric"}
{"name": f"{i}", "id": f"{i}", "type": "text", "presentation": "markdown"}
for i in range(1, wordle_today().golf_hole.hole_no + 1)
]
columns = [
@@ -119,58 +118,27 @@ def get_scores():
# Stats Helpers #
#################
SCORE_NAME_MAP = {
1: "Hole-in-1",
2: "Eagle",
3: "Birdie",
4: "Par",
5: "Bogey",
6: "Double Bogey",
7: "Fail",
}
def _get_summary_rows(score_matrix):
day_dict = score_matrix.by_hole()
def _get_score_breakdown(score, holes):
score_row = {"Score": SCORE_NAME_MAP[score]}
days = sorted(set(holes))
for day in days:
score_row[day] = holes.count(day)
return score_row
def _get_summary_rows(score_list):
days = list(sorted(set((score.hole_id.hole for score in score_list))))
day_dict = {
day: [score.score for score in score_list if score.hole_id.hole == day]
for day in days
}
totals = {
"Score": "Total",
**{day: len(scores) for day, scores in day_dict.items()},
**{day: scores.count for day, scores in day_dict.items()},
}
averages = {
"Score": "Daily Average",
**{
day: round(sum(scores) / len(scores), 2) for day, scores in day_dict.items()
},
**{day: scores.average for day, scores in day_dict.items()},
}
return [totals, averages]
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)
table_rows = []
for score in sorted(scores_by_value.keys()):
table_rows.append(_get_score_breakdown(score, scores_by_value[score]))
table_rows.extend(_get_summary_rows(score_list))
score_matrix = scores_from_db()
table_rows = [{"Score": k, **v} for k, v in score_matrix.score_breakdown().items()]
table_rows.extend(_get_summary_rows(score_matrix))
return table_rows