Files
html/api/feature-adoption.php

97 lines
3.5 KiB
PHP

<?php
/**
* WEVAL Feature Adoption Tracker sovereign 21avr2026
* Track which modules/widgets/features users interact with per session
* Storage: /opt/weval-l99/data/feature-adoption.jsonl
*
* GET ?action=stats -> adoption rate KPI
* POST ?action=track -> log feature use (feature, user, session_id)
* GET ?action=list -> features inventory
*/
header("Content-Type: application/json");
header("Access-Control-Allow-Origin: *");
$STORAGE = "/opt/weval-l99/data/feature-adoption.jsonl";
$INVENTORY = "/opt/weval-l99/data/feature-inventory.json";
@mkdir(dirname($STORAGE), 0755, true);
// Default inventory if not exists
if (!file_exists($INVENTORY)) {
$default_features = [
"wtp_dashboard", "wtp_pilotage_widget", "wtp_sparklines", "wtp_l99_brain",
"wevia_master_chat", "wevia_orchestrator", "all_ia_hub", "agents_archi",
"architecture_live", "openclaw", "nonreg_dashboard", "stripe_live",
"ethica_hcp", "wevads_ia", "director_chat", "orphans_hub",
"wikidoc", "vault_manager", "nps_feedback", "csat_rating", "ticket_create"
];
@file_put_contents($INVENTORY, json_encode(["features" => $default_features, "total" => count($default_features)]));
}
$action = $_GET["action"] ?? ($_POST["action"] ?? "stats");
if ($action === "track" && $_SERVER["REQUEST_METHOD"] === "POST") {
$feature = substr(trim($_POST["feature"] ?? ""), 0, 80);
$user = substr(trim($_POST["user"] ?? "anonymous"), 0, 60);
$session = substr(trim($_POST["session_id"] ?? ""), 0, 40);
if (!$feature) {
echo json_encode(["ok"=>false,"error"=>"feature_required"]);
exit;
}
$record = ["ts"=>date("c"),"feature"=>$feature,"user"=>$user,"session_id"=>$session];
@file_put_contents($STORAGE, json_encode($record)."\n", FILE_APPEND | LOCK_EX);
echo json_encode(["ok"=>true,"tracked"=>$feature]);
exit;
}
if ($action === "list") {
$inv = @json_decode(@file_get_contents($INVENTORY), true);
echo json_encode($inv ?: ["features"=>[],"total"=>0]);
exit;
}
// stats = adoption rate calculation
$inv = @json_decode(@file_get_contents($INVENTORY), true);
$total_features = intval($inv["total"] ?? 0);
$used_features = [];
$events = [];
if (is_readable($STORAGE)) {
foreach (file($STORAGE) as $line) {
$r = @json_decode(trim($line), true);
if ($r && isset($r["feature"])) {
$used_features[$r["feature"]] = ($used_features[$r["feature"]] ?? 0) + 1;
$events[] = $r;
}
}
}
$adopted = count($used_features);
$adoption_rate = $total_features > 0 ? round(($adopted / $total_features) * 100) : 0;
// 7d & 30d activity
$now = time();
$evt_7d = 0; $evt_30d = 0;
$users_7d = []; $users_30d = [];
foreach ($events as $e) {
$t = strtotime($e["ts"]);
if ($t >= $now - 7*86400) { $evt_7d++; $users_7d[$e["user"] ?? ""] = true; }
if ($t >= $now - 30*86400) { $evt_30d++; $users_30d[$e["user"] ?? ""] = true; }
}
echo json_encode([
"ok"=>true,
"source"=>"sovereign_jsonl_tracker",
"ts"=>date("c"),
"adoption_rate_pct"=>$adoption_rate,
"features_total"=>$total_features,
"features_adopted"=>$adopted,
"features_top5"=>array_slice(array_reverse(array_keys($used_features)), 0, 5),
"events_total"=>count($events),
"events_7d"=>$evt_7d,
"events_30d"=>$evt_30d,
"unique_users_7d"=>count($users_7d),
"unique_users_30d"=>count($users_30d),
"status"=>count($events)===0 ? "wire_needed" : ($adoption_rate >= 70 ? "ok" : "warn"),
"drill"=>"Features used / Features available · Track via POST ?action=track",
]);