Spaces:
Runtime error
Runtime error
| import json | |
| from decimal import Decimal | |
| from typing import Any | |
| from redis import Redis | |
| from sqlalchemy import and_, or_, select | |
| from sqlalchemy.orm import Session | |
| from app.core.config import get_settings | |
| from app.models.rule import RuleMaster | |
| def _serialize_rule(rule: RuleMaster) -> dict[str, Any]: | |
| return { | |
| "id": rule.id, | |
| "tenant_id": rule.tenant_id, | |
| "product_type": rule.product_type, | |
| "dealer_id": rule.dealer_id, | |
| "rule_code": rule.rule_code, | |
| "rule_name": rule.rule_name, | |
| "rule_type": rule.rule_type, | |
| "field_name": rule.field_name, | |
| "operator": rule.operator, | |
| "field_value": rule.field_value, | |
| "score_value": str(rule.score_value) if rule.score_value is not None else None, | |
| "score_component": rule.score_component, | |
| "decision_action": rule.decision_action, | |
| "reason_code": rule.reason_code, | |
| "priority": rule.priority, | |
| "version": rule.version, | |
| "is_active": rule.is_active, | |
| "created_by": rule.created_by, | |
| "created_at": rule.created_at.isoformat(), | |
| "updated_at": rule.updated_at.isoformat(), | |
| } | |
| def _hydrate_rule(data: dict[str, Any]) -> RuleMaster: | |
| rule = RuleMaster(**{k: v for k, v in data.items() if k not in {"created_at", "updated_at"}}) | |
| if data.get("score_value") is not None: | |
| rule.score_value = Decimal(data["score_value"]) | |
| return rule | |
| class RuleRepository: | |
| def __init__(self) -> None: | |
| self.settings = get_settings() | |
| self.redis: Redis | None = None | |
| if self.settings.redis_url: | |
| try: | |
| self.redis = Redis.from_url(self.settings.redis_url, decode_responses=True) | |
| except Exception: | |
| self.redis = None | |
| def cache_key(self, tenant_id: str, product_type: str, dealer_id: str | None) -> str: | |
| dealer = dealer_id or "ALL" | |
| return f"rules:{tenant_id}:{product_type}:{dealer}" | |
| def get_active_rules( | |
| self, db: Session, tenant_id: str, product_type: str, dealer_id: str | None | |
| ) -> list[RuleMaster]: | |
| key = self.cache_key(tenant_id, product_type, dealer_id) | |
| if self.redis: | |
| try: | |
| cached = self.redis.get(key) | |
| if cached: | |
| return [_hydrate_rule(item) for item in json.loads(cached)] | |
| except Exception: | |
| pass | |
| stmt = ( | |
| select(RuleMaster) | |
| .where( | |
| RuleMaster.is_active.is_(True), | |
| RuleMaster.tenant_id == tenant_id, | |
| RuleMaster.product_type == product_type, | |
| or_(RuleMaster.dealer_id.is_(None), RuleMaster.dealer_id == dealer_id), | |
| ) | |
| .order_by(RuleMaster.priority.asc(), RuleMaster.id.asc()) | |
| ) | |
| rules = list(db.scalars(stmt).all()) | |
| if self.redis: | |
| try: | |
| self.redis.setex(key, 300, json.dumps([_serialize_rule(rule) for rule in rules])) | |
| except Exception: | |
| pass | |
| return rules | |
| def invalidate(self, tenant_id: str, product_type: str, dealer_id: str | None = None) -> None: | |
| if not self.redis: | |
| return | |
| keys = [ | |
| self.cache_key(tenant_id, product_type, dealer_id), | |
| self.cache_key(tenant_id, product_type, None), | |
| ] | |
| try: | |
| self.redis.delete(*set(keys)) | |
| except Exception: | |
| pass | |
| def list_rules( | |
| self, | |
| db: Session, | |
| tenant_id: str | None = None, | |
| product_type: str | None = None, | |
| dealer_id: str | None = None, | |
| rule_type: str | None = None, | |
| is_active: bool | None = None, | |
| ) -> list[RuleMaster]: | |
| filters = [] | |
| if tenant_id: | |
| filters.append(RuleMaster.tenant_id == tenant_id) | |
| if product_type: | |
| filters.append(RuleMaster.product_type == product_type) | |
| if dealer_id: | |
| filters.append(RuleMaster.dealer_id == dealer_id) | |
| if rule_type: | |
| filters.append(RuleMaster.rule_type == rule_type) | |
| if is_active is not None: | |
| filters.append(RuleMaster.is_active.is_(is_active)) | |
| stmt = select(RuleMaster).order_by(RuleMaster.priority.asc(), RuleMaster.id.asc()) | |
| if filters: | |
| stmt = stmt.where(and_(*filters)) | |
| return list(db.scalars(stmt).all()) | |