Spaces:
Running
Running
fix(NEW-A3 part1): replace em-dashes/smart-quotes in 56+ Python files -- CI requires pure ASCII source
8a0fba4 | import os, sys, json | |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) | |
| from datetime import datetime | |
| from loguru import logger | |
| WEIGHTS_FILE = os.path.join( | |
| os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), | |
| "data", "processed", "indicator_weights.json" | |
| ) | |
| DEFAULT_WEIGHTS = { | |
| "politician_company_overlap": 0.35, | |
| "contract_concentration": 0.25, | |
| "audit_mention_frequency": 0.20, | |
| "asset_growth_anomaly": 0.15, | |
| "criminal_case_presence": 0.05, | |
| } | |
| MIN_CONFIRMATIONS = 3 | |
| DELTA_INCREASE = 0.01 | |
| DELTA_DECREASE = 0.005 | |
| class WeightOptimizer: | |
| def __init__(self): | |
| self.weights = self._load_weights() | |
| self.outcomes = self._load_outcomes() | |
| def _load_weights(self) -> dict: | |
| if os.path.exists(WEIGHTS_FILE): | |
| try: | |
| data = json.loads(open(WEIGHTS_FILE, encoding="utf-8").read()) | |
| return data.get("weights", DEFAULT_WEIGHTS.copy()) | |
| except Exception: | |
| pass | |
| return DEFAULT_WEIGHTS.copy() | |
| def _load_outcomes(self) -> list: | |
| if os.path.exists(WEIGHTS_FILE): | |
| try: | |
| data = json.loads(open(WEIGHTS_FILE, encoding="utf-8").read()) | |
| return data.get("outcomes", []) | |
| except Exception: | |
| pass | |
| return [] | |
| def record_outcome(self, entity_id: str, indicator_fired: list[str], | |
| confirmed: bool) -> None: | |
| self.outcomes.append({ | |
| "entity_id": entity_id, | |
| "indicator_fired":indicator_fired, | |
| "confirmed": confirmed, | |
| "recorded_at": datetime.now().isoformat(), | |
| }) | |
| self._save() | |
| logger.info( | |
| f"[WeightOptimizer] Outcome recorded: {entity_id} " | |
| f"confirmed={confirmed} indicators={indicator_fired}" | |
| ) | |
| def optimize(self) -> dict: | |
| confirmed = [o for o in self.outcomes if o["confirmed"]] | |
| unconfirmed = [o for o in self.outcomes if not o["confirmed"]] | |
| if len(confirmed) < MIN_CONFIRMATIONS: | |
| logger.info( | |
| f"[WeightOptimizer] Only {len(confirmed)} confirmed outcomes. " | |
| f"Need {MIN_CONFIRMATIONS} before adjusting weights." | |
| ) | |
| return {"adjusted": False, "reason": "insufficient_confirmations", | |
| "confirmed_count": len(confirmed)} | |
| changes = {} | |
| for indicator in DEFAULT_WEIGHTS: | |
| fired_confirmed = sum(1 for o in confirmed | |
| if indicator in o.get("indicator_fired", [])) | |
| fired_unconfirmed = sum(1 for o in unconfirmed | |
| if indicator in o.get("indicator_fired", [])) | |
| old_weight = self.weights.get(indicator, DEFAULT_WEIGHTS[indicator]) | |
| if fired_confirmed > fired_unconfirmed: | |
| new_weight = min(0.50, old_weight + DELTA_INCREASE) | |
| elif fired_unconfirmed > fired_confirmed: | |
| new_weight = max(0.01, old_weight - DELTA_DECREASE) | |
| else: | |
| new_weight = old_weight | |
| if abs(new_weight - old_weight) > 0.0001: | |
| changes[indicator] = { | |
| "old": round(old_weight, 4), | |
| "new": round(new_weight, 4), | |
| "delta": round(new_weight - old_weight, 4), | |
| } | |
| self.weights[indicator] = new_weight | |
| total = sum(self.weights.values()) | |
| if total > 0: | |
| self.weights = {k: round(v/total, 4) for k,v in self.weights.items()} | |
| self._save() | |
| logger.success( | |
| f"[WeightOptimizer] Weights adjusted: {len(changes)} changes. " | |
| f"Pending human approval." | |
| ) | |
| return { | |
| "adjusted": len(changes) > 0, | |
| "changes": changes, | |
| "new_weights": self.weights, | |
| "confirmed_cases": len(confirmed), | |
| "optimized_at": datetime.now().isoformat(), | |
| "note": "Changes require human approval before deployment.", | |
| } | |
| def _save(self): | |
| os.makedirs(os.path.dirname(WEIGHTS_FILE), exist_ok=True) | |
| with open(WEIGHTS_FILE, "w", encoding="utf-8") as f: | |
| json.dump({ | |
| "weights": self.weights, | |
| "outcomes": self.outcomes, | |
| "last_updated": datetime.now().isoformat(), | |
| }, f, indent=2, ensure_ascii=False) | |
| if __name__ == "__main__": | |
| print("=" * 55) | |
| print("BharatGraph - Weight Optimizer Test") | |
| print("=" * 55) | |
| opt = WeightOptimizer() | |
| print(f"\n Current weights:") | |
| for k, v in opt.weights.items(): | |
| print(f" {k}: {v}") | |
| for i in range(4): | |
| opt.record_outcome( | |
| f"test_entity_{i:03d}", | |
| ["politician_company_overlap", "contract_concentration"], | |
| confirmed=True | |
| ) | |
| opt.record_outcome("test_entity_004", | |
| ["asset_growth_anomaly"], confirmed=False) | |
| result = opt.optimize() | |
| print(f"\n Adjusted: {result['adjusted']}") | |
| print(f" Confirmed cases: {result['confirmed_cases']}") | |
| if result.get("changes"): | |
| for k, v in result["changes"].items(): | |
| print(f" {k}: {v['old']} -> {v['new']} ({v['delta']:+.4f})") | |
| print("\nDone!") | |