session management in CLI
authorNiki <niki@nikiroo.be>
Mon, 29 Sep 2025 06:01:39 +0000 (08:01 +0200)
committerNiki <niki@nikiroo.be>
Mon, 29 Sep 2025 06:01:39 +0000 (08:01 +0200)
gamiki.py
gamiki/builder.py
gamiki/game.py
gamiki/session.py

index abbcf82d162b8d6ed1475ccde8bf26e8398ab94e..a37b2db6832dc105a5841b42dd51036ac068a380 100755 (executable)
--- a/gamiki.py
+++ b/gamiki.py
@@ -24,6 +24,18 @@ global_actions.add_argument("--list-tags",
     help="list all known games and their tags",
     action="store_true"
 )
+global_actions.add_argument("--session-time", 
+    help="time played per game",
+    action="store_true"
+)
+global_actions.add_argument("--export-sessions", 
+    help="export play sessions of all known games",
+    action="store_true"
+)
+global_actions.add_argument("--import-sessions", 
+    help="import (and merge) play sessions",
+    action="store_true"
+)
 
 # Select a game
 game_selection = parser.add_argument_group('game selection')
@@ -66,6 +78,10 @@ game_actions.add_argument("--info-icon",
     help="get the icon of the game",
     metavar="GAME"
 )
+game_actions.add_argument("--info-sessions", 
+    help="get the play sessions of the game",
+    metavar="GAME"
+)
 
 args = parser.parse_args()
 
@@ -79,6 +95,15 @@ elif (args.list_tag):
 elif (args.list_tags):
     builder.list_tags()
     exit(0)
+elif (args.session_time):
+    builder.session_time()
+    exit(0)
+elif (args.export_sessions):
+    builder.export_sessions()
+    exit(0)
+elif (args.import_sessions):
+    builder.import_sessions()
+    exit(0)
 
 g = ""
 if (args.info):
@@ -95,6 +120,8 @@ elif (args.info_options):
     g = args.info_options
 elif (args.info_icon):
     g = args.info_icon
+elif (args.info_sessions):
+    g = args.info_sessions
 else:
     g = args.GAME
 
@@ -135,6 +162,11 @@ elif (args.info_options):
 elif (args.info_icon):
     if ("Icon" in game):
         print(game["Icon"])
+elif (args.info_sessions):
+    print("Listing play sessions of:", game.name)
+    game.read_sessions()
+    for sess in game.sessions:
+        print(sess)
 else:
     game.run(args.OPTION)
 
index 1e3fc2cb4669bec41cb146fb224d0f0b8a54619d..3556c07e50a17ef59a6d33612c6a50b6680881d2 100644 (file)
@@ -1,6 +1,7 @@
+from sys     import stdin, stderr
 from pathlib import Path, PurePath
 
-from gamiki         import Game, Support, Library
+from gamiki         import Game, Support, Library, Session
 from gamiki.support import (
     SupportDos, SupportWin31, SupportGog, SupportExt, SupportWin
 )
@@ -61,6 +62,66 @@ class Builder:
                     "(" + ";".join(game.tags)  +")")
             i += 1
     
+    def session_time(self):
+        i = 1
+        for game in self.games:
+            if (game.total_time > 0):
+                num = "{0:6d}".format(i)
+                print(f"{num}  {game.total_htime}: {game.name}");
+            i += 1
+    
+    def export_sessions(self):
+        for lib in self.libraries:
+            for game in lib:
+                game.read_sessions()
+                if (len(game.sessions)):
+                    print(">", lib.name + "/" + game.code)            
+                    for sess in game.sessions:
+                        print(sess)
+    
+    def import_sessions(self):
+        sgam = ""
+        slib = ""
+        curr_lib = None
+        curr_gam = None
+        sss = None
+        
+        for ln in stdin:
+            ln = ln.strip()
+            if (ln and ln[0] == ">"):
+                slib = ln.split("/")[0][2:]
+                sgam = ln.split("/", maxsplit=1)[1]
+                
+                # TODO fix speed
+                if (curr_lib and curr_lib.name != slib):
+                    curr_lib = None
+                if (not curr_lib):
+                    for lib in self.libraries:
+                        if (lib.name == slib):
+                            curr_lib = lib
+                if (curr_gam and curr_gam.code != sgam):
+                    Session.rewrite_sessions(curr_gam.dir, list(sss.values()))
+                    curr_gam = None
+                if (curr_lib and not curr_gam):
+                    for gam in curr_lib:
+                        if (gam.code == sgam):
+                            curr_gam = gam
+            else:
+                if (curr_gam):
+                    if (sss == None):
+                        curr_gam.read_sessions()
+                        sss = {}
+                        for sess in curr_gam.sessions:
+                            sss[sess.when] = sess
+                    
+                    new_sess = Session.from_str(ln)
+                    sss[new_sess.when] = new_sess
+                else:
+                    print("Ignoring unknown game:", slib + "/" + sgam, file=stderr)
+            
+        if (curr_gam):
+            Session.rewrite_sessions(curr_gam.dir, list(sss.values()))
+    
     def find(self, key: str) -> Game:
         game: Game = None
         
index 035a340ded9c32929df715dfb862a41e6cd30155..c805a3f4bbeb557b22b4c4a7526b8ce5ccb225c8 100644 (file)
@@ -30,6 +30,7 @@ class Game(dict):
     opts           : dict     = None # option name -> description
     desc           : str      = ""
     total_time     : int      = 0 
+    total_htime    : str      = "" # human readable time (HH:MM:SS) 
     total_sessions : int      = 0
     sessions       : list     = None # list:Session (call read_sessions() first)
     icon           : Path     = None # "main" icon
@@ -93,8 +94,11 @@ class Game(dict):
         except FileNotFoundError:
             pass
         
-        self.total, self.sessions = Session.read_total(self.dir)
-        # TODO: read the total per option times
+        # TODO: read the total per option times?
+        tot, htot, sessions = Session.read_total(self.dir)
+        self.total_time     = tot
+        self.total_htime    = htot
+        self.total_sessions = sessions
     
     def _read_icon(self, suffix: str = "") -> PurePath:
         """
index 957456910b91c360ad58feb4d5504a7a72fe39e4..42b3d01cc7e39a7aa211ab688d27102eb6f93416 100644 (file)
@@ -49,13 +49,17 @@ class Session():
         humanf = Session._human(self.elapsed)
         return f"Session of {dat} ({hour}) lasted: {humanf}{oopt}"
 
-    def read_total(dir: PurePath) -> (int, int):
+    def read_total(dir: PurePath) -> (int, 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)
+        @return (
+            total play time in second, 
+            total play time in HH:MM:SS format,
+            number of sessions played
+        )
         """
 
         ln1 = "(not read yet)"
@@ -74,7 +78,7 @@ class Session():
                 )
 
                 # TODO: read the total per option times?
-                return (total, num_sessions)
+                return (total, Session._human(total), num_sessions)
         except FileNotFoundError:
             pass
         except Exception as e:
@@ -94,7 +98,7 @@ class Session():
             num_sessions += 1
             total        += sess.elapsed
 
-        return (total, num_sessions)
+        return (total, Session._human(total), num_sessions)
 
     def read_sessions(dir: PurePath) -> list['Session']:
         sessions = []
@@ -165,6 +169,30 @@ class Session():
             Session.write_total(dir, sessions)
         except Exception as e:
             print(f"Cannot save total time: {e}", file=stderr)
+    
+    def rewrite_sessions(dir: PurePath, sessions: list):
+        """
+        Rewrite the list of sessions, overriding the previous one -- 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 new play sessions (will be used to overwrite
+                         the previous information, be careful!)
+
+        """
+
+        # Can throw:
+        file = dir.joinpath("sessions.txt")
+        with open(file, "w", encoding="utf-8") as data:
+            for sess in sessions:
+                data.write(str(sess))
+                data.write("\n")
+
+        # 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)