fix(rectification): three-column candidate compare card (BUG-597, BUG-598)
Replace the minute-row delivery card with up to three compare columns so users can pick the time that fits, and apply the adult-year floor on inspect fallbacks. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -240,6 +240,49 @@ def window_scan(
|
||||
return payload
|
||||
|
||||
|
||||
def _clock_key(value: Any) -> str | None:
|
||||
raw = str(value or "")[:5]
|
||||
return raw if len(raw) == 5 and raw[2] == ":" else None
|
||||
|
||||
|
||||
def _event_year_month(event: dict[str, Any]) -> str | None:
|
||||
for key in ("date_start", "date", "occurred_from"):
|
||||
raw = str(event.get(key) or "")
|
||||
if len(raw) >= 7 and raw[4] == "-":
|
||||
return raw[:7]
|
||||
year = event.get("year")
|
||||
month = event.get("month")
|
||||
if isinstance(year, int) and 1900 <= year <= 2100:
|
||||
if isinstance(month, int) and 1 <= month <= 12:
|
||||
return f"{year:04d}-{month:02d}"
|
||||
return f"{year:04d}"
|
||||
return None
|
||||
|
||||
|
||||
def column_times_for_compare(
|
||||
candidate_times: Sequence[str],
|
||||
representative_time: str | None,
|
||||
*,
|
||||
limit: int = 3,
|
||||
) -> list[str]:
|
||||
"""At most `limit` unique HH:MM keys, ranked as given (engine score order)."""
|
||||
seen: list[str] = []
|
||||
for raw in candidate_times:
|
||||
clock = _clock_key(raw)
|
||||
if not clock or clock in seen:
|
||||
continue
|
||||
seen.append(clock)
|
||||
if len(seen) >= limit:
|
||||
break
|
||||
representative = _clock_key(representative_time)
|
||||
if representative and representative not in seen:
|
||||
if len(seen) < limit:
|
||||
seen.insert(0, representative)
|
||||
elif seen:
|
||||
seen[-1] = representative
|
||||
return seen
|
||||
|
||||
|
||||
def event_dasha_ledger(
|
||||
request: dict[str, Any],
|
||||
built: dict[str, Any],
|
||||
@@ -264,6 +307,8 @@ def event_dasha_ledger(
|
||||
for track in tracks
|
||||
) or "现有大运层"
|
||||
gochara_hit = _has_gochara(rule_ids)
|
||||
domain = str(event.get("domain") or "").strip()
|
||||
year_month = _event_year_month(event)
|
||||
rows.append({
|
||||
"summary": summary[:80],
|
||||
"match": level,
|
||||
@@ -274,10 +319,24 @@ def event_dasha_ledger(
|
||||
f"{summary[:40]}:{MATCH_LABELS[level]}({track_text}"
|
||||
f"{';Gochara 激活相关宫' if gochara_hit else ';Gochara 未见对应'})"
|
||||
),
|
||||
**({"domain": domain} if domain else {}),
|
||||
**({"year_month": year_month} if year_month else {}),
|
||||
})
|
||||
return rows
|
||||
|
||||
|
||||
def event_dasha_ledgers_by_time(
|
||||
request: dict[str, Any],
|
||||
built: dict[str, Any],
|
||||
times: Sequence[str],
|
||||
) -> dict[str, list[dict[str, Any]]]:
|
||||
return {
|
||||
clock: event_dasha_ledger(request, built, clock)
|
||||
for raw in times
|
||||
if (clock := _clock_key(raw))
|
||||
}
|
||||
|
||||
|
||||
def event_fit_rate(rows: Sequence[dict[str, Any]]) -> dict[str, Any]:
|
||||
total = len(rows)
|
||||
matched = sum(1 for row in rows if row.get("match") in {"strong", "medium"})
|
||||
@@ -549,16 +608,32 @@ def build_refinement_packet(
|
||||
candidate_times: Sequence[str],
|
||||
cluster_width_minutes: int | None = None,
|
||||
include_discriminators: bool = True,
|
||||
column_times: Sequence[str] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
from time import perf_counter
|
||||
|
||||
from scripts.rectification.event_probes import prospective_windows_by_time
|
||||
|
||||
scan = window_scan(built)
|
||||
cluster = cluster_scan(built, candidate_times, representative_time, cluster_width_minutes)
|
||||
ledger = event_dasha_ledger(request, built, representative_time)
|
||||
columns = column_times_for_compare(
|
||||
column_times if column_times is not None else candidate_times,
|
||||
representative_time,
|
||||
)
|
||||
compare_started = perf_counter()
|
||||
ledgers_by_time = event_dasha_ledgers_by_time(request, built, columns)
|
||||
windows_by_time = prospective_windows_by_time(request, built, columns)
|
||||
column_compare_ms = round((perf_counter() - compare_started) * 1000, 1)
|
||||
agreement = dasha_agreement(built, candidate_times)
|
||||
stage = precision_stage(cluster, len(request.get("events") or []))
|
||||
if not include_discriminators:
|
||||
return {
|
||||
"window_scan": scan,
|
||||
"event_dasha_ledger": ledger,
|
||||
"event_dasha_ledger_by_time": ledgers_by_time,
|
||||
"prospective_windows_by_time": windows_by_time,
|
||||
"column_compare_ms": column_compare_ms,
|
||||
"event_fit_rate": event_fit_rate(ledger),
|
||||
"dasha_agreement": agreement,
|
||||
"lagna_contrast": lagna_contrast(built),
|
||||
@@ -634,6 +709,9 @@ def build_refinement_packet(
|
||||
return {
|
||||
"window_scan": scan,
|
||||
"event_dasha_ledger": ledger,
|
||||
"event_dasha_ledger_by_time": ledgers_by_time,
|
||||
"prospective_windows_by_time": windows_by_time,
|
||||
"column_compare_ms": column_compare_ms,
|
||||
"event_fit_rate": event_fit_rate(ledger),
|
||||
"dasha_agreement": agreement,
|
||||
"lagna_contrast": lagna_contrast(built),
|
||||
|
||||
Reference in New Issue
Block a user