V10.545 收斂比價覆蓋率口徑

This commit is contained in:
OoO
2026-06-01 12:02:22 +08:00
parent 83b813c1b9
commit e4534c2f96
7 changed files with 65 additions and 19 deletions

View File

@@ -25,6 +25,11 @@ from sqlalchemy import inspect, text
PCHOME_MATCH_SCORE_FLOOR = 0.76
UNIT_COMPARABLE_STATUSES = {"unit_comparable", "refresh_unit_comparable"}
MANUAL_CLOSED_ATTEMPT_STATUSES = {
"manual_rejected",
"manual_unit_price_required",
"manual_needs_research",
}
ACTIONABLE_ATTEMPT_STATUSES = {
"rescore_accepted_current",
"unit_comparable",
@@ -38,13 +43,11 @@ ACTIONABLE_ATTEMPT_STATUSES = {
"expired_match",
"refresh_no_result",
"no_result",
"manual_rejected",
"manual_unit_price_required",
"manual_needs_research",
}
REVIEW_QUEUE_ATTEMPT_STATUSES = ACTIONABLE_ATTEMPT_STATUSES | MANUAL_CLOSED_ATTEMPT_STATUSES
REVIEW_STATUS_FILTER_GROUPS = {
"rescore_accepted": ("rescore_accepted_current",),
"unit_comparable": ("unit_comparable", "refresh_unit_comparable", "manual_unit_price_required"),
"unit_comparable": ("unit_comparable", "refresh_unit_comparable"),
"identity_veto": ("identity_veto",),
"low_score": ("low_score", "refresh_low_score", "recoverable_low_score", "true_low_confidence"),
"recoverable_low_score": ("recoverable_low_score",),
@@ -705,7 +708,7 @@ def _cached_payload(cache_key: str, producer, ttl_seconds: int = COMPETITOR_INTE
def fetch_competitor_coverage(engine) -> dict:
return _cached_payload(
f"coverage:v8:floor={PCHOME_MATCH_SCORE_FLOOR}:manual_reviews=1:rescore=1:review_no_fresh=1:decision_ready=1",
f"coverage:v9:floor={PCHOME_MATCH_SCORE_FLOOR}:manual_reviews=1:rescore=1:review_no_fresh=1:decision_ready=1:open_queue=1",
lambda: _fetch_competitor_coverage_uncached(engine),
)
@@ -724,13 +727,19 @@ def _fetch_competitor_coverage_uncached(engine) -> dict:
"stale_matches": 0,
"pending": 0,
"decision_ready_matches": 0,
"identity_coverage_matches": 0,
"identity_coverage_rate": 0,
"pending_identity_count": 0,
"stale_identity_count": 0,
"match_rate": 0,
"fresh_match_rate": 0,
"decision_ready_rate": 0,
"last_decision_ready_crawled_at": None,
"attempt_status": {},
"unit_comparable_count": 0,
"rescore_accepted_count": 0,
"actionable_review_count": 0,
"manual_closed_count": 0,
"manual_review_summary": manual_review_summary,
"manual_review_total": manual_review_summary["total"],
"manual_accept_count": manual_review_summary["accept_identity"],
@@ -779,7 +788,8 @@ def _fetch_competitor_coverage_uncached(engine) -> dict:
identity_competitor AS (
SELECT DISTINCT ON (cp.sku)
cp.sku,
cp.expires_at
cp.expires_at,
cp.crawled_at
FROM competitor_prices cp
WHERE cp.source = 'pchome'
AND cp.price IS NOT NULL
@@ -789,7 +799,7 @@ def _fetch_competitor_coverage_uncached(engine) -> dict:
ORDER BY cp.sku, cp.crawled_at DESC NULLS LAST
),
fresh_competitor AS (
SELECT sku
SELECT sku, crawled_at
FROM identity_competitor
WHERE expires_at IS NULL OR expires_at > CURRENT_TIMESTAMP
),
@@ -811,6 +821,9 @@ def _fetch_competitor_coverage_uncached(engine) -> dict:
FROM latest_momo lm
LEFT JOIN identity_competitor ic ON ic.sku = lm.sku
WHERE ic.sku IS NULL) AS pending,
(SELECT MAX(fc.crawled_at)
FROM latest_momo lm
JOIN fresh_competitor fc ON fc.sku = lm.sku) AS last_decision_ready_crawled_at,
COALESCE(la.attempt_status, 'never_attempted') AS attempt_status,
COUNT(*) AS status_count
FROM latest_momo lm
@@ -834,6 +847,8 @@ def _fetch_competitor_coverage_uncached(engine) -> dict:
unit_count = sum(statuses.get(status, 0) for status in UNIT_COMPARABLE_STATUSES)
rescore_accepted_count = int(statuses.get("rescore_accepted_current") or 0)
actionable_count = sum(statuses.get(status, 0) for status in ACTIONABLE_ATTEMPT_STATUSES)
manual_closed_count = sum(statuses.get(status, 0) for status in MANUAL_CLOSED_ATTEMPT_STATUSES)
last_decision_ready_crawled_at = rows[0].get("last_decision_ready_crawled_at") if rows else None
return {
"active_with_price": active,
"valid_matches": valid,
@@ -841,13 +856,19 @@ def _fetch_competitor_coverage_uncached(engine) -> dict:
"stale_matches": stale,
"pending": pending,
"decision_ready_matches": fresh,
"identity_coverage_matches": valid,
"identity_coverage_rate": round(valid / max(active, 1) * 100, 1),
"pending_identity_count": pending,
"stale_identity_count": stale,
"match_rate": round(valid / max(active, 1) * 100, 1),
"fresh_match_rate": round(fresh / max(valid, 1) * 100, 1),
"decision_ready_rate": round(fresh / max(active, 1) * 100, 1),
"last_decision_ready_crawled_at": last_decision_ready_crawled_at,
"attempt_status": statuses,
"unit_comparable_count": unit_count,
"rescore_accepted_count": rescore_accepted_count,
"actionable_review_count": actionable_count,
"manual_closed_count": manual_closed_count,
"manual_review_summary": manual_review_summary,
"manual_review_total": manual_review_summary["total"],
"manual_accept_count": manual_review_summary["accept_identity"],