Full-detail natal charts: 21 bodies/points, 7 house systems, 11 aspect types with applying/separating, dignities, declination parallels, sect/balances/moon phase, offline city search with historical IANA time atlas, SVG wheel, and a written interpretation report. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
65 lines
2.3 KiB
Python
65 lines
2.3 KiB
Python
"""NATEGODD — natal chart generator. Run: .venv/bin/python app.py → http://localhost:7799"""
|
|
from flask import Flask, jsonify, request, send_from_directory
|
|
|
|
import chart as chart_engine
|
|
import geo
|
|
import interp
|
|
|
|
app = Flask(__name__, static_folder="static", static_url_path="")
|
|
|
|
|
|
@app.get("/")
|
|
def index():
|
|
return send_from_directory("static", "index.html")
|
|
|
|
|
|
@app.get("/api/search")
|
|
def api_search():
|
|
q = request.args.get("q", "")
|
|
return jsonify(geo.search_cities(q))
|
|
|
|
|
|
@app.post("/api/chart")
|
|
def api_chart():
|
|
d = request.get_json(force=True)
|
|
try:
|
|
year, month, day = int(d["year"]), int(d["month"]), int(d["day"])
|
|
time_unknown = bool(d.get("time_unknown"))
|
|
hour = 12 if time_unknown else int(d.get("hour", 12))
|
|
minute = 0 if time_unknown else int(d.get("minute", 0))
|
|
second = int(d.get("second", 0) or 0)
|
|
lat, lon = float(d["lat"]), float(d["lon"])
|
|
house_system = d.get("house_system", "P")
|
|
if house_system not in chart_engine.HOUSE_SYSTEMS:
|
|
house_system = "P"
|
|
except (KeyError, ValueError, TypeError) as e:
|
|
return jsonify({"error": f"bad input: {e}"}), 400
|
|
|
|
if d.get("utc_offset") not in (None, ""):
|
|
offset, tzname = float(d["utc_offset"]), "manual"
|
|
else:
|
|
try:
|
|
offset, tzname = geo.resolve_offset(lat, lon, year, month, day, hour, minute,
|
|
tz_hint=d.get("tz"))
|
|
except Exception as e:
|
|
return jsonify({"error": f"could not resolve timezone: {e}"}), 400
|
|
|
|
try:
|
|
result = chart_engine.compute_chart(year, month, day, hour, minute, second,
|
|
offset, lat, lon, house_system, time_unknown)
|
|
except Exception as e:
|
|
return jsonify({"error": f"computation failed: {e}"}), 400
|
|
|
|
result["input"] = {
|
|
"name": d.get("name", ""), "place": d.get("place", ""),
|
|
"lat": lat, "lon": lon, "tz": tzname, "utc_offset": offset,
|
|
"date": f"{year:04d}-{month:02d}-{day:02d}",
|
|
"time": None if time_unknown else f"{hour:02d}:{minute:02d}",
|
|
}
|
|
result["interpretation"] = interp.build_interpretation(result)
|
|
return jsonify(result)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
app.run(host="0.0.0.0", port=7799, debug=False)
|