"""큐레이터용 후보 가공 — 여러 엔진 결과를 하나로 병합, 중복 제거, 피처 계산.""" from typing import Dict, List, Any, Set from . import db from .recommender import recommend_numbers, recommend_with_heatmap from .analyzer import get_statistical_report from .strategy_evolver import generate_smart_recommendation LOW_HIGH_CUT = 22 def compute_features(numbers: List[int], hot: Set[int], cold: Set[int]) -> Dict[str, Any]: nums = sorted(numbers) odd = sum(1 for n in nums if n % 2 == 1) low = sum(1 for n in nums if n <= LOW_HIGH_CUT) buckets = [0, 0, 0, 0, 0] for n in nums: if n <= 10: buckets[0] += 1 elif n <= 20: buckets[1] += 1 elif n <= 30: buckets[2] += 1 elif n <= 40: buckets[3] += 1 else: buckets[4] += 1 consecutive = any(nums[i+1] - nums[i] == 1 for i in range(len(nums) - 1)) return { "odd_count": odd, "even_count": 6 - odd, "low_count": low, "high_count": 6 - low, "range_distribution": buckets, "has_consecutive": consecutive, "hot_number_count": len(set(nums) & hot), "cold_number_count": len(set(nums) & cold), "sum": sum(nums), } def _key(numbers: List[int]) -> str: return ",".join(str(n) for n in sorted(numbers)) def collect_candidates(n: int, hot: Set[int], cold: Set[int]) -> List[Dict[str, Any]]: """우선순위: simulation best_picks → meta → heatmap → statistics. 중복 제거 후 최대 n세트.""" seen: Dict[str, Dict[str, Any]] = {} order: List[str] = [] def _add(numbers: List[int], source: str) -> None: if not numbers: return k = _key(numbers) if k in seen: return seen[k] = {"numbers": sorted(numbers), "source": source} order.append(k) # 1. simulation best_picks try: for row in db.get_best_picks(limit=n): _add(row.get("numbers") or [], "simulation") except Exception: pass # draws는 한 번만 로드 draws = [] try: draws = db.get_all_draw_numbers() except Exception: pass # 2. meta-strategy (smart) try: meta = generate_smart_recommendation(sets=n) for s in meta.get("sets", []): _add(s.get("numbers") or [], "meta") except Exception: pass # 3. heatmap (n번 호출, 중복 회피) if draws: try: for _ in range(n * 2): if len(order) >= n * 2: break r = recommend_with_heatmap(draws, []) _add(r.get("numbers") or [], "heatmap") except Exception: pass # 4. statistics if draws: try: for _ in range(n * 2): if len(order) >= n * 2: break r = recommend_numbers(draws) _add(r.get("numbers") or [], "statistics") except Exception: pass out = [] for k in order[:n]: item = seen[k] item["features"] = compute_features(item["numbers"], hot, cold) out.append(item) return out def build_context(hot_limit: int = 10, cold_limit: int = 10) -> Dict[str, Any]: """주간 맥락 패키지 — get_statistical_report가 이미 hot/cold를 제공.""" hot: List[int] = [] cold: List[int] = [] last_summary = "" try: draws = db.get_all_draw_numbers() except Exception: draws = [] if draws: try: report = get_statistical_report(draws) hot = list(report.get("hot_numbers", []))[:hot_limit] cold = list(report.get("cold_numbers", []))[:cold_limit] except Exception: pass try: latest = db.get_latest_draw() except Exception: latest = None if latest: nums = [latest.get(f"n{i}") for i in range(1, 7)] nums = [n for n in nums if n is not None] if nums: odd = sum(1 for n in nums if n % 2 == 1) low = sum(1 for n in nums if n <= LOW_HIGH_CUT) last_summary = f"{latest.get('drw_no')}회: {', '.join(str(n) for n in nums)} (홀{odd}짝{6-odd}, 저{low}고{6-low})" my_perf: List[Dict[str, Any]] = [] try: from .purchase_manager import get_recent_performance my_perf = get_recent_performance(limit=3) except Exception: my_perf = [] return { "hot_numbers": hot, "cold_numbers": cold, "last_draw_summary": last_summary, "my_recent_performance": my_perf, }