solarfit-api / api /scoring.py
LIM
perf: reduce MAX_BASE_TILES=8, mini-batch inference
0939b04
Raw
History Blame Contribute Delete
2.35 kB
"""규칙 기반 종합 점수 (0-100) 산출."""
from __future__ import annotations
from api.models import AnalysisResult, ScoreResult
_WEIGHTS: dict[str, float] = {
"ghi": 0.25,
"slope": 0.20,
"south_facing": 0.10,
"land_price": 0.15,
"substation": 0.20,
"area": 0.10,
}
def _ghi_score(ghi: float | None) -> float:
if ghi is None:
return 50.0
return max(0.0, min(100.0, (ghi - 1200) / 3.0))
def _slope_score(slope: float | None) -> float:
if slope is None:
return 50.0
return max(0.0, min(100.0, 100.0 - slope / 15.0 * 100.0))
def _south_facing_score(is_south: bool | None) -> float:
if is_south is None:
return 50.0
return 100.0 if is_south else 0.0
def _price_score(price: float | None) -> float:
if price is None:
return 50.0
return max(0.0, min(100.0, 100.0 - price / 10_000.0))
def _substation_score(dist_km: float | None) -> float:
if dist_km is None:
return 30.0
return max(0.0, min(100.0, 100.0 - (dist_km - 1.0) / 9.0 * 100.0))
def _area_score(area_m2: float | None) -> float:
if area_m2 is None:
return 50.0
return max(0.0, min(100.0, area_m2 / 30_000.0 * 100.0))
def calculate_score(result: AnalysisResult) -> ScoreResult:
passed = not result.regulatory.any_blocked
if result.terrain.slope_deg is not None and result.terrain.slope_deg > 15:
passed = False
breakdown = {
"ghi": _ghi_score(result.annual_ghi_kwh),
"slope": _slope_score(result.terrain.slope_deg),
"south_facing": _south_facing_score(result.terrain.is_south_facing),
"land_price": _price_score(result.land_value.official_price_per_m2),
"substation": _substation_score(result.substation.distance_km),
"area": _area_score(result.parcel.area_m2),
}
total = sum(breakdown[k] * _WEIGHTS[k] for k in breakdown)
if not passed:
total = min(total, 20.0)
total_int = round(total)
grade = (
"A" if total_int >= 80
else "B" if total_int >= 65
else "C" if total_int >= 50
else "D" if total_int >= 35
else "F"
)
return ScoreResult(
total=total_int,
grade=grade,
passed_hard_filter=passed,
breakdown=breakdown,
)