216 lines
9.7 KiB
PHP
216 lines
9.7 KiB
PHP
<?php
|
||
/**
|
||
* WEVIA QUALITY ENGINE v1.0 — Lean/Six Sigma/Theory of Constraints
|
||
*
|
||
* Intégré dans le pipeline dev comme étape de contrôle qualité.
|
||
* Contrôlé par WEVIA Master + L99.
|
||
*
|
||
* LEAN: Cycle time, Waste detection, Value Stream Mapping
|
||
* SIX SIGMA: Defect rate, DPMO, Process capability (Cp/Cpk)
|
||
* TOC: Bottleneck identification, 5 Focusing Steps
|
||
* AGILE: Sprint velocity, WIP limits, Lead time
|
||
*/
|
||
header('Content-Type: application/json; charset=utf-8');
|
||
|
||
$action = $_GET['action'] ?? 'dashboard';
|
||
|
||
switch ($action) {
|
||
|
||
case 'dashboard':
|
||
// Collect all quality metrics
|
||
$metrics = ['timestamp' => date('Y-m-d H:i'), 'methodology' => []];
|
||
|
||
// ═══ LEAN METRICS ═══
|
||
$lean = [];
|
||
|
||
// Waste detection: unused routes, orphan files, dead Docker
|
||
$fast = @file_get_contents('/var/www/html/api/weval-ia-fast.php') ?: '';
|
||
$total_routes = substr_count($fast, '// Route');
|
||
|
||
// Dead code: routes that reference non-existent files
|
||
preg_match_all('/file_get_contents\("([^"]+)"\)/', $fast, $file_refs);
|
||
$dead_refs = 0;
|
||
foreach (array_unique($file_refs[1] ?? []) as $ref) {
|
||
if (strpos($ref, 'http') === false && !file_exists($ref)) $dead_refs++;
|
||
}
|
||
$lean['waste_dead_refs'] = $dead_refs;
|
||
|
||
// Orphan files in /api/ not referenced anywhere
|
||
$api_files = glob('/var/www/html/api/*.php');
|
||
$orphans = 0;
|
||
foreach ($api_files as $f) {
|
||
$name = basename($f, '.php');
|
||
if (strlen($name) > 5 && stripos($fast, $name) === false) $orphans++;
|
||
}
|
||
$lean['waste_orphan_apis'] = $orphans;
|
||
|
||
// Docker waste: containers using >500MB
|
||
exec("docker stats --no-stream --format '{{.Name}}:{{.MemUsage}}' 2>/dev/null | head -10", $docker_stats);
|
||
$lean['docker_containers'] = count($docker_stats);
|
||
|
||
// Cycle time: git commits last 24h
|
||
$commits_24h = intval(trim(shell_exec('cd /var/www/html && git log --since="24 hours ago" --oneline 2>/dev/null | wc -l') ?? '0'));
|
||
$lean['cycle_commits_24h'] = $commits_24h;
|
||
|
||
// Value: routes per 100 lines of code
|
||
$fast_lines = intval(trim(shell_exec('wc -l < /var/www/html/api/weval-ia-fast.php 2>/dev/null') ?? '0'));
|
||
$lean['value_routes_per_100L'] = round($total_routes / max($fast_lines, 1) * 100, 1);
|
||
|
||
$lean['score'] = round(100 - ($dead_refs * 5) - ($orphans * 2), 1);
|
||
$metrics['methodology']['lean'] = $lean;
|
||
|
||
// ═══ SIX SIGMA METRICS ═══
|
||
$sigma = [];
|
||
|
||
// Defect rate from L99
|
||
$l99 = json_decode(@file_get_contents('/var/www/html/api/l99-results.json') ?: '{}', true);
|
||
$l99_pass = 0; $l99_total = 0; $l99_fail = 0; $l99_warn = 0;
|
||
if (isset($l99['results'])) {
|
||
foreach ($l99['results'] as $r) {
|
||
$l99_total++;
|
||
if (($r['status'] ?? '') === 'PASS') $l99_pass++;
|
||
elseif (($r['status'] ?? '') === 'FAIL') $l99_fail++;
|
||
else $l99_warn++;
|
||
}
|
||
}
|
||
$sigma['l99_pass'] = $l99_pass;
|
||
$sigma['l99_total'] = $l99_total;
|
||
$sigma['l99_fail'] = $l99_fail;
|
||
$sigma['l99_warn'] = $l99_warn;
|
||
$sigma['defect_rate'] = $l99_total > 0 ? round($l99_fail / $l99_total * 100, 2) : 0;
|
||
|
||
// NonReg
|
||
$nr = json_decode(@file_get_contents('https://weval-consulting.com/api/nonreg-api.php?cat=all') ?: '{}', true);
|
||
$sigma['nonreg_pass'] = $nr['pass'] ?? 0;
|
||
$sigma['nonreg_total'] = $nr['total'] ?? 0;
|
||
$sigma['nonreg_score'] = $nr['score'] ?? 0;
|
||
|
||
// DPMO (defects per million opportunities)
|
||
$total_ops = max($l99_total + ($nr['total'] ?? 0), 1);
|
||
$total_defects = $l99_fail;
|
||
$sigma['dpmo'] = round($total_defects / $total_ops * 1000000);
|
||
|
||
// Sigma level approximation
|
||
$dpmo = $sigma['dpmo'];
|
||
if ($dpmo == 0) $sigma['sigma_level'] = '6σ+';
|
||
elseif ($dpmo < 3.4) $sigma['sigma_level'] = '6σ';
|
||
elseif ($dpmo < 233) $sigma['sigma_level'] = '5σ';
|
||
elseif ($dpmo < 6210) $sigma['sigma_level'] = '4σ';
|
||
elseif ($dpmo < 66807) $sigma['sigma_level'] = '3σ';
|
||
else $sigma['sigma_level'] = '2σ';
|
||
|
||
$metrics['methodology']['six_sigma'] = $sigma;
|
||
|
||
// ═══ THEORY OF CONSTRAINTS ═══
|
||
$toc = [];
|
||
|
||
// Identify bottleneck: slowest component
|
||
$bottlenecks = [];
|
||
|
||
// RAM constraint
|
||
$ram_used = intval(trim(shell_exec("free -m | grep Mem | awk '{print $3}'") ?? '0'));
|
||
$ram_total = intval(trim(shell_exec("free -m | grep Mem | awk '{print $2}'") ?? '1'));
|
||
$ram_pct = round($ram_used / max($ram_total, 1) * 100);
|
||
$bottlenecks['ram'] = ['usage' => "{$ram_pct}%", 'constraint' => $ram_pct > 85];
|
||
|
||
// Disk constraint
|
||
$disk_pct = intval(trim(shell_exec("df -h / | tail -1 | awk '{print $5}' | tr -d '%'") ?? '0'));
|
||
$bottlenecks['disk'] = ['usage' => "{$disk_pct}%", 'constraint' => $disk_pct > 90];
|
||
|
||
// CPU constraint
|
||
$load = floatval(trim(shell_exec('cat /proc/loadavg | cut -d" " -f1') ?? '0'));
|
||
$cores = intval(trim(shell_exec('nproc') ?? '4'));
|
||
$bottlenecks['cpu'] = ['load' => $load, 'cores' => $cores, 'constraint' => $load > $cores * 0.8];
|
||
|
||
// API rate limit constraint
|
||
$gap = json_decode(@file_get_contents('/var/www/html/api/gap-detector.json') ?: '{}', true);
|
||
$bottlenecks['wiring'] = ['score' => ($gap['score'] ?? 0) . '%', 'constraint' => ($gap['score'] ?? 100) < 80];
|
||
|
||
// Find THE bottleneck (5 Focusing Steps)
|
||
$main_bottleneck = null;
|
||
foreach ($bottlenecks as $name => $b) {
|
||
if ($b['constraint']) { $main_bottleneck = $name; break; }
|
||
}
|
||
$toc['bottlenecks'] = $bottlenecks;
|
||
$toc['main_constraint'] = $main_bottleneck ?? 'none';
|
||
$toc['five_steps'] = [
|
||
'1_identify' => $main_bottleneck ? "Constraint: $main_bottleneck" : "No active constraint",
|
||
'2_exploit' => $main_bottleneck ? "Maximize throughput of $main_bottleneck" : "System balanced",
|
||
'3_subordinate' => "Other processes aligned to constraint",
|
||
'4_elevate' => $main_bottleneck ? "Add capacity to $main_bottleneck" : "Monitor for new constraints",
|
||
'5_repeat' => "Check if constraint has shifted",
|
||
];
|
||
|
||
$metrics['methodology']['toc'] = $toc;
|
||
|
||
// ═══ AGILE METRICS ═══
|
||
$agile = [];
|
||
$agile['sprint_velocity'] = $commits_24h; // commits = velocity proxy
|
||
$agile['wip'] = intval(trim(shell_exec('cd /var/www/html && git status --porcelain 2>/dev/null | wc -l') ?? '0'));
|
||
$agile['lead_time_estimate'] = $commits_24h > 10 ? 'fast (<1h)' : ($commits_24h > 3 ? 'medium (1-4h)' : 'slow (>4h)');
|
||
$agile['routes_total'] = $total_routes;
|
||
$agile['test_coverage'] = round(($sigma['nonreg_pass'] ?? 0) / max(($sigma['nonreg_total'] ?? 1), 1) * 100, 1) . '%';
|
||
|
||
$metrics['methodology']['agile'] = $agile;
|
||
|
||
// ═══ GLOBAL QUALITY SCORE ═══
|
||
$lean_score = $lean['score'];
|
||
$sigma_score = $sigma['defect_rate'] == 0 ? 100 : max(0, 100 - $sigma['defect_rate'] * 10);
|
||
$toc_score = $main_bottleneck ? 70 : 100;
|
||
$agile_score = min(100, $commits_24h * 5 + 50);
|
||
|
||
$metrics['global_score'] = round(($lean_score + $sigma_score + $toc_score + $agile_score) / 4, 1);
|
||
$metrics['grade'] = $metrics['global_score'] >= 90 ? 'A+' : ($metrics['global_score'] >= 80 ? 'A' : ($metrics['global_score'] >= 70 ? 'B' : 'C'));
|
||
|
||
echo json_encode($metrics, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||
break;
|
||
|
||
case 'lean':
|
||
// Detailed Lean analysis
|
||
echo json_encode(['action' => 'lean', 'note' => 'Use dashboard for full metrics']);
|
||
break;
|
||
|
||
case 'toc':
|
||
// Theory of Constraints deep analysis
|
||
echo json_encode(['action' => 'toc', 'note' => 'Use dashboard for full metrics']);
|
||
break;
|
||
|
||
case 'evolve':
|
||
// L99 AUTO-EVOLUTION: scan architecture changes and add new tests
|
||
$changes = [];
|
||
|
||
// Check for new PHP files not in L99
|
||
$api_files = glob('/var/www/html/api/*.php');
|
||
$l99_content = @file_get_contents('/opt/weval-l99/l99-functional-test.py') ?: '';
|
||
$new_apis = [];
|
||
foreach ($api_files as $f) {
|
||
$name = basename($f, '.php');
|
||
if (strlen($name) > 8 && stripos($l99_content, $name) === false) {
|
||
$new_apis[] = $name;
|
||
}
|
||
}
|
||
$changes['new_apis_not_in_l99'] = $new_apis;
|
||
|
||
// Check for new crons
|
||
$cron_files = glob('/etc/cron.d/weval-*');
|
||
$changes['total_crons'] = count($cron_files);
|
||
|
||
// Check routes growth
|
||
$fast = @file_get_contents('/var/www/html/api/weval-ia-fast.php') ?: '';
|
||
$changes['total_routes'] = substr_count($fast, '// Route');
|
||
|
||
// Suggest new L99 tests
|
||
$suggestions = [];
|
||
foreach (array_slice($new_apis, 0, 5) as $api) {
|
||
$suggestions[] = "Add L99 test for /api/$api.php (HTTP 200 + JSON valid)";
|
||
}
|
||
$changes['l99_suggestions'] = $suggestions;
|
||
$changes['l99_current_scripts'] = intval(trim(shell_exec('ls /opt/weval-l99/l99-*.py 2>/dev/null | wc -l') ?? '0'));
|
||
|
||
echo json_encode($changes, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||
break;
|
||
|
||
default:
|
||
echo json_encode(['engine' => 'WEVIA Quality v1.0', 'actions' => ['dashboard', 'lean', 'toc', 'evolve']]);
|
||
}
|