feat: expose dossier timeline and exceptions API
This commit is contained in:
@@ -1,6 +1,11 @@
|
|||||||
"""API blueprint — groups, documents, supplements, sync, cleanup, spec-current, chat."""
|
"""API blueprint — groups, documents, supplements, sync, cleanup, spec-current, chat."""
|
||||||
|
import json
|
||||||
|
from datetime import date
|
||||||
from flask import Blueprint, request, jsonify
|
from flask import Blueprint, request, jsonify
|
||||||
from db import documents, supplements, spec_current
|
from db import documents, supplements, spec_current
|
||||||
|
from db import dossiers as db_dossiers
|
||||||
|
from db import exceptions as db_exceptions
|
||||||
|
from db import timeline as db_timeline
|
||||||
from db.connection import execute, query, transaction
|
from db.connection import execute, query, transaction
|
||||||
from services.grouping import group_documents, apply_groups
|
from services.grouping import group_documents, apply_groups
|
||||||
from config import LLM_URL, LLM_KEY, LLM_MODEL
|
from config import LLM_URL, LLM_KEY, LLM_MODEL
|
||||||
@@ -9,6 +14,56 @@ import httpx
|
|||||||
api_bp = Blueprint("api", __name__)
|
api_bp = Blueprint("api", __name__)
|
||||||
|
|
||||||
|
|
||||||
|
# ── Contract timeline and exceptions ────────────────────────────
|
||||||
|
|
||||||
|
@api_bp.route("/api/dossiers/<dossier_id>/timeline")
|
||||||
|
def api_dossier_timeline(dossier_id):
|
||||||
|
"""Вернуть полную историю и активный срез обязательств dossier."""
|
||||||
|
dossier = db_dossiers.get_dossier(dossier_id)
|
||||||
|
if not dossier:
|
||||||
|
return jsonify(ok=False, error="dossier not found"), 404
|
||||||
|
|
||||||
|
as_of = request.args.get("as_of") or date.today().isoformat()
|
||||||
|
try:
|
||||||
|
as_of = date.fromisoformat(as_of).isoformat()
|
||||||
|
except ValueError:
|
||||||
|
return jsonify(ok=False, error="as_of must be YYYY-MM-DD"), 400
|
||||||
|
|
||||||
|
history = db_timeline.list_timeline(dossier_id)
|
||||||
|
active = db_timeline.list_timeline(dossier_id, as_of)
|
||||||
|
for interval in history + active:
|
||||||
|
try:
|
||||||
|
interval["config_params"] = json.loads(interval.get("config_params_json") or "{}")
|
||||||
|
except (TypeError, json.JSONDecodeError):
|
||||||
|
interval["config_params"] = {}
|
||||||
|
return jsonify(
|
||||||
|
ok=True,
|
||||||
|
dossier=dossier,
|
||||||
|
as_of=as_of,
|
||||||
|
timeline=history,
|
||||||
|
active=active,
|
||||||
|
exceptions=db_exceptions.list_exceptions(dossier_id=dossier_id, status="OPEN"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@api_bp.route("/api/exceptions")
|
||||||
|
def api_exceptions():
|
||||||
|
"""Вернуть исключения только по явно заданной области batch или dossier."""
|
||||||
|
batch_id = request.args.get("batch_id")
|
||||||
|
dossier_id = request.args.get("dossier_id")
|
||||||
|
status = request.args.get("status")
|
||||||
|
if not batch_id and not dossier_id:
|
||||||
|
return jsonify(ok=False, error="batch_id or dossier_id required"), 400
|
||||||
|
if status and status not in {"OPEN", "RESOLVED"}:
|
||||||
|
return jsonify(ok=False, error="status must be OPEN or RESOLVED"), 400
|
||||||
|
return jsonify(
|
||||||
|
ok=True,
|
||||||
|
exceptions=db_exceptions.list_exceptions(
|
||||||
|
batch_id=batch_id, dossier_id=dossier_id, status=status,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# ── Supplements ──────────────────────────────────────────────────
|
# ── Supplements ──────────────────────────────────────────────────
|
||||||
|
|
||||||
@api_bp.route("/api/supplements")
|
@api_bp.route("/api/supplements")
|
||||||
|
|||||||
Reference in New Issue
Block a user