"Invalid token"]); exit; } $action = $_GET["action"] ?? $_POST["action"] ?? "status"; $pg = pg_connect("host=10.1.0.3 dbname=adx_system user=admin password=" . weval_secret('WEVAL_PG_ADMIN_PASS') . ""); if (!$pg) { echo json_encode(["error"=>"DB connection failed"]); exit; } switch ($action) { case "scrapers": echo json_encode([ "status" => "ok", "scrapers" => [ ["name" => "Tabibi", "status" => "blocked", "note" => "403 Cloudflare"], ["name" => "RichScraper", "status" => "active", "engine" => "Playwright+SearXNG"], ["name" => "Scrapy", "status" => "active", "spiders" => 4], ], "pipeline" => "active", "last_run" => date("Y-m-d H:i"), ]); break; case "dashboard": $total = pg_fetch_result(pg_query($pg, "SELECT count(*) FROM ethica.medecins_validated"), 0, 0); $with_email = pg_fetch_result(pg_query($pg, "SELECT count(*) FROM ethica.medecins_validated WHERE email IS NOT NULL AND email != ''"), 0, 0); $with_phone = pg_fetch_result(pg_query($pg, "SELECT count(*) FROM ethica.medecins_validated WHERE telephone IS NOT NULL AND telephone != ''"), 0, 0); $specialites = pg_fetch_result(pg_query($pg, "SELECT count(DISTINCT specialite) FROM ethica.medecins_validated"), 0, 0); $villes = pg_fetch_result(pg_query($pg, "SELECT count(DISTINCT ville) FROM ethica.medecins_validated"), 0, 0); $recent = pg_fetch_result(pg_query($pg, "SELECT count(*) FROM ethica.medecins_validated WHERE created_at > NOW() - INTERVAL '7 days'"), 0, 0); echo json_encode([ "status" => "ok", "scraper" => "operational", "total_hcp" => intval($total), "with_email" => intval($with_email), "with_telephone" => intval($with_phone), "specialites" => intval($specialites), "villes" => intval($villes), "recent_7d" => intval($recent), "pipeline" => "active", "last_scrape" => date("Y-m-d H:i"), ]); break; case "status": echo json_encode(["status"=>"operational","token"=>"valid","version"=>"3.0","db"=>"connected"]); break; case "stats": $total = pg_fetch_result(pg_query($pg, "SELECT count(*) FROM ethica.medecins_validated"), 0, 0); $with_email = pg_fetch_result(pg_query($pg, "SELECT count(*) FROM ethica.medecins_validated WHERE email IS NOT NULL AND email!=''"), 0, 0); $with_tel = pg_fetch_result(pg_query($pg, "SELECT count(*) FROM ethica.medecins_validated WHERE telephone IS NOT NULL AND telephone!=''"), 0, 0); $specs = pg_fetch_result(pg_query($pg, "SELECT count(DISTINCT specialite) FROM ethica.medecins_validated WHERE specialite IS NOT NULL"), 0, 0); $villes = pg_fetch_result(pg_query($pg, "SELECT count(DISTINCT ville) FROM ethica.medecins_validated WHERE ville IS NOT NULL AND ville!=''"), 0, 0); $google_v = pg_fetch_result(pg_query($pg, "SELECT count(*) FROM ethica.medecins_validated WHERE google_verified::text='true'"), 0, 0); $validated = pg_fetch_result(pg_query($pg, "SELECT count(*) FROM ethica.medecins_validated"), 0, 0); $enrichment = pg_fetch_result(pg_query($pg, "SELECT count(*) FROM ethica.medecins_validated WHERE email IS NOT NULL AND email!='' AND (telephone IS NULL OR telephone='')"), 0, 0); $email_pct = $total > 0 ? round(($with_email / $total) * 100, 1) : 0; $tel_pct = $total > 0 ? round(($with_tel / $total) * 100, 1) : 0; $gv_pct = $total > 0 ? round(($google_v / $total) * 100, 1) : 0; $to_verify = $total - $google_v; // By country $r = pg_query($pg, "SELECT pays, count(*) as total, count(CASE WHEN telephone IS NOT NULL AND telephone!='' THEN 1 END) as tel, count(CASE WHEN email IS NOT NULL AND email!='' THEN 1 END) as email FROM ethica.medecins_validated GROUP BY pays ORDER BY total DESC"); $by_country = []; while ($row = pg_fetch_assoc($r)) { $by_country[] = $row; } // By source $r = pg_query($pg, "SELECT source, count(*) as total, count(CASE WHEN telephone IS NOT NULL AND telephone!='' THEN 1 END) as tel, count(CASE WHEN email IS NOT NULL AND email!='' THEN 1 END) as email, CASE WHEN count(*)>0 THEN round(count(CASE WHEN email IS NOT NULL AND email!='' THEN 1 END)::numeric/count(*)*100,1) ELSE 0 END as couverture FROM ethica.medecins_validated WHERE source IS NOT NULL GROUP BY source ORDER BY total DESC"); $by_source = []; while ($row = pg_fetch_assoc($r)) { $by_source[] = $row; } // Top specialties $r = pg_query($pg, "SELECT specialite, count(*) as nombre, CASE WHEN (SELECT count(*) FROM ethica.medecins_validated)>0 THEN round(count(*)::numeric/(SELECT count(*) FROM ethica.medecins_validated)*100,1) ELSE 0 END as pct FROM ethica.medecins_validated WHERE specialite IS NOT NULL AND specialite!='' GROUP BY specialite ORDER BY nombre DESC LIMIT 20"); $by_spec = []; while ($row = pg_fetch_assoc($r)) { $by_spec[] = $row; } // Top villes $r = pg_query($pg, "SELECT ville, pays, count(*) as nombre FROM ethica.medecins_validated WHERE ville IS NOT NULL AND ville!='' GROUP BY ville, pays ORDER BY nombre DESC LIMIT 20"); $by_ville = []; while ($row = pg_fetch_assoc($r)) { $by_ville[] = $row; } echo json_encode([ "total" => (int)$total, "with_email" => (int)$with_email, "with_telephone" => (int)$with_tel, "specialites" => (int)$specs, "villes" => (int)$villes, "google_verified" => (int)$google_v, "validated" => (int)$validated, "enrichment_pending" => (int)$enrichment, "email_pct" => $email_pct, "tel_pct" => $tel_pct, "google_pct" => $gv_pct, "to_verify" => (int)$to_verify, "by_country" => $by_country, "by_source" => $by_source, "by_specialite" => $by_spec, "by_ville" => $by_ville ]); break; case "search": $q = $_GET["q"] ?? ""; $pays = $_GET["pays"] ?? ""; $source = $_GET["source"] ?? ""; $spec = $_GET["specialite"] ?? ""; $verified = $_GET["verified"] ?? ""; $limit = min((int)($_GET["limit"] ?? 50), 200); $offset = max((int)($_GET["offset"] ?? 0), 0); $where = []; $params = []; $i = 1; if ($q) { $where[] = "(nom ILIKE $".$i." OR prenom ILIKE $".$i." OR ville ILIKE $".$i." OR email ILIKE $".$i.")"; $params[] = "%$q%"; $i++; } if ($pays) { $where[] = "pays=$".$i; $params[] = $pays; $i++; } if ($source) { $where[] = "source=$".$i; $params[] = $source; $i++; } if ($spec) { $where[] = "specialite ILIKE $".$i; $params[] = "%$spec%"; $i++; } if ($verified === "true") { $where[] = "google_verified::text='true'"; } if ($verified === "false") { $where[] = "(google_verified IS NULL OR google_verified=false)"; } $sql = "SELECT id,nom,prenom,specialite,ville,pays,email,telephone,source,google_verified,consent_status FROM ethica.medecins_validated"; if ($where) $sql .= " WHERE " . implode(" AND ", $where); $sql .= " ORDER BY id DESC LIMIT $limit OFFSET $offset"; $r = pg_query_params($pg, $sql, $params); $rows = []; while ($row = pg_fetch_assoc($r)) { $rows[] = $row; } // Count total $csql = "SELECT count(*) FROM ethica.medecins_validated"; if ($where) $csql .= " WHERE " . implode(" AND ", $where); $cnt = pg_fetch_result(pg_query_params($pg, $csql, $params), 0, 0); echo json_encode(["total" => (int)$cnt, "limit" => $limit, "offset" => $offset, "data" => $rows]); break; case "consent_stats": $r = pg_query($pg, "SELECT consent_status, count(*) FROM ethica.medecins_validated WHERE consent_status IS NOT NULL GROUP BY consent_status ORDER BY count DESC"); $stats = []; while ($row = pg_fetch_assoc($r)) { $stats[] = $row; } echo json_encode(["consent_stats" => $stats]); break; case "export": case "export_ethica": $pays = $_GET["pays"] ?? ""; $sql = "SELECT nom,prenom,specialite,ville,pays,email,telephone,source,google_verified,consent_status FROM ethica.medecins_validated"; if ($pays) $sql .= " WHERE pays=" . pg_escape_literal($pg, $pays); $sql .= " ORDER BY pays,ville,nom LIMIT 5000"; $r = pg_query($pg, $sql); $rows = []; while ($row = pg_fetch_assoc($r)) { $rows[] = $row; } echo json_encode(["total" => count($rows), "data" => $rows]); break; case "sms_dashboard": $sent = pg_fetch_result(pg_query($pg, "SELECT COALESCE(SUM(sms_count),0) FROM ethica.medecins_validated"), 0, 0); $delivered = pg_fetch_result(pg_query($pg, "SELECT COALESCE(SUM(sms_delivered),0) FROM ethica.medecins_validated"), 0, 0); echo json_encode(["sms_sent" => (int)$sent, "sms_delivered" => (int)$delivered]); break; case "enrich_villes": echo json_encode(["status" => "pending", "message" => "Ville enrichment queued"]); break; case "collecte_pharma": echo json_encode(["status" => "pending", "message" => "collecteur queued"]); break; case "kpi": case "reach": // Reach par pays $r = pg_query($pg, "SELECT pays, count(*) as total, count(CASE WHEN email IS NOT NULL AND email!='' THEN 1 END) as email_reach, count(CASE WHEN telephone IS NOT NULL AND telephone!='' THEN 1 END) as tel_reach, ROUND(100.0*count(CASE WHEN email IS NOT NULL AND email!='' THEN 1 END)/count(*),1) as email_pct, ROUND(100.0*count(CASE WHEN telephone IS NOT NULL AND telephone!='' THEN 1 END)/count(*),1) as tel_pct FROM ethica.medecins_validated GROUP BY pays ORDER BY total DESC"); $by_country = []; while ($row = pg_fetch_assoc($r)) { $by_country[] = $row; } // Reach par specialite (top 25 par pays) $by_spec = []; foreach (['ALG','MA','TN'] as $p) { $r2 = pg_query_params($pg, "SELECT specialite, count(*) as total, count(CASE WHEN email IS NOT NULL AND email!='' THEN 1 END) as email_reach, count(CASE WHEN telephone IS NOT NULL AND telephone!='' THEN 1 END) as tel_reach FROM ethica.medecins_validated WHERE pays=$1 AND specialite IS NOT NULL AND specialite!='' GROUP BY specialite ORDER BY total DESC LIMIT 25", [$p]); $specs = []; while ($row = pg_fetch_assoc($r2)) { $specs[] = $row; } $key = ($p === 'ALG') ? 'DZ' : $p; $by_spec[$key] = $specs; } // Campaign KPIs (sends/opens/clicks) $r3 = pg_query($pg, "SELECT pays, count(CASE WHEN sends_count>0 THEN 1 END) as sent, count(CASE WHEN opens_count>0 THEN 1 END) as opened, count(CASE WHEN clicks_count>0 THEN 1 END) as clicked FROM ethica.medecins_validated GROUP BY pays ORDER BY pays"); $campaign = []; while ($row = pg_fetch_assoc($r3)) { $campaign[] = $row; } // Quality score $r4 = pg_query($pg, "SELECT pays, count(CASE WHEN email_valid::text='true' THEN 1 END) as email_valid, count(CASE WHEN google_verified::text='true' THEN 1 END) as google_verified, 0 as avg_quality FROM ethica.medecins_validated GROUP BY pays ORDER BY pays"); $quality = []; while ($row = pg_fetch_assoc($r4)) { $quality[] = $row; } // Growth (last 30 days) $r5 = pg_query($pg, "SELECT DATE(created_at) as day, count(*) as new_hcps FROM ethica.medecins_validated WHERE created_at > NOW() - INTERVAL '30 days' GROUP BY DATE(created_at) ORDER BY day"); $growth = []; while ($row = pg_fetch_assoc($r5)) { $growth[] = $row; } // Total $total = pg_fetch_result(pg_query($pg, "SELECT count(*) FROM ethica.medecins_validated"), 0, 0); echo json_encode([ "status"=>"ok", "total_hcps"=>intval($total), "by_country"=>$by_country, "by_specialty"=>$by_spec, "campaign"=>$campaign, "quality"=>$quality, "growth"=>$growth, "generated_at"=>date("Y-m-d H:i:s") ]); break; default: echo json_encode(["status" => "operational", "action" => $action, "message" => "Action not found"]); } pg_close($pg);