"""NAS folder structure per client and project — geen alles-op-een-hoop.""" from __future__ import annotations import json import os import re from pathlib import Path from typing import Any, Optional from app.db import execute, fetch_one NAS_ROOT = Path(os.getenv("NAS_CLIENTS_ROOT", "/data/nas-clients")) PROJECT_SUBDIRS = ("photos", "documents", "packaging", "exports", "briefs") PROJECT_TYPES = [ {"id": "packaging", "label_nl": "Verpakking & label", "label_en": "Packaging & label", "icon": "📦", "nas_sub": "packaging"}, {"id": "retail_listing", "label_nl": "Retail listing / schap", "label_en": "Retail listing", "icon": "🏪", "nas_sub": "documents"}, {"id": "recipe", "label_nl": "Recept & productontwikkeling", "label_en": "Recipe & R&D", "icon": "🍱", "nas_sub": "briefs"}, {"id": "marketing", "label_nl": "Marketing campagne", "label_en": "Marketing campaign", "icon": "📣", "nas_sub": "exports"}, {"id": "halal", "label_nl": "Halal certificering", "label_en": "Halal certification", "icon": "☪️", "nas_sub": "documents"}, {"id": "sourcing", "label_nl": "Sourcing & import", "label_en": "Sourcing & import", "icon": "🚢", "nas_sub": "documents"}, {"id": "crm", "label_nl": "Klant & partnership", "label_en": "Client & partnership", "icon": "🤝", "nas_sub": "documents"}, {"id": "research", "label_nl": "Marktonderzoek", "label_en": "Market research", "icon": "🔬", "nas_sub": "briefs"}, {"id": "general", "label_nl": "Algemeen project", "label_en": "General project", "icon": "📁", "nas_sub": "documents"}, ] def slugify(name: str, max_len: int = 48) -> str: s = re.sub(r"[^a-zA-Z0-9]+", "-", (name or "project").strip().lower()).strip("-") return (s[:max_len] or "project") def _write_meta(path: Path, data: dict[str, Any]) -> None: path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding="utf-8") def ensure_client_folder(client_id: int, client_name: str) -> str: """Maak NAS-map per klant: clients/{slug}/ met submappen.""" slug = slugify(client_name) root = NAS_ROOT / slug for sub in ("projects", "photos", "documents", "inbox"): (root / sub).mkdir(parents=True, exist_ok=True) meta = { "client_id": client_id, "client_name": client_name, "slug": slug, "structure": ["projects", "photos", "documents", "inbox"], } _write_meta(root / "client.json", meta) rel = str(root) execute("UPDATE clients SET nas_folder = %s WHERE id = %s", (rel, client_id)) return rel def ensure_project_folder( project_id: int, project_name: str, client_id: Optional[int], client_name: Optional[str], project_type: str = "general", ) -> dict[str, Any]: """Projectmap onder klant: clients/{client}/projects/{project}/""" client_root: Path | None = None if client_id and client_name: row = fetch_one("SELECT nas_folder FROM clients WHERE id = %s", (client_id,)) if row and row.get("nas_folder"): client_root = Path(row["nas_folder"]) else: client_root = Path(ensure_client_folder(client_id, client_name)) else: client_root = NAS_ROOT / "_geen-klant" client_root.mkdir(parents=True, exist_ok=True) proj_slug = f"{project_id}-{slugify(project_name)}" proj_root = client_root / "projects" / proj_slug for sub in PROJECT_SUBDIRS: (proj_root / sub).mkdir(parents=True, exist_ok=True) type_info = next((t for t in PROJECT_TYPES if t["id"] == project_type), PROJECT_TYPES[-1]) _write_meta( proj_root / "project.json", { "project_id": project_id, "name": project_name, "client_id": client_id, "project_type": project_type, "folders": list(PROJECT_SUBDIRS), "primary_sub": type_info.get("nas_sub", "documents"), }, ) nas_path = str(proj_root) client_rel = str(client_root) execute( """UPDATE cockpit_projects SET nas_path = %s, nas_client_root = %s, updated_at = NOW() WHERE id = %s""", (nas_path, client_rel, project_id), ) return {"nas_path": nas_path, "nas_client_root": client_rel, "project_slug": proj_slug} def list_types() -> list[dict[str, Any]]: return list(PROJECT_TYPES)