97 lines
3.5 KiB
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",
|
|
]);
|