feat: causal lab — éditeur visuel, types nœuds, force/signe arêtes, NFP v2
This commit is contained in:
@@ -306,6 +306,16 @@ class UpdateCoefRequest(BaseModel):
|
||||
coefficients: dict # {coef_key: float}
|
||||
|
||||
|
||||
class CreateTemplateRequest(BaseModel):
|
||||
name: str
|
||||
category: str
|
||||
sub_type: str = ""
|
||||
description: str = ""
|
||||
instruments: list = ["EURUSD"]
|
||||
graph_json: dict
|
||||
ai_rationale: str = ""
|
||||
|
||||
|
||||
class RecommendRequest(BaseModel):
|
||||
market_event_id: int
|
||||
|
||||
@@ -314,8 +324,8 @@ class AnalyzeRequest(BaseModel):
|
||||
market_event_id: int
|
||||
template_id: int
|
||||
instrument: str = "EURUSD"
|
||||
inputs: dict = {} # Inputs manuels (ex: tone_score)
|
||||
coef_overrides: dict = {} # Coefficients overrides pour cet run
|
||||
inputs: dict = {}
|
||||
coef_overrides: dict = {}
|
||||
|
||||
|
||||
# ── Endpoints ─────────────────────────────────────────────────────────────────
|
||||
@@ -359,6 +369,62 @@ def list_templates(category: str = Query("")):
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
@router.post("/api/causal-lab/template")
|
||||
def create_template(body: CreateTemplateRequest):
|
||||
"""Crée un nouveau template utilisateur."""
|
||||
try:
|
||||
from services.database import get_conn
|
||||
from services.causal_graphs import init_tables, seed_templates
|
||||
conn = get_conn()
|
||||
init_tables(conn); seed_templates(conn)
|
||||
existing = conn.execute(
|
||||
"SELECT id FROM causal_graph_templates WHERE name = ?", (body.name,)
|
||||
).fetchone()
|
||||
if existing:
|
||||
raise HTTPException(409, f"Un template nommé '{body.name}' existe déjà")
|
||||
cur = conn.execute("""
|
||||
INSERT INTO causal_graph_templates
|
||||
(name, category, sub_type, instruments, description, graph_json, ai_rationale, created_by)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, 'user')
|
||||
""", (
|
||||
body.name, body.category, body.sub_type,
|
||||
json.dumps(body.instruments), body.description,
|
||||
json.dumps(body.graph_json), body.ai_rationale,
|
||||
))
|
||||
conn.commit()
|
||||
new_id = cur.lastrowid
|
||||
conn.close()
|
||||
return {"id": new_id, "name": body.name}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"[causal_lab] create_template: {e}")
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
@router.delete("/api/causal-lab/template/{template_id}")
|
||||
def delete_template(template_id: int):
|
||||
"""Supprime un template utilisateur (les templates system sont protégés)."""
|
||||
try:
|
||||
from services.database import get_conn
|
||||
conn = get_conn()
|
||||
row = conn.execute(
|
||||
"SELECT created_by FROM causal_graph_templates WHERE id = ?", (template_id,)
|
||||
).fetchone()
|
||||
if not row:
|
||||
conn.close(); raise HTTPException(404, "Template introuvable")
|
||||
if row["created_by"] == "system":
|
||||
conn.close(); raise HTTPException(403, "Les templates système ne peuvent pas être supprimés")
|
||||
conn.execute("DELETE FROM causal_graph_templates WHERE id = ?", (template_id,))
|
||||
conn.commit(); conn.close()
|
||||
return {"ok": True}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"[causal_lab] delete_template {template_id}: {e}")
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
@router.get("/api/causal-lab/template/{template_id}")
|
||||
def get_template_detail(template_id: int):
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user