feat(scoring): flexible source fallback (no board crash) + per-source calc audit (Q2, Q3)

Q2 flexible scoring: _fetch_with_cache now degrades instead of raising
DashboardError — a source that fails with no cached value returns {} so the
theme scorer drops that source's factors; a previously-good value is kept as
stale by the daily cache. Verified: all-sources-down still builds 13 themes.

Q3 per-source audit: new themes.factor_source_breakdown(fetched, theme) shows
per factor source/raw/normalized/weight/contribution; dashboard exposes
fetch_data + factor_sources; per-symbol modal renders symbolDetail.factor_sources
(e.g. retail: te_thailand ยอดขายปลีก -14.5 -> -1.0 x 0.7 = -0.7).

Suite 369 green; independent review passed: true.
Q1 (HAR for deferred sources) spike recorded: method works, REIC needs deeper
interaction; NBTC 403 likely unbpassable without a session.
This commit is contained in:
Kunthawat Greethong
2026-08-29 12:15:54 +07:00
parent 51400f20c7
commit 7f175e5a05
10 changed files with 216 additions and 20 deletions

View File

@@ -65,6 +65,26 @@ Deferred (feasibility blocked): REIC (JS SPA/XHR), EPPO (JS/WordPress), NBTC (40
anti-bot), PTTEP (JS shell), PTT/BCP (404/DNS). Need browser/XHR approach, not
plain-HTML — recorded in the plan as a separate effort.
## Phase D (same day — 3 follow-up asks)
1. **Flexible scoring (Q2)**: `_fetch_with_cache` now degrades instead of raising
`DashboardError` — source down + no cache = return {} (theme drops that
source's factors); previously-good value present = daily cache returns stale.
Board no longer crashes on any single upstream failure (verified: all-sources-
down still builds 13 themes). `DashboardError` class now unused by build().
2. **Per-source calc detail (Q3)**: new `themes.factor_source_breakdown(fetched,
theme)` surfaced in dashboard as `fetch_data` + `factor_sources`, and in the
per-symbol modal as `symbolDetail.factor_sources` — the owner sees, per theme,
each factor's source → raw → normalized → weight → contribution (e.g. retail:
te_thailand ยอดขายปลีก -14.5 → -1.0 × 0.7 = -0.7). Backend test added.
3. **HAR feasibility for deferred sources (Q1)**: captured REIC via
`har-derived-api-client` (Playwright drive → HAR → derived XHR endpoint
`POST /Home/Web_All_Num_View`). Spike shows the method WORKS (browser drives
the JS SPA, XHR endpoint derivable) BUT the homepage XHR returned an empty
body — actual property data needs a deeper interaction (a real Transfer page
click), so a full REIC collector is a larger follow-up, not a quick add.
Finishing: commit Q2+Q3 with suite 369 green; Q1 recorded as feasible-but-
needs-deeper-capture and left as a decision for the owner.
- Note: a **concurrent process** also landed `thai_trade.py` (external-sector
exports/imports/current-account) and external_* factors mid-session; its 3
initially-broken tests were fixed to reach the 360-green baseline here.