import os, io, json, re, requests, httpx from fastapi import FastAPI, Request, HTTPException, Body, Depends from fastapi.responses import StreamingResponse, RedirectResponse from fastapi.templating import Jinja2Templates from datetime import datetime from .engine import PrecisionEngine from pydantic import BaseModel, Field, field_validator from typing import Optional app = FastAPI() templates = Jinja2Templates(directory="app/templates") engine = PrecisionEngine() class BirthDataInput(BaseModel): name: str = Field(..., min_length=2, max_length=100) date: str = Field(..., pattern=r'^\d{4}-\d{2}-\d{2}$') time: str = Field(..., pattern=r'^\d{2}:\d{2}$') city: str = Field(..., min_length=2, max_length=100) state: str = Field(..., min_length=2, max_length=50) @field_validator('name') def validate_name(cls, v): if not re.match(r'^[a-zA-Z\s]+$', v): raise ValueError('Name must contain only letters and spaces') return v.strip() def get_coordinates(city, state): try: url = f"https://nominatim.openstreetmap.org/search?city={city}&state={state}&format=json" headers = {'User-Agent': 'DefragPrecisionEngine/1.0'} response = requests.get(url, headers=headers, timeout=5) if response.status_code == 200 and len(response.json()) > 0: data = response.json()[0] return {"lat": float(data['lat']), "lon": float(data['lon'])} except: pass return {"lat": 34.1064, "lon": -117.5931} def reduce_number(n): while n > 9 and n not in [11, 22, 33]: n = sum(int(d) for d in str(n)) return n def calculate_life_path(date_str: str) -> int: y, m, d = date_str.split('-') y_red = reduce_number(sum(int(digit) for digit in y)) m_red = reduce_number(sum(int(digit) for digit in m)) d_red = reduce_number(sum(int(digit) for digit in d)) return reduce_number(y_red + m_red + d_red) @app.get("/") async def index(request: Request): return templates.TemplateResponse("dashboard.html", {"request": request}) @app.get("/api/get-signed-url") async def get_signed_url(): ELEVENLABS_API_KEY = os.environ.get("ELEVENLABS_API_KEY") AGENT_ID = "agent_4101kfyny563e168da90yze4vva4" async with httpx.AsyncClient() as client: response = await client.get( f"https://api.elevenlabs.io/v1/convai/conversation/get_signed_url?agent_id={AGENT_ID}", headers={"xi-api-key": ELEVENLABS_API_KEY} ) if response.status_code != 200: raise HTTPException(status_code=500, detail="Failed to get signed URL") return response.json() @app.post("/v1/analyze/clinical") async def clinical_analysis(data: BirthDataInput): coords = get_coordinates(data.city, data.state) natal_dt = datetime.fromisoformat(f"{data.date}T{data.time}") engine_data = engine.calculate_chart(natal_dt, {"latitude": coords['lat'], "longitude": coords['lon']}) life_path = calculate_life_path(data.date) zodiac = ['Aries', 'Taurus', 'Gemini', 'Cancer', 'Leo', 'Virgo', 'Libra', 'Scorpio', 'Sagittarius', 'Capricorn', 'Aquarius', 'Pisces'] planets_rich = {n: {"longitude": round(p.longitude, 4), "gate": p.gate, "line": p.line, "sign": zodiac[int(p.longitude / 30) % 12]} for n, p in engine_data.items()} sun_sign = planets_rich.get('Sun', {}).get('sign', 'Unknown') return { "synthesis": f"Natal Profile: {data.name}. Alignment secured at {coords['lat']:.2f}, {coords['lon']:.2f}.", "planets": planets_rich, "numerology": {"life_path_number": life_path, "sun_sign": sun_sign} }