docs: consolidate documentation into organized structure

- 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
This commit is contained in:
2025-10-05 11:05:04 +02:00
parent 887847dde6
commit 5050c7d73a
36686 changed files with 196456 additions and 12398919 deletions

View File

@@ -0,0 +1,324 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Analytics\Commands;
use App\Framework\Console\Analytics\AnalyticsService;
use App\Framework\Console\Analytics\ValueObjects\Period;
use App\Framework\Console\Attributes\ConsoleCommand;
use App\Framework\Console\ExitCode;
use App\Framework\Console\Input\ConsoleInput;
use App\Framework\Console\Layout\ResponsiveOutput;
use App\Framework\Console\Output\ConsoleOutput;
use DateTimeImmutable;
final readonly class AnalyticsCommand
{
public function __construct(
private AnalyticsService $analyticsService
) {
}
#[ConsoleCommand(
name: 'analytics:usage',
description: 'Show command usage analytics and statistics'
)]
public function showUsage(ConsoleInput $input, ConsoleOutput $output): ExitCode
{
$commandName = $input->getArgument('command');
$days = (int) ($input->getOption('days') ?? 30);
$since = new DateTimeImmutable("-{$days} days");
if ($commandName) {
return $this->showCommandUsage($commandName, $since, $output);
}
return $this->showOverallUsage($since, $output);
}
#[ConsoleCommand(
name: 'analytics:popular',
description: 'Show most popular commands'
)]
public function showPopularCommands(ConsoleInput $input, ConsoleOutput $output): ExitCode
{
$limit = (int) ($input->getOption('limit') ?? 10);
$days = (int) ($input->getOption('days') ?? 30);
$since = new DateTimeImmutable("-{$days} days");
$popularCommands = $this->analyticsService->getPopularCommands($limit, $since);
if (empty($popularCommands)) {
$output->writeLine('<red>No command usage data found.</red>');
return ExitCode::SUCCESS;
}
$responsiveOutput = ResponsiveOutput::create($output);
$output->writeLine("<yellow>Most Popular Commands (Last {$days} days)</yellow>\n");
// Prepare table data
$headers = ['Rank', 'Command', 'Executions', 'Usage %', 'Avg Time', 'Success Rate'];
$rows = [];
foreach ($popularCommands as $command) {
$rows[] = [
(string) $command->rank,
$command->commandName,
(string) $command->totalExecutions,
$command->usagePercentage->format(1),
$command->averageExecutionTime->toHumanReadable(),
$command->successRate->format(1),
];
}
$responsiveOutput->writeTable($headers, $rows);
return ExitCode::SUCCESS;
}
#[ConsoleCommand(
name: 'analytics:trends',
description: 'Show command usage trends over time'
)]
public function showTrends(ConsoleInput $input, ConsoleOutput $output): ExitCode
{
$commandName = $input->getArgument('command');
if (! $commandName) {
$output->writeLine('<red>Command name is required for trend analysis.</red>');
return ExitCode::INVALID_ARGUMENTS;
}
$periodStr = $input->getOption('period') ?? 'daily';
$period = match (strtolower($periodStr)) {
'hourly' => Period::HOURLY,
'daily' => Period::DAILY,
'weekly' => Period::WEEKLY,
'monthly' => Period::MONTHLY,
'yearly' => Period::YEARLY,
default => Period::DAILY
};
$days = (int) ($input->getOption('days') ?? 30);
$start = new DateTimeImmutable("-{$days} days");
$end = new DateTimeImmutable();
$trends = $this->analyticsService->getCommandTrends($commandName, $period, $start, $end);
if (empty($trends)) {
$output->writeLine('<red>No trend data found for this command.</red>');
return ExitCode::SUCCESS;
}
$responsiveOutput = ResponsiveOutput::create($output);
$trend = $trends[0];
$output->writeLine("<yellow>Usage Trends for '{$commandName}' ({$period->getDescription()})</yellow>\n");
// Show trend summary
$summary = [
'Period' => "{$trend->startDate->format('Y-m-d')} to {$trend->endDate->format('Y-m-d')}",
'Trend' => $trend->getTrendDescription(),
'Direction' => ($trend->trendDirection > 0 ? '+' : '') . round($trend->trendDirection, 3),
'Strength' => round($trend->trendStrength, 3),
];
$responsiveOutput->writeKeyValue($summary);
$output->writeLine('');
// Show trend data points as responsive table
$headers = ['Date', 'Executions', 'Avg Time', 'Success Rate'];
$rows = [];
foreach ($trend->dataPoints as $point) {
$rows[] = [
$point->date->format($period->getDateFormat()),
(string) $point->executionCount,
$point->averageExecutionTime->toHumanReadable(),
$point->successRate->format(1),
];
}
$responsiveOutput->writeTable($headers, $rows);
return ExitCode::SUCCESS;
}
#[ConsoleCommand(
name: 'analytics:health',
description: 'Show command health scores and problematic commands'
)]
public function showHealth(ConsoleInput $input, ConsoleOutput $output): ExitCode
{
$days = (int) ($input->getOption('days') ?? 30);
$since = new DateTimeImmutable("-{$days} days");
$output->writeLine("<yellow>Command Health Analysis (Last {$days} days)</yellow>\n");
// Show failing commands
$failingCommands = $this->analyticsService->getMostFailingCommands(10, $since);
if (! empty($failingCommands)) {
$output->writeLine("<red>Commands with Highest Failure Rates:</red>");
$output->writeLine(sprintf(
"%-25s %-12s %-12s %s",
'Command',
'Failure Rate',
'Total Runs',
'Failures'
));
$output->writeLine(str_repeat('-', 70));
foreach ($failingCommands as $command) {
$output->writeLine(sprintf(
"%-25s %-12s %-12d %d",
$command['command_name'],
$command['failure_rate']->format(1),
$command['total_executions'],
$command['failed_executions']
));
}
$output->writeLine('');
}
// Show slow commands
$slowCommands = $this->analyticsService->getSlowestCommands(10, $since);
if (! empty($slowCommands)) {
$output->writeLine("<yellow>Slowest Commands:</yellow>");
$output->writeLine(sprintf(
"%-25s %-15s %-15s %s",
'Command',
'Avg Time',
'Max Time',
'Total Runs'
));
$output->writeLine(str_repeat('-', 80));
foreach ($slowCommands as $command) {
$output->writeLine(sprintf(
"%-25s %-15s %-15s %d",
$command['command_name'],
$command['average_execution_time']->toHumanReadable(),
$command['max_execution_time']->toHumanReadable(),
$command['total_executions']
));
}
$output->writeLine('');
}
return ExitCode::SUCCESS;
}
#[ConsoleCommand(
name: 'analytics:cleanup',
description: 'Clean up old analytics data'
)]
public function cleanup(ConsoleInput $input, ConsoleOutput $output): ExitCode
{
$days = (int) ($input->getOption('days') ?? 90);
$output->writeLine("<yellow>Cleaning up analytics data older than {$days} days...</yellow>");
$deletedRecords = $this->analyticsService->cleanupOldData($days);
$output->writeLine("<green>Successfully deleted {$deletedRecords} old analytics records.</green>");
return ExitCode::SUCCESS;
}
private function showCommandUsage(string $commandName, DateTimeImmutable $since, ConsoleOutput $output): ExitCode
{
$stats = $this->analyticsService->getCommandStatistics($commandName, $since);
$output->writeLine("<yellow>Usage Statistics for '{$commandName}'</yellow>\n");
if ($stats->totalExecutions === 0) {
$output->writeLine('<red>No usage data found for this command.</red>');
return ExitCode::SUCCESS;
}
$output->writeLine("Total Executions: {$stats->totalExecutions}");
$output->writeLine("Successful: {$stats->successfulExecutions} ({$stats->getSuccessRate()->format(1)})");
$output->writeLine("Failed: {$stats->failedExecutions} ({$stats->getFailureRate()->format(1)})");
$output->writeLine('');
$output->writeLine("Performance:");
$output->writeLine(" Average Time: {$stats->averageExecutionTime->toHumanReadable()}");
$output->writeLine(" Min Time: {$stats->minExecutionTime->toHumanReadable()}");
$output->writeLine(" Max Time: {$stats->maxExecutionTime->toHumanReadable()}");
if ($stats->medianExecutionTime) {
$output->writeLine(" Median Time: {$stats->medianExecutionTime->toHumanReadable()}");
}
// Show hourly distribution
if ($stats->hourlyDistribution) {
$output->writeLine('');
$output->writeLine("Usage by Hour:");
$maxUsage = max($stats->hourlyDistribution);
for ($hour = 0; $hour < 24; $hour++) {
$usage = $stats->hourlyDistribution[$hour];
$percentage = $maxUsage > 0 ? ($usage / $maxUsage) * 100 : 0;
$bar = str_repeat('▓', (int) ($percentage / 5));
$output->writeLine(sprintf(
"%02d:00 %4d %s",
$hour,
$usage,
$bar
));
}
}
return ExitCode::SUCCESS;
}
private function showOverallUsage(DateTimeImmutable $since, ConsoleOutput $output): ExitCode
{
$commands = $this->analyticsService->getAllCommandNames();
$output->writeLine("<yellow>Overall Command Usage Statistics</yellow>\n");
if (empty($commands)) {
$output->writeLine('<red>No command usage data found.</red>');
return ExitCode::SUCCESS;
}
$totalExecutions = 0;
$totalCommands = count($commands);
foreach ($commands as $commandName) {
$stats = $this->analyticsService->getCommandStatistics($commandName, $since);
$totalExecutions += $stats->totalExecutions;
}
$output->writeLine("Total Commands: {$totalCommands}");
$output->writeLine("Total Executions: {$totalExecutions}");
$output->writeLine("Average per Command: " . round($totalExecutions / $totalCommands, 2));
$output->writeLine('');
// Show usage by hour
$hourlyUsage = $this->analyticsService->getUsageByHour($since);
$output->writeLine("Usage Distribution by Hour:");
$maxHourly = max($hourlyUsage);
for ($hour = 0; $hour < 24; $hour++) {
$usage = $hourlyUsage[$hour];
$percentage = $maxHourly > 0 ? ($usage / $maxHourly) * 100 : 0;
$bar = str_repeat('▓', (int) ($percentage / 5));
$output->writeLine(sprintf(
"%02d:00 %4d %s",
$hour,
$usage,
$bar
));
}
return ExitCode::SUCCESS;
}
}