246 lines
13 KiB
PHP
246 lines
13 KiB
PHP
<?php
|
|
|
|
// === INPUT SANITIZATION ===
|
|
function weval_input($key, $type='string', $method='GET') {
|
|
$src = $method === 'POST' ? INPUT_POST : INPUT_GET;
|
|
$val = filter_input($src, $key, FILTER_SANITIZE_FULL_SPECIAL_CHARS);
|
|
if ($val === null || $val === false) {
|
|
$val = ($method === 'POST') ? ($_POST[$key] ?? '') : ($_GET[$key] ?? '');
|
|
$val = htmlspecialchars(strip_tags(trim($val)), ENT_QUOTES, 'UTF-8');
|
|
}
|
|
if ($type === 'int') return intval($val);
|
|
if ($type === 'email') return filter_var($val, FILTER_SANITIZE_EMAIL);
|
|
return $val;
|
|
}
|
|
|
|
require_once __DIR__ . '/_secrets.php';
|
|
header("Content-Type: application/json");
|
|
header("Access-Control-Allow-Origin: *");
|
|
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
|
|
header("Access-Control-Allow-Headers: Content-Type");
|
|
if ($_SERVER["REQUEST_METHOD"] === "OPTIONS") { http_response_code(200); exit; }
|
|
|
|
$token = $_GET["token"] ?? $_POST["token"] ?? "";
|
|
if ($token !== "ETHICA_API_2026_SECURE") { echo json_encode(["error"=>"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);
|