PiedPiper / backend /scoring.py
RajatA98's picture
Initial PiedPiper Space build - Dockerfile + backend + 160-track catalog
fe19082 verified
Raw
History Blame Contribute Delete
3.32 kB
"""Bit-equal port of `quality-scorer/src/lib/scoring.js`.
`compute_report(raw)` is the single source of truth for both the offline ingest
pipeline and the live `/analyze` endpoint. Parity vs the JS twin is asserted by
`tests/test_scoring_parity.py`.
"""
from __future__ import annotations
import math
from .signals import SIGNALS, clamp, evaluate_signal
# Critical signals weight hardest; a broken track passing is the costly error.
WEIGHT: dict[str, float] = {
"silence": 1.0,
"clipping": 1.0,
"noise": 0.9,
"truncation": 0.85,
"channel": 0.7,
"duration": 0.6,
"dynamics": 0.4,
}
FAIL_PHRASE: dict[str, str] = {
"silence": "mostly dead air",
"clipping": "hard clipping",
"noise": "noise-dominated spectrum",
"truncation": "cut off mid-phrase",
"duration": "length out of range",
"channel": "collapsed stereo channel",
"dynamics": "no dynamic range",
}
def _js_round(x: float) -> int:
"""JavaScript `Math.round` semantics: half rounds toward +∞.
Python's built-in `round()` uses banker's rounding (half-to-even), which
diverges on .5 boundaries. Composite scores are derived from continuous
severity sums so .5 is rare, but parity demands the JS rule.
"""
return math.floor(x + 0.5)
def compute_report(raw: dict) -> dict:
"""Map raw signal values β†’ the full Track-report shape consumed by the UI.
Verdict is precision-first: any CRITICAL signal failing β†’ DROP, regardless
of the composite score. Dynamics can fail without forcing a drop.
"""
signals: list[dict] = []
for s in SIGNALS:
ev = evaluate_signal(s["id"], raw[s["id"]])
signals.append({
"id": s["id"],
"label": s["label"],
"short": s["short"],
"critical": s["critical"],
"threshold": s["threshold"],
"blurb": s["blurb"],
"value": raw[s["id"]],
**ev,
})
penalty = 0.0
for s in signals:
penalty += (s["severity"] ** 1.4) * WEIGHT.get(s["id"], 0.5) * 27
score = _js_round(clamp(100 - penalty, 0, 100))
failed = [s for s in signals if s["status"] == "fail"]
critical_fails = sorted(
(s for s in failed if s["critical"]),
key=lambda x: x["severity"],
reverse=True,
)
verdict = "drop" if critical_fails else "keep"
primary_fail = critical_fails[0]["id"] if verdict == "drop" else None
if verdict == "drop":
w = critical_fails[0]
extra = f" Β· +{len(critical_fails) - 1} more" if len(critical_fails) > 1 else ""
reason = (
f"Dropped β€” {FAIL_PHRASE[w['id']]} "
f"({w['label'].lower()} {w['display']}){extra}."
)
else:
warns = [s for s in signals if s["status"] == "warn"]
if warns:
plural = "s" if len(warns) > 1 else ""
reason = (
f"Kept β€” within bounds; {len(warns)} signal{plural} "
"flagged for review."
)
else:
reason = "Kept β€” all technical signals within bounds."
return {
"score": score,
"verdict": verdict,
"primaryFail": primary_fail,
"reason": reason,
"signals": signals,
"failModes": [s["id"] for s in failed],
}