181 lines
5.6 KiB
Python
181 lines
5.6 KiB
Python
import json
|
|
import os
|
|
from datetime import datetime
|
|
from typing import List, Dict, Any, Optional
|
|
|
|
|
|
def _project_root() -> str:
|
|
# modules/crm/search.py -> remonter à la racine projet
|
|
return os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
|
|
def _prospects_dir() -> str:
|
|
return os.path.join(_project_root(), "Data", "prospects")
|
|
|
|
|
|
def _read_json(path: str) -> Optional[Dict[str, Any]]:
|
|
try:
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
return json.load(f)
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
def _norm_date(date_str: Optional[str]) -> Optional[str]:
|
|
if not date_str:
|
|
return None
|
|
# Tente de normaliser vers YYYY-MM-DD
|
|
s = str(date_str)
|
|
# Si déjà au format ISO ou contient "T"
|
|
if len(s) >= 10:
|
|
return s[:10]
|
|
return None
|
|
|
|
|
|
def _extract_fields(data: Dict[str, Any], fallback_id: str) -> Dict[str, Any]:
|
|
# Essaye plusieurs variantes de clés
|
|
name = data.get("name") or data.get("nom") or data.get("full_name") or data.get("contact_name") or ""
|
|
email = data.get("email") or data.get("mail") or ""
|
|
company = data.get("company") or data.get("societe") or data.get("entreprise") or data.get("organization") or ""
|
|
status = data.get("status") or data.get("statut") or ""
|
|
city = data.get("city") or data.get("ville") or ""
|
|
tags = data.get("tags") or []
|
|
if isinstance(tags, str):
|
|
# transformer "a, b, c" -> ["a","b","c"]
|
|
tags = [t.strip() for t in tags.split(",") if t.strip()]
|
|
created_at = data.get("created_at") or data.get("created") or data.get("date_created") or ""
|
|
created_at = _norm_date(created_at) or ""
|
|
pid = data.get("id") or fallback_id
|
|
|
|
# Liaisons potentielles
|
|
client_id = data.get("client_id") or data.get("clientId") or data.get("client")
|
|
project_ids = data.get("project_ids") or data.get("projects") or data.get("projectId") or []
|
|
if isinstance(project_ids, (str, int)):
|
|
project_ids = [str(project_ids)]
|
|
elif isinstance(project_ids, list):
|
|
project_ids = [str(x) for x in project_ids]
|
|
|
|
return {
|
|
"id": pid,
|
|
"entity_type": "prospect",
|
|
"name": name,
|
|
"email": email,
|
|
"company": company,
|
|
"status": status,
|
|
"city": city,
|
|
"tags": tags,
|
|
"created_at": created_at,
|
|
"client_id": client_id,
|
|
"project_ids": project_ids,
|
|
}
|
|
|
|
|
|
def load_all_prospects() -> List[Dict[str, Any]]:
|
|
base_dir = _prospects_dir()
|
|
os.makedirs(base_dir, exist_ok=True)
|
|
results: List[Dict[str, Any]] = []
|
|
for fn in os.listdir(base_dir):
|
|
if not fn.endswith(".json"):
|
|
continue
|
|
path = os.path.join(base_dir, fn)
|
|
data = _read_json(path)
|
|
if not data:
|
|
continue
|
|
pid = os.path.splitext(fn)[0]
|
|
results.append(_extract_fields(data, pid))
|
|
return results
|
|
|
|
|
|
def _contains(hay: str, needle: str) -> bool:
|
|
return needle in hay
|
|
|
|
|
|
def _safe_lower(s: Optional[str]) -> str:
|
|
return (s or "").lower()
|
|
|
|
|
|
def filter_prospects(
|
|
*,
|
|
q: Optional[str] = None,
|
|
name: Optional[str] = None,
|
|
email: Optional[str] = None,
|
|
company: Optional[str] = None,
|
|
status: Optional[str] = None,
|
|
city: Optional[str] = None,
|
|
tags: Optional[List[str]] = None,
|
|
date_from: Optional[str] = None, # YYYY-MM-DD
|
|
date_to: Optional[str] = None, # YYYY-MM-DD
|
|
) -> List[Dict[str, Any]]:
|
|
items = load_all_prospects()
|
|
|
|
q = _safe_lower(q)
|
|
name = _safe_lower(name)
|
|
email = _safe_lower(email)
|
|
company = _safe_lower(company)
|
|
status = _safe_lower(status)
|
|
city = _safe_lower(city)
|
|
tags = [t.strip().lower() for t in (tags or []) if t.strip()]
|
|
|
|
df = None
|
|
dt = None
|
|
try:
|
|
if date_from:
|
|
df = datetime.strptime(date_from, "%Y-%m-%d").date()
|
|
except Exception:
|
|
df = None
|
|
try:
|
|
if date_to:
|
|
dt = datetime.strptime(date_to, "%Y-%m-%d").date()
|
|
except Exception:
|
|
dt = None
|
|
|
|
out: List[Dict[str, Any]] = []
|
|
for it in items:
|
|
iname = _safe_lower(it.get("name"))
|
|
iemail = _safe_lower(it.get("email"))
|
|
icompany = _safe_lower(it.get("company"))
|
|
istatus = _safe_lower(it.get("status"))
|
|
icity = _safe_lower(it.get("city"))
|
|
itags = [str(t).lower() for t in (it.get("tags") or [])]
|
|
icreated = it.get("created_at") or ""
|
|
icreated_d = None
|
|
try:
|
|
if icreated:
|
|
icreated_d = datetime.strptime(icreated[:10], "%Y-%m-%d").date()
|
|
except Exception:
|
|
icreated_d = None
|
|
|
|
# Filtre global q
|
|
if q and not (
|
|
_contains(iname, q) or _contains(iemail, q) or _contains(icompany, q) or _contains(icity, q)
|
|
):
|
|
continue
|
|
|
|
# Filtres spécifiques
|
|
if name and not _contains(iname, name):
|
|
continue
|
|
if email and not _contains(iemail, email):
|
|
continue
|
|
if company and not _contains(icompany, company):
|
|
continue
|
|
if status and istatus != status:
|
|
continue
|
|
if city and not _contains(icity, city):
|
|
continue
|
|
|
|
if tags:
|
|
# exiger que tous les tags demandés soient présents
|
|
itags_set = set(itags)
|
|
if not all(tag in itags_set for tag in tags):
|
|
continue
|
|
|
|
if df and (not icreated_d or icreated_d < df):
|
|
continue
|
|
if dt and (not icreated_d or icreated_d > dt):
|
|
continue
|
|
|
|
out.append(it)
|
|
|
|
# On peut trier par date de création desc, puis nom
|
|
out.sort(key=lambda d: (d.get("created_at") or "", d.get("name") or ""), reverse=True)
|
|
return out
|