scorecardapi / rule_engine.py
zombee11's picture
Upload 34 files
7872230 verified
Raw
History Blame Contribute Delete
1.81 kB
from dataclasses import dataclass, field
from decimal import Decimal
from app.core.enums import RuleType
from app.models.rule import RuleMaster
from app.services.operators import evaluate_operator
@dataclass
class RuleEvaluation:
matched: list[RuleMaster] = field(default_factory=list)
skipped: list[str] = field(default_factory=list)
@property
def codes(self) -> list[str]:
return [rule.rule_code for rule in self.matched]
@property
def reason_codes(self) -> list[str]:
return [rule.reason_code for rule in self.matched]
class RuleEngine:
def evaluate(self, rules: list[RuleMaster], context: dict) -> RuleEvaluation:
result = RuleEvaluation()
for rule in sorted(rules, key=lambda item: item.priority):
try:
if evaluate_operator(context.get(rule.field_name), rule.operator, rule.field_value):
result.matched.append(rule)
else:
result.skipped.append(rule.rule_code)
except ValueError:
result.skipped.append(rule.rule_code)
return result
def score(self, rules: list[RuleMaster], context: dict) -> tuple[Decimal, dict[str, Decimal], RuleEvaluation]:
score_rules = [rule for rule in rules if rule.rule_type == RuleType.SCORE]
evaluation = self.evaluate(score_rules, context)
total = Decimal("0")
breakup: dict[str, Decimal] = {}
for rule in evaluation.matched:
value = Decimal(rule.score_value or 0)
component = rule.score_component or "other"
breakup[component] = breakup.get(component, Decimal("0")) + value
total += value
capped = max(Decimal("0"), min(Decimal("100"), total))
return capped, breakup, evaluation