diff --git a/WebHostLib/stats.py b/WebHostLib/stats.py index 2ce25c2cc7a2..e5f350f125f7 100644 --- a/WebHostLib/stats.py +++ b/WebHostLib/stats.py @@ -35,19 +35,21 @@ def get_db_data(known_games: set[str]) -> tuple[Counter[str], defaultdict[date, def get_color_palette(colors_needed: int) -> list[RGB]: colors = [] - # colors_needed +1 to prevent first and last color being too close to each other - colors_needed += 1 - for x in range(0, 361, 360 // colors_needed): + step = (5 ** 0.5 - 1)/2 # golden ratio to maximize color separation + for n in range(colors_needed): # a bit of noise on value to add some luminosity difference - colors.append(RGB(*(val * 255 for val in hsv_to_rgb(x / 360, 0.8, 0.8 + (x / 1800))))) - - # splice colors for maximum hue contrast. - colors = colors[::2] + colors[1::2] + colors.append(RGB(*(val * 255 for val in hsv_to_rgb(step * n, 0.8, 0.8 + (step * n % 0.2))))) return colors +def truncated_name(game: str) -> str: + if len(game) > 20: + return f"{game[:18]}…" + return game + + def create_game_played_figure(all_games_data: dict[date, dict[str, int]], game: str, color: RGB) -> figure: occurences = [] days = [day for day, game_data in all_games_data.items() if game_data[game]] @@ -59,39 +61,38 @@ def create_game_played_figure(all_games_data: dict[date, dict[str, int]], game: } plot = figure( - title=f"{game} Played Per Day", x_axis_type='datetime', x_axis_label="Date", + title=f"{game} Played Per Day", x_axis_type="datetime", x_axis_label="Date", y_axis_label="Games Played", sizing_mode="scale_both", width=PLOT_WIDTH, height=500, toolbar_location=None, tools="", - # setting legend to False seems broken in bokeh currently? - # legend=False ) hover = HoverTool(tooltips=[("Date:", "@days{%F}"), ("Played:", "@played")], formatters={"@days": "datetime"}) plot.add_tools(hover) - plot.vbar(x="days", top="played", legend_label=game, color=color, source=ColumnDataSource(data=data), width=1) + plot.vbar(x="days", top="played", color=color, source=ColumnDataSource(data=data), width=timedelta(days=0.5)) return plot -@app.route('/stats') +@app.route("/stats") @cache.memoize(timeout=60 * 60) # regen once per hour should be plenty def stats(): from worlds import network_data_package known_games = set(network_data_package["games"]) - plot = figure(title="Games Played Per Day", x_axis_type='datetime', x_axis_label="Date", + plot = figure(title="Games Played Per Day", x_axis_type="datetime", x_axis_label="Date", y_axis_label="Games Played", sizing_mode="scale_both", width=PLOT_WIDTH, height=500) total_games, games_played = get_db_data(known_games) days = sorted(games_played) color_palette = get_color_palette(len(total_games)) - game_to_color: dict[str, RGB] = {game: color for game, color in zip(total_games, color_palette)} + # give in order of most common so colors next to each other on pie chart can't randomly be similar + game_to_color: dict[str, RGB] = {game: color for (game, _), color in zip(total_games.most_common(), color_palette)} for game in sorted(total_games): occurences = [] for day in days: occurences.append(games_played[day][game]) plot.line([datetime.combine(day, datetime.min.time()) for day in days], - occurences, legend_label=game, line_width=2, color=game_to_color[game]) + occurences, legend_label=truncated_name(game), line_width=2, color=game_to_color[game]) total = sum(total_games.values()) pie = figure(title=f"Games Played in the Last 30 Days (Total: {total})", toolbar_location=None, @@ -103,6 +104,7 @@ def stats(): data = { "games": [], + "games_abbrev": [], "count": [], "start_angles": [], "end_angles": [], @@ -110,6 +112,7 @@ def stats(): current_angle = 0 for i, (game, count) in enumerate(total_games.most_common()): data["games"].append(game) + data["games_abbrev"].append(truncated_name(game)) data["count"].append(count) data["start_angles"].append(current_angle) angle = count / total * tau @@ -120,7 +123,7 @@ def stats(): pie.wedge(x=0, y=0, radius=0.5, start_angle="start_angles", end_angle="end_angles", fill_color="colors", - source=ColumnDataSource(data=data), legend_field="games") + source=ColumnDataSource(data=data), legend_field="games_abbrev") per_game_charts = [create_game_played_figure(games_played, game, game_to_color[game]) for game in sorted(total_games, key=lambda game: total_games[game])