- Move 12 markdown files from root to docs/ subdirectories - Organize documentation by category: • docs/troubleshooting/ (1 file) - Technical troubleshooting guides • docs/deployment/ (4 files) - Deployment and security documentation • docs/guides/ (3 files) - Feature-specific guides • docs/planning/ (4 files) - Planning and improvement proposals Root directory cleanup: - Reduced from 16 to 4 markdown files in root - Only essential project files remain: • CLAUDE.md (AI instructions) • README.md (Main project readme) • CLEANUP_PLAN.md (Current cleanup plan) • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements) This improves: ✅ Documentation discoverability ✅ Logical organization by purpose ✅ Clean root directory ✅ Better maintainability
154 lines
4.9 KiB
PHP
154 lines
4.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\Console\Analytics;
|
|
|
|
use App\Framework\Console\Analytics\Repository\CommandUsageRepository;
|
|
use App\Framework\Console\Analytics\ValueObjects\Period;
|
|
use App\Framework\Console\Analytics\ValueObjects\UsageStatistics;
|
|
use DateTimeImmutable;
|
|
|
|
final readonly class AnalyticsService
|
|
{
|
|
public function __construct(
|
|
private CommandUsageRepository $repository
|
|
) {
|
|
}
|
|
|
|
public function getCommandStatistics(string $commandName, ?DateTimeImmutable $since = null): UsageStatistics
|
|
{
|
|
return $this->repository->getUsageStatistics($commandName, $since);
|
|
}
|
|
|
|
public function getPopularCommands(int $limit = 10, ?DateTimeImmutable $since = null): array
|
|
{
|
|
return $this->repository->getPopularCommands($limit, $since);
|
|
}
|
|
|
|
public function getCommandTrends(
|
|
string $commandName,
|
|
Period $period = Period::DAILY,
|
|
?DateTimeImmutable $start = null,
|
|
?DateTimeImmutable $end = null
|
|
): array {
|
|
$start ??= new DateTimeImmutable('-30 days');
|
|
$end ??= new DateTimeImmutable();
|
|
|
|
return $this->repository->getTrendData($commandName, $period, $start, $end);
|
|
}
|
|
|
|
public function getAllCommandNames(): array
|
|
{
|
|
return $this->repository->getAllCommandNames();
|
|
}
|
|
|
|
public function cleanupOldData(int $daysToKeep = 90): int
|
|
{
|
|
$cutoffDate = new DateTimeImmutable("-{$daysToKeep} days");
|
|
|
|
return $this->repository->cleanup($cutoffDate);
|
|
}
|
|
|
|
public function getOverallStatistics(?DateTimeImmutable $since = null): array
|
|
{
|
|
$commands = $this->getAllCommandNames();
|
|
$statistics = [];
|
|
|
|
foreach ($commands as $commandName) {
|
|
$statistics[$commandName] = $this->getCommandStatistics($commandName, $since);
|
|
}
|
|
|
|
return $statistics;
|
|
}
|
|
|
|
public function getMostFailingCommands(int $limit = 10, ?DateTimeImmutable $since = null): array
|
|
{
|
|
$commands = $this->getAllCommandNames();
|
|
$failingCommands = [];
|
|
|
|
foreach ($commands as $commandName) {
|
|
$stats = $this->getCommandStatistics($commandName, $since);
|
|
|
|
if ($stats->totalExecutions > 0) {
|
|
$failingCommands[] = [
|
|
'command_name' => $commandName,
|
|
'failure_rate' => $stats->getFailureRate(),
|
|
'total_executions' => $stats->totalExecutions,
|
|
'failed_executions' => $stats->failedExecutions,
|
|
];
|
|
}
|
|
}
|
|
|
|
// Sort by failure rate descending
|
|
usort($failingCommands, function ($a, $b) {
|
|
return $b['failure_rate']->getValue() <=> $a['failure_rate']->getValue();
|
|
});
|
|
|
|
return array_slice($failingCommands, 0, $limit);
|
|
}
|
|
|
|
public function getSlowestCommands(int $limit = 10, ?DateTimeImmutable $since = null): array
|
|
{
|
|
$commands = $this->getAllCommandNames();
|
|
$slowCommands = [];
|
|
|
|
foreach ($commands as $commandName) {
|
|
$stats = $this->getCommandStatistics($commandName, $since);
|
|
|
|
if ($stats->totalExecutions > 0) {
|
|
$slowCommands[] = [
|
|
'command_name' => $commandName,
|
|
'average_execution_time' => $stats->averageExecutionTime,
|
|
'max_execution_time' => $stats->maxExecutionTime,
|
|
'total_executions' => $stats->totalExecutions,
|
|
];
|
|
}
|
|
}
|
|
|
|
// Sort by average execution time descending
|
|
usort($slowCommands, function ($a, $b) {
|
|
return $b['average_execution_time']->toMilliseconds() <=>
|
|
$a['average_execution_time']->toMilliseconds();
|
|
});
|
|
|
|
return array_slice($slowCommands, 0, $limit);
|
|
}
|
|
|
|
public function getUsageByHour(?DateTimeImmutable $since = null): array
|
|
{
|
|
$commands = $this->getAllCommandNames();
|
|
$hourlyUsage = array_fill(0, 24, 0);
|
|
|
|
foreach ($commands as $commandName) {
|
|
$stats = $this->getCommandStatistics($commandName, $since);
|
|
|
|
if ($stats->hourlyDistribution) {
|
|
foreach ($stats->hourlyDistribution as $hour => $count) {
|
|
$hourlyUsage[$hour] += $count;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $hourlyUsage;
|
|
}
|
|
|
|
public function getCommandHealthScore(string $commandName, ?DateTimeImmutable $since = null): float
|
|
{
|
|
$stats = $this->getCommandStatistics($commandName, $since);
|
|
|
|
if ($stats->totalExecutions === 0) {
|
|
return 0.0;
|
|
}
|
|
|
|
// Health score based on success rate (70%) and performance (30%)
|
|
$successScore = $stats->getSuccessRate()->getValue() / 100;
|
|
|
|
// Performance score: inverse of execution time (normalized)
|
|
$avgTimeMs = $stats->averageExecutionTime->toMilliseconds();
|
|
$performanceScore = $avgTimeMs > 0 ? min(1.0, 1000 / $avgTimeMs) : 1.0;
|
|
|
|
return ($successScore * 0.7) + ($performanceScore * 0.3);
|
|
}
|
|
}
|