diff --git a/aocsync.py b/aocsync.py index 663f30a..7610b50 100755 --- a/aocsync.py +++ b/aocsync.py @@ -37,6 +37,7 @@ class PerformanceResult: part: int time_ns: int # Runner time in nanoseconds generator_time_ns: int = 0 # Generator time in nanoseconds (optional) + output_bytes: int = 0 # Number of bytes in the output/answer git_rev: str = "" # Git revision (short hash) repo_url: str = "" # Repository URL timestamp: str = "" @@ -121,6 +122,7 @@ class Database: part INTEGER NOT NULL, time_ns INTEGER NOT NULL, generator_time_ns INTEGER NOT NULL DEFAULT 0, + output_bytes INTEGER NOT NULL DEFAULT 0, git_rev TEXT NOT NULL DEFAULT '', repo_url TEXT NOT NULL DEFAULT '', timestamp TEXT NOT NULL, @@ -131,6 +133,7 @@ class Database: # Add new columns if they don't exist (for existing databases) for column, col_type in [ ('generator_time_ns', 'INTEGER NOT NULL DEFAULT 0'), + ('output_bytes', 'INTEGER NOT NULL DEFAULT 0'), ('git_rev', 'TEXT NOT NULL DEFAULT \'\''), ('repo_url', 'TEXT NOT NULL DEFAULT \'\'') ]: @@ -156,10 +159,10 @@ class Database: try: cursor.execute(''' INSERT OR REPLACE INTO results - (user, year, day, part, time_ns, generator_time_ns, git_rev, repo_url, timestamp) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + (user, year, day, part, time_ns, generator_time_ns, output_bytes, git_rev, repo_url, timestamp) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', (result.user, result.year, result.day, result.part, - result.time_ns, result.generator_time_ns, result.git_rev, + result.time_ns, result.generator_time_ns, result.output_bytes, result.git_rev, result.repo_url, result.timestamp)) conn.commit() except sqlite3.IntegrityError: @@ -178,7 +181,7 @@ class Database: cursor = conn.cursor() query = ''' - SELECT user, year, day, part, time_ns, generator_time_ns, git_rev, repo_url, timestamp + SELECT user, year, day, part, time_ns, generator_time_ns, output_bytes, git_rev, repo_url, timestamp FROM results r1 WHERE timestamp = ( SELECT MAX(timestamp) @@ -220,9 +223,10 @@ class Database: 'part': row[3], 'time_ns': row[4], 'generator_time_ns': row[5] if len(row) > 5 else 0, - 'git_rev': row[6] if len(row) > 6 else '', - 'repo_url': row[7] if len(row) > 7 else '', - 'timestamp': row[8] if len(row) > 8 else (row[6] if len(row) > 6 else '') + 'output_bytes': row[6] if len(row) > 6 else 0, + 'git_rev': row[7] if len(row) > 7 else '', + 'repo_url': row[8] if len(row) > 8 else '', + 'timestamp': row[9] if len(row) > 9 else (row[7] if len(row) > 7 else '') } for row in rows ] @@ -785,9 +789,12 @@ class CargoAOCRunner: stdout_clean = CargoAOCRunner._strip_ansi_codes(result.stdout or "") stderr_clean = CargoAOCRunner._strip_ansi_codes(result.stderr or "") + # Count bytes in stdout output (original, before ANSI stripping) + output_bytes = len(result.stdout.encode('utf-8')) if result.stdout else 0 + # Parse output for runtime information day_results = CargoAOCRunner._parse_runtime_output( - stdout_clean, stderr_clean, day, year, user, git_rev, repo_url + stdout_clean, stderr_clean, day, year, user, git_rev, repo_url, output_bytes ) if day_results: logger.info(f"Parsed {len(day_results)} runtime result(s) for {user} year {year} day {day}") @@ -816,7 +823,7 @@ class CargoAOCRunner: @staticmethod def _parse_runtime_output(stdout: str, stderr: str, day: int, year: int, - user: str, git_rev: str = "", repo_url: str = "") -> List[PerformanceResult]: + user: str, git_rev: str = "", repo_url: str = "", output_bytes: int = 0) -> List[PerformanceResult]: """Parse cargo-aoc runtime output cargo aoc typically outputs timing information like: @@ -869,6 +876,7 @@ class CargoAOCRunner: # First, try to parse the generator/runner format which is most common # Look for "Day X - Part Y" lines and extract both generator and runner times + # output_bytes parameter contains the total stdout bytes for this day run lines = output.split('\n') current_day = None current_part = None @@ -889,6 +897,7 @@ class CargoAOCRunner: part=current_part, time_ns=runner_time_ns, generator_time_ns=generator_time_ns, + output_bytes=output_bytes, git_rev=git_rev, repo_url=repo_url, timestamp=timestamp @@ -933,6 +942,7 @@ class CargoAOCRunner: part=current_part, time_ns=runner_time_ns, generator_time_ns=generator_time_ns, + output_bytes=output_bytes, git_rev=git_rev, repo_url=repo_url, timestamp=timestamp @@ -994,6 +1004,7 @@ class CargoAOCRunner: day=actual_day, part=part_num, time_ns=time_ns, + output_bytes=output_bytes, timestamp=timestamp )) except ValueError: @@ -1023,6 +1034,7 @@ class CargoAOCRunner: day=day, part=1, time_ns=time_ns, + output_bytes=output_bytes, timestamp=timestamp )) elif len(matches) == 2: @@ -1036,6 +1048,7 @@ class CargoAOCRunner: day=day, part=idx, time_ns=time_ns, + output_bytes=output_bytes, timestamp=timestamp )) break @@ -1100,6 +1113,7 @@ class HTMLGenerator: user = result['user'] runner_time_ns = result['time_ns'] generator_time_ns = result.get('generator_time_ns', 0) + output_bytes = result.get('output_bytes', 0) git_rev = result.get('git_rev', '') repo_url = result.get('repo_url', '') @@ -1111,11 +1125,12 @@ class HTMLGenerator: 'total': total_time_ns, 'runner': runner_time_ns, 'generator': generator_time_ns, + 'output_bytes': output_bytes, 'git_rev': git_rev, 'repo_url': repo_url } - html = self._generate_html(data, years, users, db, config) + html = self._generate_html(data, years, users, db, config, results) output_file = self.output_dir / 'index.html' with open(output_file, 'w') as f: @@ -1286,7 +1301,7 @@ class HTMLGenerator: html += '' return html - def _generate_html(self, data: dict, years: List[int], users: List[str], db: Database, config: Config) -> str: + def _generate_html(self, data: dict, years: List[int], users: List[str], db: Database, config: Config, results: List[dict]) -> str: """Generate HTML content""" # Get refresh interval from config (default 5 minutes = 300 seconds) refresh_interval = config.config.get('html_refresh_interval', 300) @@ -1944,6 +1959,60 @@ class HTMLGenerator: """ + # Add output bytes summary table + # Use the results passed to the method + html += """ +
Number of bytes written to stdout for each day/part/user combination
+| Year | +Day | +Part | +""" + for user in sorted(users): + html += f"{user} | \n" + html += """|
|---|---|---|---|---|
| {year} | +{day} | +{part} | +""" + for user in sorted(users): + bytes_val = output_bytes_data[year][day][part].get(user, None) + if bytes_val is not None and bytes_val > 0: + html += f"{bytes_val:,} | \n" + else: + html += "- | \n" + html += "