"""Validation and override conversion for GUI experiment fields.""" from __future__ import annotations from pathlib import Path from typing import Any from .gui_models import GuiExperimentRequest, GuiValidationResult def validate_experiment_request(request: GuiExperimentRequest) -> GuiValidationResult: errors: list[str] = []; warnings: list[str] = [] if request.input_type not in {"camera", "video"}: errors.append("Input type must be camera or video") if request.camera_index < 0: errors.append("Camera index must be zero or greater") if request.input_type == "video" and (not request.video_path or not Path(request.video_path).is_file()): errors.append("Select an existing video file") if request.voice_mode not in {"prompt", "control", "disabled"}: errors.append("Voice mode is invalid") if not 0.0 <= request.voice_volume <= 1.0: errors.append("Volume must be between 0.0 and 1.0") if request.face_every is not None and request.face_every < 1: errors.append("Face interval must be at least 1") if request.max_face_persons is not None and request.max_face_persons < 1: errors.append("Maximum face persons must be at least 1") if request.signage_yaw_direction not in {None, "positive", "negative"}: errors.append("Signage yaw direction is invalid") if request.voice_mode == "prompt" and (not request.audio_file or not Path(request.audio_file).is_file()): warnings.append("Prompt mode has no existing audio file") if not Path(request.config_path).is_file(): errors.append("Configuration file does not exist") return GuiValidationResult(tuple(errors), tuple(warnings)) def experiment_overrides(request: GuiExperimentRequest) -> dict[str, Any]: values: dict[str, Any] = { "input": {"type": request.input_type, "camera_id": request.camera_index, "video_path": request.video_path if request.input_type == "video" else None}, "display": {"enabled": request.show_opencv_window}, "voice_prompt": {"enabled": request.voice_mode != "disabled", "mode": request.voice_mode}, "audio": {"volume": request.voice_volume}, "unified_logging": {"enabled": request.enable_unified_logging, "camera_position_note": request.camera_position_note}, } if request.audio_file: values["audio"]["file_path"] = request.audio_file if request.face_every is not None: values.setdefault("face", {})["process_every_n_frames"] = request.face_every if request.max_face_persons is not None: values.setdefault("face", {})["max_persons_per_frame"] = request.max_face_persons if request.signage_yaw_direction: values.setdefault("turn_detection", {})["signage_yaw_direction"] = request.signage_yaw_direction return values class ConfigPanelLogic: """Compatibility facade used by tests and a future richer config editor.""" validate = staticmethod(validate_experiment_request) to_overrides = staticmethod(experiment_overrides)