43 lines
1.5 KiB
Python
43 lines
1.5 KiB
Python
from decimal import Decimal
|
|
|
|
from app.domain.enums import Classification, Eligibility
|
|
from app.domain.models import ScoringModel
|
|
from app.scoring.mathutil import clamp
|
|
|
|
|
|
def classify_deal_score(score: Decimal, model: ScoringModel) -> str:
|
|
for bound in model.classification_upper_bounds:
|
|
if bound.inclusive and score <= bound.upper:
|
|
return bound.label
|
|
if not bound.inclusive and score < bound.upper:
|
|
return bound.label
|
|
return Classification.EXCEPTIONAL.value
|
|
|
|
|
|
def score_deal(product_score: Decimal, price_score: Decimal, model: ScoringModel) -> Decimal:
|
|
return clamp(model.product_weight * product_score + model.price_weight * price_score)
|
|
|
|
|
|
def eligibility_from_parts(product: Eligibility, offer: Eligibility) -> Eligibility:
|
|
if Eligibility.INELIGIBLE in {product, offer}:
|
|
return Eligibility.INELIGIBLE
|
|
if Eligibility.UNKNOWN in {product, offer}:
|
|
return Eligibility.UNKNOWN
|
|
return Eligibility.ELIGIBLE
|
|
|
|
|
|
def derive_eligible(overall: Eligibility) -> bool | None:
|
|
if overall == Eligibility.ELIGIBLE:
|
|
return True
|
|
if overall == Eligibility.INELIGIBLE:
|
|
return False
|
|
return None
|
|
|
|
|
|
def classification_for(overall: Eligibility, deal_score: Decimal | None, model: ScoringModel) -> str:
|
|
if overall == Eligibility.INELIGIBLE:
|
|
return Classification.INELIGIBLE.value
|
|
if overall == Eligibility.UNKNOWN or deal_score is None:
|
|
return Classification.INSUFFICIENT_DATA.value
|
|
return classify_deal_score(deal_score, model)
|