Add candidate sensitivity scan to active rectification
This commit is contained in:
@@ -34,15 +34,50 @@ def _parse_time(value: str) -> datetime:
|
|||||||
def _candidate_scan(center: datetime, uncertainty_minutes: int, step_minutes: int) -> dict[str, Any]:
|
def _candidate_scan(center: datetime, uncertainty_minutes: int, step_minutes: int) -> dict[str, Any]:
|
||||||
start = center - timedelta(minutes=uncertainty_minutes)
|
start = center - timedelta(minutes=uncertainty_minutes)
|
||||||
end = center + timedelta(minutes=uncertainty_minutes)
|
end = center + timedelta(minutes=uncertainty_minutes)
|
||||||
|
total_minutes = int((end - start).total_seconds() // 60)
|
||||||
|
candidate_count = total_minutes // step_minutes + 1
|
||||||
|
sample_offsets = sorted({-uncertainty_minutes, 0, uncertainty_minutes})
|
||||||
|
samples = []
|
||||||
|
for offset in sample_offsets:
|
||||||
|
candidate = center + timedelta(minutes=offset)
|
||||||
|
if offset < 0:
|
||||||
|
cluster = "early_candidate_cluster"
|
||||||
|
elif offset > 0:
|
||||||
|
cluster = "late_candidate_cluster"
|
||||||
|
else:
|
||||||
|
cluster = "middle_candidate_cluster"
|
||||||
|
samples.append({
|
||||||
|
"time": candidate.strftime("%Y-%m-%d %H:%M"),
|
||||||
|
"offset_minutes": offset,
|
||||||
|
"cluster": cluster,
|
||||||
|
"sensitivity_flags": _sensitivity_flags(abs(offset)),
|
||||||
|
})
|
||||||
return {
|
return {
|
||||||
"start": start.strftime("%Y-%m-%d %H:%M"),
|
"start": start.strftime("%Y-%m-%d %H:%M"),
|
||||||
"end": end.strftime("%Y-%m-%d %H:%M"),
|
"end": end.strftime("%Y-%m-%d %H:%M"),
|
||||||
"step_minutes": step_minutes,
|
"step_minutes": step_minutes,
|
||||||
"candidate_count": int((end - start).total_seconds() // 60 // step_minutes) + 1,
|
"candidate_count": candidate_count,
|
||||||
"cluster_labels": ["early_candidate_cluster", "middle_candidate_cluster", "late_candidate_cluster"],
|
"cluster_labels": ["early_candidate_cluster", "middle_candidate_cluster", "late_candidate_cluster"],
|
||||||
|
"samples": samples,
|
||||||
|
"sensitivity_summary": {
|
||||||
|
"method": "range_bucket_scan_v1",
|
||||||
|
"high_value_layers": ["D9", "D10", "D24", "D30", "D60", "UL", "A7", "A10", "KP_cusp"],
|
||||||
|
"computed_layers": ["time_range", "candidate_cluster", "question_sensitivity_map"],
|
||||||
|
"blocked_layers": ["true_varga_recast", "true_kp_cusp_recast", "true_arudha_recast"],
|
||||||
|
"boundary": "This is a candidate-question scan. True chart-difference recast is the next gate.",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _sensitivity_flags(abs_offset_minutes: int) -> list[str]:
|
||||||
|
flags = ["D9", "D10", "D24", "A10"]
|
||||||
|
if abs_offset_minutes >= 10:
|
||||||
|
flags.extend(["D30", "UL", "A7"])
|
||||||
|
if abs_offset_minutes >= 20:
|
||||||
|
flags.extend(["D60", "KP_cusp"])
|
||||||
|
return flags
|
||||||
|
|
||||||
|
|
||||||
def build_questionnaire(birth_time: str, uncertainty_minutes: int = 30, step_minutes: int = 1) -> dict[str, Any]:
|
def build_questionnaire(birth_time: str, uncertainty_minutes: int = 30, step_minutes: int = 1) -> dict[str, Any]:
|
||||||
questions = []
|
questions = []
|
||||||
for qid, round_id, domain, sensitivity, window, prompt, yes_bias, no_bias in QUESTION_TEMPLATES:
|
for qid, round_id, domain, sensitivity, window, prompt, yes_bias, no_bias in QUESTION_TEMPLATES:
|
||||||
|
|||||||
@@ -16,6 +16,13 @@ def test_active_rectification_questions_generate_choice_based_workflow() -> None
|
|||||||
assert {q["round"] for q in report["questions"]} == {1, 2, 3}
|
assert {q["round"] for q in report["questions"]} == {1, 2, 3}
|
||||||
assert all({option["key"] for option in q["options"]} == {"A", "B", "C", "D"} for q in report["questions"])
|
assert all({option["key"] for option in q["options"]} == {"A", "B", "C", "D"} for q in report["questions"])
|
||||||
assert all("scoring_map" in q for q in report["questions"])
|
assert all("scoring_map" in q for q in report["questions"])
|
||||||
|
assert report["candidate_scan"]["samples"]
|
||||||
|
assert report["candidate_scan"]["sensitivity_summary"]
|
||||||
|
assert {"D9", "D10", "D24", "D30", "UL", "A10", "KP_cusp"} <= set(
|
||||||
|
report["candidate_scan"]["sensitivity_summary"]["high_value_layers"]
|
||||||
|
)
|
||||||
|
assert report["candidate_scan"]["samples"][0]["cluster"] == "early_candidate_cluster"
|
||||||
|
assert report["candidate_scan"]["samples"][-1]["cluster"] == "late_candidate_cluster"
|
||||||
|
|
||||||
|
|
||||||
def test_active_rectification_scores_answers_and_selects_next_round() -> None:
|
def test_active_rectification_scores_answers_and_selects_next_round() -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user