SysOps: voice-agy-webbuilder-backup — 2026-06-23 10:04 UTC
This commit is contained in:
+147
@@ -0,0 +1,147 @@
|
||||
"""Klant 360° — geaggregeerde data uit NAS, CRM, retail, trends."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from app.db import fetch_all, fetch_one
|
||||
from app.services import projects as projects_svc
|
||||
|
||||
|
||||
def _serialize_row(row: dict | None) -> dict | None:
|
||||
if not row:
|
||||
return None
|
||||
out = dict(row)
|
||||
for k, v in list(out.items()):
|
||||
if hasattr(v, "isoformat"):
|
||||
out[k] = v.isoformat()
|
||||
return out
|
||||
|
||||
|
||||
def _serialize_rows(rows: list) -> list[dict]:
|
||||
return [_serialize_row(r) for r in rows if r]
|
||||
|
||||
|
||||
def get_client_360(client_id: int) -> dict[str, Any]:
|
||||
client = fetch_one("SELECT * FROM clients WHERE id = %s", (client_id,))
|
||||
if not client:
|
||||
return {"ok": False, "error": "Client not found"}
|
||||
|
||||
projs = projects_svc.list_projects(client_id=client_id, limit=50)
|
||||
links = fetch_all(
|
||||
"""
|
||||
SELECT dl.*, p.name AS project_name
|
||||
FROM document_links dl
|
||||
LEFT JOIN cockpit_projects p ON p.id = dl.project_id
|
||||
WHERE dl.client_id = %s
|
||||
ORDER BY dl.created_at DESC
|
||||
""",
|
||||
(client_id,),
|
||||
)
|
||||
paths = [l["storage_path"] for l in links if l.get("storage_path")]
|
||||
docs: list[dict] = []
|
||||
if paths:
|
||||
placeholders = ",".join(["%s"] * len(paths))
|
||||
docs = fetch_all(
|
||||
f"""
|
||||
SELECT filename, storage_path, doc_type, word_count, sentiment_label,
|
||||
sentiment_compound, analyzed_at, user_labels
|
||||
FROM document_analytics
|
||||
WHERE storage_path IN ({placeholders})
|
||||
OR storage_path LIKE ANY (
|
||||
SELECT dl.storage_path || '/%%' FROM document_links dl
|
||||
WHERE dl.client_id = %s AND dl.is_folder = TRUE
|
||||
)
|
||||
ORDER BY analyzed_at DESC NULLS LAST
|
||||
LIMIT 80
|
||||
""",
|
||||
tuple(paths + [client_id]),
|
||||
)
|
||||
sentiment_rows = fetch_all(
|
||||
"""
|
||||
SELECT sentiment_label, COUNT(*) AS n, AVG(sentiment_compound) AS avg_c
|
||||
FROM document_analytics da
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM document_links dl
|
||||
WHERE dl.client_id = %s
|
||||
AND (da.storage_path = dl.storage_path
|
||||
OR (dl.is_folder AND da.storage_path LIKE dl.storage_path || '/%%'))
|
||||
)
|
||||
GROUP BY sentiment_label
|
||||
""",
|
||||
(client_id,),
|
||||
)
|
||||
top_words = fetch_all(
|
||||
"""
|
||||
SELECT dwc.lemma, SUM(dwc.count) AS total
|
||||
FROM document_word_counts dwc
|
||||
JOIN document_analytics da ON da.id = dwc.document_id
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM document_links dl
|
||||
WHERE dl.client_id = %s
|
||||
AND (da.storage_path = dl.storage_path
|
||||
OR (dl.is_folder AND da.storage_path LIKE dl.storage_path || '/%%'))
|
||||
)
|
||||
AND NOT dwc.is_stopword
|
||||
GROUP BY dwc.lemma
|
||||
ORDER BY total DESC
|
||||
LIMIT 25
|
||||
""",
|
||||
(client_id,),
|
||||
)
|
||||
deals = fetch_all(
|
||||
"SELECT id, title, value, stage, next_action, deadline FROM deals WHERE client_id = %s ORDER BY updated_at DESC LIMIT 15",
|
||||
(client_id,),
|
||||
)
|
||||
stores = fetch_all(
|
||||
"""
|
||||
SELECT s.id, s.name, s.chain, s.city, s.partnership_status, s.halal_certified
|
||||
FROM client_supermarket_links l
|
||||
JOIN supermarkets s ON s.id = l.supermarket_id
|
||||
WHERE l.client_id = %s
|
||||
LIMIT 30
|
||||
""",
|
||||
(client_id,),
|
||||
)
|
||||
trends = fetch_all(
|
||||
"""
|
||||
SELECT DATE_TRUNC('month', da.analyzed_at) AS month,
|
||||
COUNT(*) AS docs,
|
||||
AVG(da.sentiment_compound) AS avg_sentiment,
|
||||
SUM(da.word_count) AS words
|
||||
FROM document_analytics da
|
||||
WHERE da.analyzed_at IS NOT NULL
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM document_links dl
|
||||
WHERE dl.client_id = %s
|
||||
AND (da.storage_path = dl.storage_path
|
||||
OR (dl.is_folder AND da.storage_path LIKE dl.storage_path || '/%%'))
|
||||
)
|
||||
GROUP BY 1
|
||||
ORDER BY 1 DESC
|
||||
LIMIT 12
|
||||
""",
|
||||
(client_id,),
|
||||
)
|
||||
for t in trends:
|
||||
if hasattr(t.get("month"), "isoformat"):
|
||||
t["month"] = t["month"].isoformat()
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"client": _serialize_row(client),
|
||||
"projects": projs,
|
||||
"links": _serialize_rows(links),
|
||||
"documents": _serialize_rows(docs),
|
||||
"sentiment_breakdown": _serialize_rows(sentiment_rows),
|
||||
"top_words": _serialize_rows(top_words),
|
||||
"deals": _serialize_rows(deals),
|
||||
"stores": _serialize_rows(stores),
|
||||
"monthly_trends": trends,
|
||||
"stats": {
|
||||
"linked_paths": len(links),
|
||||
"documents": len(docs),
|
||||
"projects": len(projs),
|
||||
"stores": len(stores),
|
||||
"deals": len(deals),
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user