V10.545 收斂比價覆蓋率口徑
This commit is contained in:
@@ -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"],
|
||||
|
||||
Reference in New Issue
Block a user