151 lines
5.9 KiB
Python
151 lines
5.9 KiB
Python
"""API blueprint — groups, documents, supplements, sync, cleanup, spec-current."""
|
|
from flask import Blueprint, request, jsonify
|
|
from app.db import documents, supplements, spec_current
|
|
from app.db.connection import execute, query
|
|
from app.services.grouping import group_documents, apply_groups
|
|
from app.config import API_KEY
|
|
|
|
api_bp = Blueprint("api", __name__)
|
|
|
|
|
|
def _require_auth():
|
|
"""Проверка API-ключа для опасных операций."""
|
|
if API_KEY:
|
|
key = request.headers.get("X-Api-Key", "")
|
|
if key != API_KEY:
|
|
return False
|
|
return True
|
|
|
|
|
|
# ── Supplements ──────────────────────────────────────────────────
|
|
|
|
@api_bp.route("/api/supplements")
|
|
def api_supplements():
|
|
cid = request.args.get("contract_id")
|
|
if not cid:
|
|
return jsonify(ok=False, error="contract_id required"), 400
|
|
rows = supplements.list_by_contract(cid)
|
|
return jsonify(ok=True, supplements=rows)
|
|
|
|
|
|
# ── Documents ────────────────────────────────────────────────────
|
|
|
|
@api_bp.route("/api/documents/<doc_id>")
|
|
def api_document(doc_id):
|
|
doc = documents.get(doc_id)
|
|
if not doc:
|
|
return jsonify(ok=False, error="not found"), 404
|
|
return jsonify(ok=True, **{
|
|
k: doc.get(k) for k in [
|
|
"id", "filename", "status", "elements_json", "doc_type",
|
|
"own_number", "parent_number", "doc_date", "counterparty",
|
|
"classify_status", "classify_raw", "classify_input",
|
|
]
|
|
})
|
|
|
|
|
|
@api_bp.route("/api/documents/<doc_id>", methods=["DELETE"])
|
|
def api_document_delete(doc_id):
|
|
supps = query("SELECT id, contract_id FROM supplements WHERE document_id = %s", (doc_id,))
|
|
for s in (supps or []):
|
|
execute(
|
|
"""DELETE FROM spec_current WHERE contract_id = %s
|
|
AND last_event_id IN (SELECT id FROM spec_events WHERE supplement_id = %s)""",
|
|
(s["contract_id"], s["id"]),
|
|
)
|
|
execute("DELETE FROM spec_events WHERE supplement_id = %s", (s["id"],))
|
|
execute("DELETE FROM supplements WHERE id = %s", (s["id"],))
|
|
execute("DELETE FROM documents WHERE id = %s", (doc_id,))
|
|
return jsonify(ok=True)
|
|
|
|
|
|
# ── Sync ─────────────────────────────────────────────────────────
|
|
|
|
@api_bp.route("/api/sync", methods=["POST"])
|
|
def api_sync():
|
|
"""Удалить документы, не входящие в keep_ids."""
|
|
body = request.get_json()
|
|
keep_ids = set(body.get("keep_ids", []))
|
|
if len(keep_ids) > 1000:
|
|
return jsonify(ok=False, error="too many keep_ids (max 1000)"), 400
|
|
|
|
docs = query("SELECT id FROM documents", ())
|
|
deleted = 0
|
|
for d in (docs or []):
|
|
if d["id"] in keep_ids:
|
|
continue
|
|
supps = query("SELECT id, contract_id FROM supplements WHERE document_id = %s", (d["id"],))
|
|
for s in (supps or []):
|
|
execute(
|
|
"""DELETE FROM spec_current WHERE contract_id = %s
|
|
AND last_event_id IN (SELECT id FROM spec_events WHERE supplement_id = %s)""",
|
|
(s["contract_id"], s["id"]),
|
|
)
|
|
execute("DELETE FROM spec_events WHERE supplement_id = %s", (s["id"],))
|
|
execute("DELETE FROM supplements WHERE id = %s", (s["id"],))
|
|
execute("DELETE FROM documents WHERE id = %s", (d["id"],))
|
|
deleted += 1
|
|
|
|
execute("DELETE FROM contracts WHERE id NOT IN (SELECT DISTINCT contract_id FROM supplements)")
|
|
return jsonify(ok=True, deleted=deleted)
|
|
|
|
|
|
# ── Groups ───────────────────────────────────────────────────────
|
|
|
|
@api_bp.route("/api/groups")
|
|
def api_groups():
|
|
batch_id = request.args.get("batch")
|
|
if not batch_id:
|
|
return jsonify(ok=False, error="batch required"), 400
|
|
result = group_documents(batch_id)
|
|
return jsonify(result)
|
|
|
|
|
|
@api_bp.route("/api/batch-progress")
|
|
def api_batch_progress():
|
|
batch_id = request.args.get("batch")
|
|
if not batch_id:
|
|
return jsonify(ok=False, error="batch required"), 400
|
|
counts = documents.count_by_status(batch_id)
|
|
docs = documents.list_by_batch(batch_id)
|
|
return jsonify(ok=True, counts=counts, total=len(docs))
|
|
|
|
|
|
@api_bp.route("/api/apply-groups", methods=["POST"])
|
|
def api_apply_groups():
|
|
body = request.get_json()
|
|
batch_id = body.get("batch_id")
|
|
groups = body.get("groups", [])
|
|
if not batch_id:
|
|
return jsonify(ok=False, error="batch_id required"), 400
|
|
result = apply_groups(batch_id, groups)
|
|
return jsonify(result)
|
|
|
|
|
|
# ── Spec current ─────────────────────────────────────────────────
|
|
|
|
@api_bp.route("/api/spec-current")
|
|
def api_spec_current():
|
|
cid = request.args.get("contract_id")
|
|
if not cid:
|
|
return jsonify(ok=False, error="contract_id required"), 400
|
|
rows = spec_current.list_by_contract(cid)
|
|
return jsonify(ok=True, rows=rows)
|
|
|
|
|
|
# ── Cleanup (требует API-ключ) ────────────────────────────────────
|
|
|
|
@api_bp.route("/api/cleanup", methods=["POST"])
|
|
def api_cleanup():
|
|
"""Полная очистка всех данных (кроме prompts)."""
|
|
if not _require_auth():
|
|
return jsonify(ok=False, error="unauthorized"), 401
|
|
|
|
execute("DELETE FROM spec_current")
|
|
execute("DELETE FROM spec_events")
|
|
execute("DELETE FROM supplements")
|
|
execute("DELETE FROM upload_chunks")
|
|
execute("DELETE FROM documents")
|
|
execute("DELETE FROM contracts")
|
|
return jsonify(ok=True, message="all data cleaned")
|