From 5c1271bd435e276f9770e91e38e1843a59268707 Mon Sep 17 00:00:00 2001 From: niki Date: Sun, 28 Sep 2025 22:30:10 +0200 Subject: [PATCH] export session management to Session --- gamiki/__init__.py | 1 + gamiki/game.py | 124 +++----------------------------- gamiki/session.py | 173 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 185 insertions(+), 113 deletions(-) create mode 100644 gamiki/session.py diff --git a/gamiki/__init__.py b/gamiki/__init__.py index 595436a..55bf1b7 100644 --- a/gamiki/__init__.py +++ b/gamiki/__init__.py @@ -1,4 +1,5 @@ # Order the dependencies in the right way, and make public classes accessible +from .session import Session from .game import Game from .support import Support from .library import Library diff --git a/gamiki/game.py b/gamiki/game.py index 9c6354e..035a340 100644 --- a/gamiki/game.py +++ b/gamiki/game.py @@ -4,6 +4,7 @@ from pathlib import PurePath, Path from time import time from datetime import datetime from random import choice +from gamiki import Session class Game(dict): """Game object generated from a directory.""" @@ -30,7 +31,7 @@ class Game(dict): desc : str = "" total_time : int = 0 total_sessions : int = 0 - sessions : list = None # list of (YYYY-MM-DD, HH:MM, secs, opt) + sessions : list = None # list:Session (call read_sessions() first) icon : Path = None # "main" icon icons : dict = None # all found icons intro : list = None # all found intro videos @@ -92,27 +93,8 @@ class Game(dict): except FileNotFoundError: pass - file = self.dir.joinpath("sessions.total") - try: - with open(file, encoding="utf-8") as data: - ln = data.readline() # number of sessions - sessions = int(ln.split(":", maxsplit=1)[1].strip()) - ln = data.readline() # total time in human formated time - human = ln.split(":", maxsplit=1)[1].strip() - tab = human.split(":") - total = int((int)(tab[0]) * 3600 - + (int)(tab[1]) * 60 - + (int)(tab[2]) - ) - - self.total_time = total - self.total_sessions = sessions - - # TODO: read the total per option times - except FileNotFoundError: - pass - except Exception as e: - print(f"Error when querying {file.as_posix()}: {e}", file=stderr) + self.total, self.sessions = Session.read_total(self.dir) + # TODO: read the total per option times def _read_icon(self, suffix: str = "") -> PurePath: """ @@ -152,96 +134,8 @@ class Game(dict): def get_icon(self, key: str) -> PurePath: return self.icons.get(key, self.icon) - def _read_sessions(self) -> list: - self.sessions = [] - file = self.dir.joinpath("sessions.txt") - if (file.exists()): - try: - with open(file, "r", encoding="utf-8") as data: - for ln in data: - start_d = "" - start_h = "" - total = 0 - so = "" - tab = ln.strip().split(" ") - if (len(tab) >= 4): - start_d = tab[2] - start_h = tab[3].split("(")[1].split(")")[0] - if (len(tab) >= 6): - htab = tab[5].split(":") - if (len(htab) == 3): - total += int(htab[0]) * 3600 - total += int(htab[1]) * 60 - total += int(htab[2]) - if (len(tab) >= 8): - so = tab[7] - if (start_d and start_h): - self.sessions.append((start_d, start_h, total, so)) - except Exception as e: - print(f"Error when querying {file.as_posix()}: {e}",file=stderr) - - def add_session(self, when: datetime, elapsed: int, opt: str): - def human(seconds: int) -> str: - ss = (seconds % 60) - mm = (seconds // 60) % 60 - hh = (seconds // 3600) - return f"{hh:02}:{mm:02}:{ss:02}" - - O = "" - if (opt): - O = f" - {opt}" - - start_d = when.strftime('%Y-%m-%d') - start_h = when.strftime('%H:%M') - text = f"Session of {start_d} ({start_h}) lasted: {human(elapsed)}{O}" - print(f"[{self.name}]: {text}") - - if (not self.sessions): - self._read_sessions() - - self.sessions.append((start_d, start_h, elapsed, opt)) - - total = 0 - for session in self.sessions: - total += session[2] - print( - f"[{self.name}]: Total time: " - f"{human(total)} (in {len(self.sessions)} session(s))" - ) - - try: - file = self.dir.joinpath("sessions.txt") - with open(file, "a", encoding="utf-8") as data: - data.write(text) - data.write("\n") - except Exception as e: - print("Cannot save session time:", e, file=stderr) - total = None - - try: - if (total): - file = self.dir.joinpath("sessions.total") - with open(file, "w", encoding="utf-8") as data: - data.write(f"Sessions: {len(self.sessions)}\n") - data.write(f"Total time: {human(total)}\n") - - if (len(self.opts) > 1): - data.write("\nTotal per option:\n") - total = 0 - for session in self.sessions: - if (session[3] == None or session[3] == ""): - total += session[2] - data.write(f"* {human(total)}\n"); - for opt in self.opts: - total = 0 - for session in self.sessions: - if (len(session) > 3): - if (session[3] == opt): - total += session[2] - data.write(f"* {human(total)} - {opt}\n"); - - except: - print("Cannot save total time", file=stderr) + def read_sessions(self) -> list: + self.sessions = Session.read_sessions(self.dir) def run(self, opt: str = None): if (not self.support): @@ -261,5 +155,9 @@ class Game(dict): elapsed = int(time() - begin) if (elapsed >= 60): # Ignore very short sessions - self.add_session(today, elapsed, opt) + if (self.sessions == None): + self.read_sessions() + + new_sess = Session(today, elapsed, opt) + Session.write_new_session(self.dir, self.sessions, new_sess) diff --git a/gamiki/session.py b/gamiki/session.py new file mode 100644 index 0000000..9574569 --- /dev/null +++ b/gamiki/session.py @@ -0,0 +1,173 @@ +from pathlib import PurePath, Path +from sys import stderr +from time import time +from datetime import datetime + +class Session(): + """Play session for a game.""" + + when : datetime = None + elapsed : int = None + opt : str = None + + def __init__(self, when: datetime, elapsed: int, opt: str = None): + self.when = when + self.elapsed = elapsed + self.opt = opt + + def from_str(ln: str): + dat = "" + hour = "" + total = 0 + opt = "" + tab = ln.strip().split(" ") + if (len(tab) >= 4): + dat = tab[2] + hour = tab[3].split("(")[1].split(")")[0] + if (len(tab) >= 6): + htab = tab[5].split(":") + if (len(htab) == 3): + total += int(htab[0]) * 3600 + total += int(htab[1]) * 60 + total += int(htab[2]) + if (len(tab) >= 8): + opt = tab[7] + if (dat and hour): + return Session( + datetime.strptime(f"{dat} {hour}", "%Y-%m-%d %H:%M"), total, opt + ) + + return None + + def __str__(self) -> str: + oopt = "" + if (self.opt): + oopt = f" - {opt}" + + dat = self.when.strftime('%Y-%m-%d') + hour = self.when.strftime('%H:%M') + humanf = Session._human(self.elapsed) + return f"Session of {dat} ({hour}) lasted: {humanf}{oopt}" + + def read_total(dir: PurePath) -> (int, int): + """ + Read a session.total file. + + @param dir: the game directory where to find the session.total file + + @return (total play time in second, number of sessions played) + """ + + ln1 = "(not read yet)" + ln2 = "(not read yet)" + file = dir.joinpath("sessions.total") + try: + with open(file, encoding="utf-8") as data: + ln1 = data.readline().strip() # number of sessions + num_sessions = int(ln1.split(":", maxsplit=1)[1].strip()) + ln2 = data.readline().strip() # total time, human formated + humanf = ln2.split(":", maxsplit=1)[1].strip() + tab = humanf.split(":") + total = int((int)(tab[0]) * 3600 + + (int)(tab[1]) * 60 + + (int)(tab[2]) + ) + + # TODO: read the total per option times? + return (total, num_sessions) + except FileNotFoundError: + pass + except Exception as e: + #raise ValueError(f"Error when querying {file.as_posix()}: {e}" + # + "\n" + "First 2 lines:\n" + ln1 + "\n" + ln2 + "\n.\n" + #) + print(f"Error when reading total from {file.as_posix()}: {e}" + + "\n" + "First 2 lines:\n" + ln1 + "\n" + ln2 + "\n.\n" + + "We will try to re-read the whole session file...", + file=stderr + ) + + total = 0 + num_sessions = 0 + sessions = Session.read_sessions(dir) + for sess in sessions: + num_sessions += 1 + total += sess.elapsed + + return (total, num_sessions) + + def read_sessions(dir: PurePath) -> list['Session']: + sessions = [] + file = dir.joinpath("sessions.txt") + if (file.exists()): + try: + with open(file, "r", encoding="utf-8") as data: + for ln in data: + sess = Session.from_str(ln) + if (sess): + sessions.append(sess) + except Exception as e: + raise ValueError(f"Error when reading {file.as_posix()}: {e}") + + return sessions + + def write_total(dir: PurePath, sessions: list): + total = 0 + opts = [] + for sess in sessions: + total += sess.elapsed + if not sess.opt in opts: + opts.append(sess.opt) + + file = dir.joinpath("sessions.total") + with open(file, "w", encoding="utf-8") as data: + data.write(f"Sessions: {len(sessions)}\n") + data.write(f"Total time: {Session._human(total)}\n") + + if (len(opts) > 1): + data.write("\nTotal per option:\n") + total = 0 + for session in sessions: + if (session[3] == None or session[3] == ""): + total += session[2] + data.write(f"* {_human(total)}\n"); + for opt in opts: + total = 0 + for session in sessions: + if (len(session) > 3): + if (session[3] == opt): + total += session[2] + data.write(f"* {Session._human(total)} - {opt}\n"); + + def write_new_session(dir: PurePath, sessions: list, new_sess: 'Session'): + """ + Write a new session on the given game directory -- will also update the + sessions.total file or create it if needed. + + @param dir: the game directory where to find the session.total file + @param sessions: the previous play sessions (will be used to overwrite + the previous information, be careful!) -- note that the + new play session will also be appended on this one + @param new_sess: the new play session to add + + """ + + # Can throw: + file = dir.joinpath("sessions.txt") + with open(file, "a", encoding="utf-8") as data: + data.write(str(new_sess)) + data.write("\n") + + sessions.append(new_sess) + + # We catch the exceptions, as this file can be recomputed if needed + try: + Session.write_total(dir, sessions) + except Exception as e: + print(f"Cannot save total time: {e}", file=stderr) + + def _human(seconds: int) -> str: + ss = (seconds % 60) + mm = (seconds // 60) % 60 + hh = (seconds // 3600) + return f"{hh:02}:{mm:02}:{ss:02}" -- 2.27.0