A wrapper around metadata dictionary with convenient access methods
| 214 | |
| 215 | |
| 216 | class Metadata: |
| 217 | """A wrapper around metadata dictionary with convenient access methods""" |
| 218 | |
| 219 | def __init__(self, data: dict[str, Any] | None = None): |
| 220 | self._data = data or {} |
| 221 | |
| 222 | def get_path(self, path: str, default: Any = None) -> Any: |
| 223 | """Get value from nested dictionary using dot notation path |
| 224 | |
| 225 | Args: |
| 226 | path: Dot-separated path like "config.tournament.rounds" |
| 227 | default: Default value if path doesn't exist |
| 228 | |
| 229 | Returns: |
| 230 | Value at path or default |
| 231 | """ |
| 232 | current = self._data |
| 233 | for key in path.split("."): |
| 234 | if isinstance(current, dict) and key in current: |
| 235 | current = current[key] |
| 236 | else: |
| 237 | return default |
| 238 | return current |
| 239 | |
| 240 | @property |
| 241 | def is_valid(self) -> bool: |
| 242 | """Check if metadata was loaded successfully""" |
| 243 | return bool(self._data) |
| 244 | |
| 245 | @property |
| 246 | def total_rounds(self) -> int | None: |
| 247 | """Get total number of rounds from config""" |
| 248 | return self.get_path("config.tournament.rounds") |
| 249 | |
| 250 | @property |
| 251 | def completed_rounds(self) -> int: |
| 252 | """Get number of completed rounds (excluding round 0 warmup)""" |
| 253 | round_stats = self.get_path("round_stats", {}) |
| 254 | return sum(1 for round_key in round_stats.keys() if int(round_key) > 0) |
| 255 | |
| 256 | @property |
| 257 | def round_count_info(self) -> tuple[int, int] | None: |
| 258 | """Get (completed_rounds, total_rounds) tuple""" |
| 259 | total = self.total_rounds |
| 260 | if total is not None: |
| 261 | return (self.completed_rounds, total) |
| 262 | return None |
| 263 | |
| 264 | @property |
| 265 | def models(self) -> list[str]: |
| 266 | """Get list of unique model names from players config""" |
| 267 | players_config = self.get_path("config.players", []) |
| 268 | models = [] |
| 269 | for player_config in players_config: |
| 270 | if isinstance(player_config, dict): |
| 271 | model_name = self.get_path_from_dict(player_config, "config.model.model_name") |
| 272 | if model_name and model_name not in models: |
| 273 | models.append(model_name) |