|
|
|
@@ -0,0 +1,99 @@
|
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
"""Generate active-choice birth-time rectification questions."""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
|
import json
|
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
|
from typing import Any
|
|
|
|
|
|
|
|
|
|
OPTIONS = [
|
|
|
|
|
{"key": "A", "label": "明确有,且时间大致吻合", "score": 2},
|
|
|
|
|
{"key": "B", "label": "有类似,但时间略偏或不够重大", "score": 1},
|
|
|
|
|
{"key": "C", "label": "没有明显发生", "score": -2},
|
|
|
|
|
{"key": "D", "label": "不确定 / 不记得", "score": 0},
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
QUESTION_TEMPLATES = [
|
|
|
|
|
("education_environment_shift", 1, "education", ["D24", "D4", "Dasha"], "age_16_to_18", "16-18岁附近,是否有明显学业、学校、专业方向或学习环境变化?", "middle_candidate_cluster", "against_D24_sensitive_cluster"),
|
|
|
|
|
("residence_relocation_shift", 1, "residence", ["D4", "12H", "Rahu/Ketu", "Transit"], "age_20_to_24", "20-24岁附近,是否有搬家、离乡、长期异地、住宿或居住结构变化?", "D4_relocation_cluster", "against_D4_relocation_cluster"),
|
|
|
|
|
("relationship_or_partner_entry", 1, "relationship", ["D9", "UL", "A7", "7H"], "age_21_to_26", "21-26岁附近,是否有关系对象进入、关系断裂、暧昧升级或关系观明显转变?", "D9_UL_A7_cluster", "against_relationship_cluster"),
|
|
|
|
|
("career_responsibility_pressure", 1, "career", ["D10", "A10", "Saturn", "10H"], "age_26_to_30", "26-30岁附近,是否有责任增加、合作压力、工作结构变化或长期压力阶段?", "D10_A10_saturn_cluster", "against_career_pressure_cluster"),
|
|
|
|
|
("research_tool_expression_shift", 1, "career_learning", ["D10", "D24", "Mercury", "A10"], "recent_three_years", "近三年是否明显进入写作、技术、系统化学习、工具搭建、内容表达、AI/研究类方向?", "Mercury_D24_A10_cluster", "against_learning_expression_cluster"),
|
|
|
|
|
("health_crisis_or_low_period", 2, "health_pressure", ["D30", "6H", "8H", "Saturn/Mars"], "largest_pressure_window", "某个压力窗口附近,是否有健康、事故、低谷、睡眠/精神压力或身体负担明显阶段?", "D30_crisis_cluster", "against_D30_crisis_cluster"),
|
|
|
|
|
("public_role_or_project_visibility", 2, "public_work", ["A10", "D10", "AmK", "Karakamsha"], "career_visibility_window", "某个事业窗口附近,是否有项目公开、作品产出、职位/身份变化或被他人看见的机会?", "A10_public_visibility_cluster", "against_A10_cluster"),
|
|
|
|
|
("sequence_inner_vs_outer", 3, "fine_timing", ["KP_cusp", "Pratyantar", "Dasha_boundary"], "top_candidate_window", "关键变化更像先有内在转向、后有外部结果,还是几乎同时发生?", "fine_boundary_cluster", "neutral"),
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _parse_time(value: str) -> datetime:
|
|
|
|
|
return datetime.strptime(value, "%Y-%m-%d %H:%M")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _candidate_scan(center: datetime, uncertainty_minutes: int, step_minutes: int) -> dict[str, Any]:
|
|
|
|
|
start = center - timedelta(minutes=uncertainty_minutes)
|
|
|
|
|
end = center + timedelta(minutes=uncertainty_minutes)
|
|
|
|
|
return {
|
|
|
|
|
"start": start.strftime("%Y-%m-%d %H:%M"),
|
|
|
|
|
"end": end.strftime("%Y-%m-%d %H:%M"),
|
|
|
|
|
"step_minutes": step_minutes,
|
|
|
|
|
"candidate_count": int((end - start).total_seconds() // 60 // step_minutes) + 1,
|
|
|
|
|
"cluster_labels": ["early_candidate_cluster", "middle_candidate_cluster", "late_candidate_cluster"],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def build_questionnaire(birth_time: str, uncertainty_minutes: int = 30, step_minutes: int = 1) -> dict[str, Any]:
|
|
|
|
|
questions = []
|
|
|
|
|
for qid, round_id, domain, sensitivity, window, prompt, yes_bias, no_bias in QUESTION_TEMPLATES:
|
|
|
|
|
questions.append({
|
|
|
|
|
"id": qid,
|
|
|
|
|
"round": round_id,
|
|
|
|
|
"domain": domain,
|
|
|
|
|
"sensitivity": sensitivity,
|
|
|
|
|
"window": window,
|
|
|
|
|
"prompt": prompt,
|
|
|
|
|
"options": OPTIONS,
|
|
|
|
|
"scoring_map": {
|
|
|
|
|
"A": {"effect": "support", "cluster": yes_bias, "points": 2},
|
|
|
|
|
"B": {"effect": "weak_support", "cluster": yes_bias, "points": 1},
|
|
|
|
|
"C": {"effect": "exclude_or_penalize", "cluster": no_bias, "points": -2},
|
|
|
|
|
"D": {"effect": "neutral", "cluster": "neutral", "points": 0},
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
return {
|
|
|
|
|
"scope": "active_birth_time_rectification_questionnaire",
|
|
|
|
|
"schema_version": 1,
|
|
|
|
|
"candidate_scan": _candidate_scan(_parse_time(birth_time), uncertainty_minutes, step_minutes),
|
|
|
|
|
"workflow": [
|
|
|
|
|
"candidate_time_scan",
|
|
|
|
|
"varga_arudha_kp_sensitivity_diff",
|
|
|
|
|
"high_information_question_generation",
|
|
|
|
|
"multiple_choice_user_answers",
|
|
|
|
|
"dynamic_candidate_cluster_scoring",
|
|
|
|
|
"next_round_question_selection",
|
|
|
|
|
],
|
|
|
|
|
"rounds": {
|
|
|
|
|
"1": "coarse screen",
|
|
|
|
|
"2": "domain follow-up",
|
|
|
|
|
"3": "fine confirmation",
|
|
|
|
|
},
|
|
|
|
|
"sensitivity_layers": ["D9", "D10", "D24", "D30", "D60", "D4", "UL", "A7", "A10", "KP_cusp", "Vimshottari", "Narayana", "Chara"],
|
|
|
|
|
"questions": questions,
|
|
|
|
|
"boundary": "Question generation only; final rectification requires scoring answers against actual candidate chart differences.",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main() -> int:
|
|
|
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
|
|
|
parser.add_argument("--birth-time", required=True, help="Approximate local birth time, YYYY-MM-DD HH:MM")
|
|
|
|
|
parser.add_argument("--uncertainty-minutes", type=int, default=30)
|
|
|
|
|
parser.add_argument("--step-minutes", type=int, default=1)
|
|
|
|
|
parser.add_argument("--pretty", action="store_true")
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
print(json.dumps(build_questionnaire(args.birth_time, args.uncertainty_minutes, args.step_minutes), ensure_ascii=False, indent=2 if args.pretty else None))
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
raise SystemExit(main())
|