From 2da73b8a8c227d71e05037ff2bb8f77067e2273b Mon Sep 17 00:00:00 2001 From: Kunthawat Greethong Date: Wed, 26 Aug 2026 15:55:30 +0700 Subject: [PATCH] [verified] Sources table auto-derived from FACTORS registry + next-update column MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _build_sources now iterates FACTORS registry (grouped by fetch module) instead of hardcoded 5-row list -> adding a FACTOR auto-appends its source row (single source of truth, answers 'เพิ่มแหล่งควรอัตโนมัติ') - Each source row gains ความถี่ + อัปเดตครั้งต่อไป (from frequency cadence) - Frontend sources table: 6 columns (ข้อมูล/แหล่ง/ช่วงข้อมูล/ความถี่/อัปเดตครั้งต่อไป/อัปเดตล่าสุด), all from /api/v1/dashboard (not mock) - Verified live: 5 sources auto-derived w/ next-update; browser shows 6 cols --- backend/app/dashboard.py | 90 +++++++++++++++++++++++++++++++++++----- frontend/src/App.vue | 6 ++- 2 files changed, 83 insertions(+), 13 deletions(-) diff --git a/backend/app/dashboard.py b/backend/app/dashboard.py index 08ac419..5bec732 100644 --- a/backend/app/dashboard.py +++ b/backend/app/dashboard.py @@ -332,18 +332,86 @@ class RealDashboard: return board def _build_sources(self, auto_d, npl_d, en_d, macro_d, tourism) -> list: + """Sources derived from the FACTORS registry — adding a factor to + factors.py auto-appends its source row here (no hardcoded list).""" import datetime as _dt + from app import factors as factors_mod now = _dt.datetime.now(_dt.timezone.utc).isoformat(timespec="minutes") - rows = [ - {"จาก": "สินเชื่อรถยนต์ (ยอดขายรถ)", "แหล่ง": "TradingEconomics", "ข้อมูล": auto_d.get("as_of", "น่าล่าสุด")}, - {"จาก": "NPL รถยนต์", "แหล่ง": "BOT FI_NP_003_S2", "ข้อมูล": npl_d.get("period", "รายไตรมาส")}, - {"จาก": "โรงกลั่น (TOP)", "แหล่ง": "Thai Oil investor", "ข้อมูล": "รายไตรมาส"}, - {"จาก": "ภาพรวมประเทศไทย", "แหล่ง": "BOT Thai Economy", "ข้อมูล": "รายเดือน"}, - ] - for r in rows: - r["dึงมาเมื่อ"] = now - # append tourism source if present + + fetched = { + "auto_credit": auto_d, "auto_npl": npl_d, + "energy_thai": en_d, "macro_thai": macro_d, + } + # group FACTORS by fetch module -> one row per distinct source + source_by_module: dict = {} + for fkey, f in factors_mod.FACTORS.items(): + mod = f.get("fetch") + if not mod: + continue + if mod not in source_by_module: + source_by_module[mod] = { + "mod": mod, + "source": f.get("source", mod), + "freq": f.get("frequency", "monthly"), + "factors": [], + } + source_by_module[mod]["factors"].append(fkey) + + # next-update cadence per frequency (hours) + _cadence = {"daily": 24, "monthly": 24 * 30, "quarterly": 24 * 91, + "annual": 24 * 365, "weekly": 24 * 7} + + rows = [] + for mod, meta in source_by_module.items(): + data = fetched.get(mod) or {} + _source_label = { + "auto_credit": "TradingEconomics", + "auto_npl": "BOT FI_NP_003_S2", + "energy_thai": "Thai Oil investor", + "macro_thai": "BOT Thai Economy", + "bot_tourism": "BOT Tourism", + } + freq = meta["freq"] + as_of = data.get("as_of") or data.get("period") or _period(data, meta["factors"]) + next_in_h = _cadence.get(freq, 24 * 30) + next_at = (now_plus(now, next_in_h)) + rows.append({ + "แหล่ง": _source_label.get(mod, meta["source"]), + "ขอบเขต": " | ".join(factors_mod.FACTORS[k]["name_th"] for k in meta["factors"]), + "ข้อมูล": as_of or "", + "ความถี่": freq_th(freq), + "อัปเดตครั้งต่อไป": next_at, + "dึงมาเมื่อ": now, + }) + # tourism (fetched separately) if tourism and isinstance(tourism, dict): - rows.append({"จาก": "ท่องเที่ยว", "แหล่ง": tourism.get("source", {}).get("source_id", "BOT"), - "ข้อมูล": tourism.get("period", ""), "dึงมาเมื่อ": now}) + rows.insert(0, { + "แหล่ง": "BOT Tourism", + "ขอบเขต": "นักท่องเที่ยว", + "ข้อมูล": tourism.get("period", "") or "", + "ความถี่": "รายเดือน", + "อัปเดตครั้งต่อไป": now_plus(now, 24 * 30), + "dึงมาเมื่อ": now, + }) return rows + + +def now_plus(iso_now: str, hours: float) -> str: + import datetime as _dt + base = _dt.datetime.fromisoformat(iso_now) + return (base + _dt.timedelta(hours=hours)).isoformat(timespec="minutes") + + +def freq_th(freq: str) -> str: + return {"daily": "รายวัน", "weekly": "รายสัปดาห์", "monthly": "รายเดือน", + "quarterly": "รายไตรมาส", "annual": "รายปี"}.get(freq, freq) + + +def _period(data: dict, factor_keys: list) -> str: + from app import factors as factors_mod + for k in factor_keys: + f = factors_mod.FACTORS.get(k, {}) + vk = f.get("value_key") + if vk and data.get(vk) is not None: + return f.get("name_th", k) + return "--" diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 990a399..a8b0657 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -549,14 +549,16 @@ onMounted(loadDashboard) - + - + + +
ข้อมูลแหล่งช่วงข้อมูลอัปเดตข้อมูลแหล่งช่วงข้อมูลความถี่อัปเดตครั้งต่อไปอัปเดตล่าสุด
{{ s['จาก'] }}{{ s['จาก'] || s['ขอบเขต'] }} {{ s['แหล่ง'] }} {{ s['ข้อมูล'] }}{{ s['ความถี่'] || '—' }}{{ s['อัปเดตครั้งต่อไป'] ? formatDate(s['อัปเดตครั้งต่อไป']) : '—' }} {{ s['dึงมาเมื่อ'] ? formatDate(s['dึงมาเมื่อ']) : '—' }}