119 lines
5.1 KiB
PHP
119 lines
5.1 KiB
PHP
<?php
|
||
// V84 LinkedIn × Archi score — computed live (not hardcoded)
|
||
// Replaces hardcoded audit_score: 4.8 with real calculation from 10 KPIs
|
||
header("Content-Type: application/json");
|
||
|
||
$base_kpi = @json_decode(@file_get_contents("http://127.0.0.1/api/linkedin-alignment-kpi.php"), true);
|
||
if (!$base_kpi) { echo json_encode(['error'=>'base KPI fetch failed']); exit; }
|
||
|
||
$kpis = $base_kpi['kpis'] ?? [];
|
||
|
||
// Score weights per KPI status
|
||
function score_kpi($kpi) {
|
||
$s = strtoupper($kpi['status'] ?? 'TBD');
|
||
switch ($s) {
|
||
case 'OK': return 1.0;
|
||
case 'BELOW': return 0.3;
|
||
case 'SKEWED': return 0.4;
|
||
case 'CRITICAL': return 0.0;
|
||
case 'PENDING': return 0.5;
|
||
case 'TBD': return 0.5;
|
||
default: return 0.3;
|
||
}
|
||
}
|
||
|
||
$total_score = 0;
|
||
$max_possible = count($kpis); // 1 point per KPI
|
||
$breakdown = [];
|
||
$levers = [];
|
||
|
||
foreach ($kpis as $key => $kpi) {
|
||
$pts = score_kpi($kpi);
|
||
$total_score += $pts;
|
||
$status = $kpi['status'] ?? 'TBD';
|
||
$value = $kpi['value'] ?? 0;
|
||
$target = $kpi['target'] ?? $kpi['target_range'][1] ?? '—';
|
||
$breakdown[$key] = [
|
||
'status' => $status,
|
||
'points' => $pts,
|
||
'max_points' => 1.0,
|
||
'value' => $value,
|
||
'target' => $target,
|
||
];
|
||
if ($pts < 1.0) {
|
||
$potential_gain = 1.0 - $pts;
|
||
$levers[] = [
|
||
'kpi' => $key,
|
||
'current_status' => $status,
|
||
'potential_gain' => round($potential_gain, 2),
|
||
'action' => get_lever_action($key, $kpi),
|
||
'priority' => $potential_gain >= 0.7 ? 'HIGH' : ($potential_gain >= 0.4 ? 'MEDIUM' : 'LOW'),
|
||
];
|
||
}
|
||
}
|
||
|
||
function get_lever_action($key, $kpi) {
|
||
switch ($key) {
|
||
case 'risky_claims':
|
||
$posts = $kpi['posts'] ?? [];
|
||
return 'OWNER: rewrite ' . count($posts) . ' posts removing claims "+500%" / "52 domaines" / "launch in days" — specifically: ' . implode(' | ', array_slice($posts, 0, 3));
|
||
case 'tagline_compliance':
|
||
return 'OWNER: Deploy V1 tagline consistent across corp + LS accounts + update LinkedIn headline';
|
||
case 'avg_reach_30d':
|
||
return 'OWNER: (1) Post more regularly 3-5x/week (2) Use native video+docs formats (3) Engage first 1h (4) Target: 800+ views/post';
|
||
case 'posts_with_metric':
|
||
return 'OWNER: Add concrete numbers to each post (client count, ROI%, hours saved, clients onboarded) — chiffres-chocs rule';
|
||
case 'unique_proofs_cited':
|
||
return 'OWNER: Track /live-status landing hits + cite 15+ unique proofs/month (MRR, clients, ARR, NPS live)';
|
||
case 'linkedin_to_demo':
|
||
return 'OWNER: Wire landing /demo tracking → target 30 discovery calls/month from LinkedIn CTAs';
|
||
case 'named_cases_month':
|
||
return 'OWNER: Publish 2+ named case studies/month (Ethica, Vistex, Huawei) with client approval';
|
||
case 'account_parity':
|
||
return 'Balance corp vs LS post ratio between 0.8-1.2';
|
||
case 'public_services_up':
|
||
return 'Keep public services >80% uptime — check realtime-status';
|
||
case 'engagement_rate_30d':
|
||
return 'Maintain current 4.02% (already OK)';
|
||
default:
|
||
return 'Review KPI source';
|
||
}
|
||
}
|
||
|
||
$score_live = round($total_score / $max_possible * 10, 1); // normalize to /10
|
||
$score_hardcoded = $base_kpi['audit_score'] ?? 4.8;
|
||
|
||
// Max potential score = if all BELOW/CRITICAL become OK
|
||
$max_potential_score = 0;
|
||
foreach ($kpis as $key => $kpi) {
|
||
$status = $kpi['status'] ?? 'TBD';
|
||
if (in_array($status, ['OK'])) $max_potential_score += 1.0;
|
||
else $max_potential_score += 0.95; // realistic achievable after owner action
|
||
}
|
||
$max_potential_normalized = round($max_potential_score / $max_possible * 10, 1);
|
||
|
||
// Sort levers by priority
|
||
usort($levers, function($a,$b){ return $b['potential_gain'] <=> $a['potential_gain']; });
|
||
|
||
echo json_encode([
|
||
'v' => 'V84-linkedin-archi-score-live-computed',
|
||
'ts' => date('c'),
|
||
'score_live_computed' => $score_live,
|
||
'score_hardcoded_outdated' => $score_hardcoded,
|
||
'score_max' => 10.0,
|
||
'max_potential_after_owner_actions' => $max_potential_normalized,
|
||
'potential_gain' => round($max_potential_normalized - $score_live, 1),
|
||
'total_kpis' => $max_possible,
|
||
'kpis_ok' => count(array_filter($breakdown, fn($k) => $k['status']==='OK')),
|
||
'kpis_critical' => count(array_filter($breakdown, fn($k) => $k['status']==='CRITICAL')),
|
||
'kpis_below' => count(array_filter($breakdown, fn($k) => $k['status']==='BELOW')),
|
||
'breakdown' => $breakdown,
|
||
'levers_prioritized' => $levers,
|
||
'owner_action_plan' => [
|
||
'P0_immediate' => array_values(array_filter($levers, fn($l) => $l['priority']==='HIGH')),
|
||
'P1_week' => array_values(array_filter($levers, fn($l) => $l['priority']==='MEDIUM')),
|
||
'P2_month' => array_values(array_filter($levers, fn($l) => $l['priority']==='LOW')),
|
||
],
|
||
'doctrine_4_honest' => 'score 4.8 was HARDCODED in linkedin-alignment-kpi.php line 86, now computed live from 10 KPIs status weights. Max theoretical 9.5+ reachable after owner rewrites 3 risky posts + deploys tagline V1 + tracks reach metrics',
|
||
], JSON_PRETTY_PRINT);
|