""" Simple run-scoped logger for the template generator. The log file under logs/ is erased and recreated at the start of every run (see reset_log()) so it only ever reflects the most recent generation. """ import datetime import os ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) LOGS_DIR = os.path.join(ROOT_DIR, "logs") LOG_FILE = os.path.join(LOGS_DIR, "template_generator.log") _log_path = None def reset_log(): """Erases any previous log and starts a fresh one for this run.""" global _log_path os.makedirs(LOGS_DIR, exist_ok=True) _log_path = LOG_FILE with open(_log_path, "w", encoding="utf-8") as f: f.write(f"Timetable generation log - started {datetime.datetime.now().isoformat()}\n") f.write("=" * 80 + "\n") return _log_path def log_error(message): """Appends an ERROR line to the current run's log file and prints it.""" _write("ERROR", message) def log_warning(message): _write("WARNING", message) def log_info(message): _write("INFO", message) def _write(level, message): global _log_path if _log_path is None: os.makedirs(LOGS_DIR, exist_ok=True) _log_path = LOG_FILE if not os.path.exists(_log_path): # First-ever write with no prior reset_log() call in this # process (e.g. a phase_N command run without a preceding # clear_all): create the file fresh, but never truncate an # existing log — separate process invocations of phase_1/2/3 # must all append to the same ongoing log. with open(_log_path, "w", encoding="utf-8") as f: f.write(f"Timetable generation log - started {datetime.datetime.now().isoformat()}\n") f.write("=" * 80 + "\n") line = f"[{datetime.datetime.now().strftime('%H:%M:%S')}] {level}: {message}" with open(_log_path, "a", encoding="utf-8") as f: f.write(line + "\n") print(line)