from pathlib import Path import json import os import sys from typing import Iterable, Optional from sqlalchemy.exc import SQLAlchemyError ROOT = Path(__file__).resolve().parents[1] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) from src.services.training import CaseGenerationResult, TrainingCaseGenerator def run_training_generation( generator: TrainingCaseGenerator, instrument_id: int = 1, timeframe: str = "1m", case_types: Optional[Iterable[str]] = None, limit_per_type: int = 10, output_json: bool = False, ) -> int: case_type_list = list(case_types) if case_types is not None else ["sweep", "fvg", "range", "mss_displacement"] result = generator.generate_cases( instrument_id=instrument_id, timeframe=timeframe, case_types=case_type_list, limit_per_type=limit_per_type, ) payload = _generation_payload( result=result, instrument_id=instrument_id, timeframe=timeframe, limit_per_type=limit_per_type, case_types=case_type_list, ) if output_json: print(json.dumps(payload, default=str, ensure_ascii=False, sort_keys=True)) else: print(f"Training cases generated: {result.generated}") print(f"Training case ids: {', '.join(str(case_id) for case_id in result.case_ids) or 'none'}") print(f"Training cases by type: {json.dumps(result.by_type, ensure_ascii=False, sort_keys=True)}") return 0 def main(env=None) -> int: env = os.environ if env is None else env output_json = _get_bool(env, "TRAINING_CASES_OUTPUT_JSON", False) instrument_id = _get_int(env, "TRAINING_INSTRUMENT_ID", 1) timeframe = str(env.get("TRAINING_TIMEFRAME") or "1m") limit_per_type = _get_int(env, "TRAINING_LIMIT_PER_TYPE", 10) case_types = _get_case_types(env, "TRAINING_CASE_TYPES") try: return run_training_generation( generator=TrainingCaseGenerator(), instrument_id=instrument_id, timeframe=timeframe, case_types=case_types, limit_per_type=limit_per_type, output_json=output_json, ) except SQLAlchemyError as exc: payload = { "passed": False, "instrument_id": instrument_id, "timeframe": timeframe, "limit_per_type": limit_per_type, "case_types": case_types or ["sweep", "fvg", "range", "mss_displacement"], "generated": 0, "by_type": {}, "case_ids": [], "error_type": exc.__class__.__name__, "error": str(exc), } if output_json: print(json.dumps(payload, default=str, ensure_ascii=False, sort_keys=True)) else: print(f"Database unavailable for training case generation: {exc.__class__.__name__}") return 2 def _generation_payload( result: CaseGenerationResult, instrument_id: int, timeframe: str, limit_per_type: int, case_types: list[str], ) -> dict: return { "passed": True, "instrument_id": instrument_id, "timeframe": timeframe, "limit_per_type": limit_per_type, "case_types": case_types, "generated": result.generated, "by_type": result.by_type, "case_ids": result.case_ids, } def _get_int(env, key: str, default: int) -> int: value = env.get(key) if value is None or value == "": return default return int(value) def _get_bool(env, key: str, default: bool) -> bool: value = env.get(key) if value is None or value == "": return default return str(value).strip().lower() in {"1", "true", "yes", "on"} def _get_case_types(env, key: str) -> Optional[list[str]]: value = env.get(key) if value is None or str(value).strip() == "": return None case_types = [part.strip() for part in str(value).split(",") if part.strip()] return case_types or None if __name__ == "__main__": raise SystemExit(main())