From 4b477c7112b3368a5f3a5e4acc214fac68767512 Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Tue, 26 Mar 2024 11:27:00 -0700 Subject: [PATCH] Add '--rating-history-db' to save ratings in a DB Add `--rating-history-db` command-line option to save ratings in an sqlite3 database. This adds a method to InMemoryStorage to save the rating history. It takes a `category` string for future use with rating grids, where there's a different InMemoryStorage for each rating category, but for now only `OneGameAtATime` has been updated. An aborted implementation used a new storage type OnDiskStorage instead of InMemoryStorage, but it was way too slow to build the table incrementally. Instead, this implementation writes the values in bulk at the end, using a generator expression to visit the rating history. --- .../analyze_glicko2_one_game_at_a_time.py | 6 ++- analysis/util/InMemoryStorage.py | 44 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/analysis/analyze_glicko2_one_game_at_a_time.py b/analysis/analyze_glicko2_one_game_at_a_time.py index cbfebf6..96cc136 100755 --- a/analysis/analyze_glicko2_one_game_at_a_time.py +++ b/analysis/analyze_glicko2_one_game_at_a_time.py @@ -64,8 +64,8 @@ def process_game(self, game: GameRecord) -> Glicko2Analytics: self._storage.set(game.black_id, updated_black) self._storage.set(game.white_id, updated_white) - #self._storage.add_rating_history(game.black_id, game.ended, updated_black) - #self._storage.add_rating_history(game.white_id, game.ended, updated_white) + self._storage.add_rating_history(game.black_id, game.ended, updated_black) + self._storage.add_rating_history(game.white_id, game.ended, updated_white) return Glicko2Analytics( skipped=False, @@ -99,6 +99,8 @@ def process_game(self, game: GameRecord) -> Glicko2Analytics: analytics = engine.process_game(game) tally.add_glicko2_analytics(analytics) +storage.save_rating_history("overall") + tally.print() self_reported_ratings = tally.get_self_reported_rating() diff --git a/analysis/util/InMemoryStorage.py b/analysis/util/InMemoryStorage.py index e070752..ea24166 100644 --- a/analysis/util/InMemoryStorage.py +++ b/analysis/util/InMemoryStorage.py @@ -1,10 +1,18 @@ +import sqlite3 from collections import defaultdict from typing import Any, DefaultDict, Dict, List, Tuple +from analysis.util import cli from goratings.interfaces import Storage +from .Config import config + __all__ = ["InMemoryStorage"] +cli.add_argument( + "--rating-history-db", dest="rating_history_db", type=str, + help="Path to DB for ratings history (not saved by default)", +) class InMemoryStorage(Storage): _data: Dict[int, Any] @@ -87,3 +95,39 @@ def get_matches_newer_or_equal_to(self, player_id: int, timestamp: int) -> Any: else: break return [e[1] for e in self._match_history[player_id][-ct:]] + + def save_rating_history(self, category: str) -> None: + if config.args.rating_history_db is None: + return + connection = sqlite3.connect(config.args.rating_history_db) + cursor = connection.cursor() + cursor.execute(""" + CREATE TABLE IF NOT EXISTS rating_history( + category TEXT, + player_id INTEGER, + timestamp INTEGER, + rating REAL, + deviation REAL, + volatility REAL + )""") + cursor.execute(""" + CREATE INDEX IF NOT EXISTS index_ratings_by_player + ON rating_history + (player_id,timestamp) + """) + cursor.execute(""" + CREATE INDEX IF NOT EXISTS index_ratings_by_category + ON rating_history + (category,player_id,timestamp) + """) + cursor.executemany(""" + INSERT INTO rating_history(category,player_id,timestamp,rating,deviation,volatility) + VALUES (?,?,?,?,?,?) + """, + ((category, player, timestamp, r.rating, r.deviation, r.volatility) + for (player,history) in self._rating_history.items() + for (timestamp, r) in history + ), + ) + connection.commit() + connection.close()