"""Generate an example config.json showing all available options with their defaults. Produces a merged document: dataclass defaults provide the full key schema, the bundled config.json supplies the real values (wins on conflict). Usage: python scripts/gen_example_config.py python scripts/gen_example_config.py > docs/example_config.json """ import dataclasses import json import sys from importlib.resources import files sys.path.insert(0, str(__import__('pathlib').Path(__file__).parent.parent)) from room_cal._utils import CalendarEntry, Config, ServiceConfig, _deep_merge def _strip_none(obj): """Recursively remove keys whose value is None.""" if isinstance(obj, dict): return {k: _strip_none(v) for k, v in obj.items() if v is not None} if isinstance(obj, list): return [_strip_none(i) for i in obj] return obj def _defaults_dict() -> dict: raw = dataclasses.asdict(Config()) raw['global'] = raw.pop('global_') raw.pop('api_url', None) raw.pop('services', None) raw.pop('calendars', None) raw['rules'].pop('pipelines', None) return _strip_none(raw) def _expand_service(key: str, d: dict) -> dict: """Round-trip a service dict through ServiceConfig so all dataclass fields appear.""" full = dataclasses.asdict(ServiceConfig.from_dict(key, d)) full.pop('key', None) return _strip_none(full) def generate() -> str: defaults = _defaults_dict() stock = json.loads(files('room_cal').joinpath('config.json').read_text(encoding='utf-8')) merged = _deep_merge(defaults, stock) merged['services'] = {k: _expand_service(k, v) for k, v in merged.get('services', {}).items()} merged['calendars'] = [dataclasses.asdict(CalendarEntry.from_dict(e)) for e in merged.get('calendars', [])] return json.dumps(merged, indent=2) def main(): print(generate()) if __name__ == '__main__': main()