"""JSON configuration loader for ITU document generation.""" import json import os import sys import datetime def load_config(config_path): """Load and validate a JSON configuration file. Returns a dict with all configuration values, with defaults applied. Relative paths (e.g. workProgramme) are resolved relative to the config file's directory. If 'end' date is not provided, it will be fetched from the ITU meeting page. """ try: with open(config_path, "r", encoding="utf-8") as fid: content = json.loads(fid.read()) except Exception as e: print(f"Error loading config {config_path}: {e}") sys.exit(1) # Study group group = _get_int(content, 'group', "group") # Start date (required) start_string = content.get('start') start = _parse_date(start_string, "start date") start_date = f"{start.year:04}{start.month:02}{start.day:02}" # End date and place - fetched from ITU meeting page from common.itu_api import get_meeting_info print(f"Fetching meeting info from ITU website...") meeting_info = get_meeting_info(group, start_date) if meeting_info: end = meeting_info['end'] place = meeting_info.get('place', '') country = meeting_info.get('country', '') print(f" Found: {meeting_info['start'].strftime('%Y-%m-%d')} to {end.strftime('%Y-%m-%d')}, {place} ({country})") else: print(" Warning: Could not fetch meeting info from ITU. Using defaults.") end = start place = '' country = '' config = { 'group': group, 'place': place, 'country': country, 'start': start, 'startString': start_string, 'startDate': start_date, 'end': end, '_raw': content, } return config def load_question_config(config_path): """Load config for a Question report. Adds the 'question' and 'next_meeting' fields.""" config = load_config(config_path) config['question'] = _get_int(config['_raw'], 'question', "question") # Optional next_meeting date for filtering candidate work items next_meeting_str = config['_raw'].get('next_meeting') if next_meeting_str: config['next_meeting'] = _parse_date(next_meeting_str, "next_meeting") else: config['next_meeting'] = None return config def load_wp_config(config_path): """Load config for a Working Party report. Adds the 'workingParty' and 'next_meeting' fields.""" config = load_config(config_path) config['workingParty'] = _get_int(config['_raw'], 'workingParty', "workingParty") # Optional next_meeting date for filtering candidate work items next_meeting_str = config['_raw'].get('next_meeting') if next_meeting_str: config['next_meeting'] = _parse_date(next_meeting_str, "next_meeting") else: config['next_meeting'] = None return config def _get_int(content, key, label): """Extract an integer value from config.""" val = content.get(key) if val is None: print(f"Missing required field: {label}") sys.exit(1) try: return int(val) except (ValueError, TypeError): print(f"Invalid {label}: {val}") sys.exit(1) def _parse_date(date_string, label): """Parse a YYYY/MM/DD date string.""" try: return datetime.datetime.strptime(date_string, "%Y/%m/%d") except Exception: print(f"Invalid {label}: {date_string}") sys.exit(1)