Files
opentextsummaries/app/Console/Commands/SummaryStatus.php
T

230 lines
7.2 KiB
PHP
Raw Normal View History

2026-03-29 16:14:18 +03:00
<?php
namespace App\Console\Commands;
use App\Models\Book;
use App\Models\Summary;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class SummaryStatus extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'summary:status
{--detailed : Show detailed breakdown}
{--recent=10 : Number of recent activities to show}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Display statistics and status of summary generation';
/**
* Execute the console command.
*/
public function handle(): int
{
$this->info('Summary Generation Status');
$this->info('========================');
$this->newLine();
$this->displayOverallStatistics();
$this->newLine();
if ($this->option('detailed')) {
$this->displayDetailedBreakdown();
$this->newLine();
}
$this->displayRecentActivity();
$this->newLine();
$this->displayQueueStatus();
return self::SUCCESS;
}
/**
* Display overall statistics.
*/
protected function displayOverallStatistics(): void
{
$totalBooks = Book::count();
$gutendexBooks = Book::gutendex()->count();
$booksWithText = Book::whereNotNull('full_text_url')->count();
$totalSummaries = Summary::count();
$completedSummaries = Summary::completed()->count();
$pendingSummaries = Summary::pending()->count();
$processingSummaries = Summary::processing()->count();
$failedSummaries = Summary::failed()->count();
$booksWithoutSummaries = Book::gutendex()
->whereNotNull('full_text_url')
->doesntHave('summary')
->count();
$completionRate = $totalSummaries > 0
? round(($completedSummaries / $totalSummaries) * 100, 2)
: 0;
$this->table(
['Metric', 'Count', 'Percentage'],
[
['Total Books', number_format($totalBooks), '-'],
['Gutendex Books', number_format($gutendexBooks), round(($gutendexBooks / max($totalBooks, 1)) * 100, 1) . '%'],
['Books with Full Text', number_format($booksWithText), '-'],
['Books without Summaries', number_format($booksWithoutSummaries), '-'],
['---', '---', '---'],
['Total Summaries', number_format($totalSummaries), '-'],
['✓ Completed', number_format($completedSummaries), $completionRate . '%'],
['⏳ Pending', number_format($pendingSummaries), round(($pendingSummaries / max($totalSummaries, 1)) * 100, 1) . '%'],
['⚙ Processing', number_format($processingSummaries), round(($processingSummaries / max($totalSummaries, 1)) * 100, 1) . '%'],
['✗ Failed', number_format($failedSummaries), round(($failedSummaries / max($totalSummaries, 1)) * 100, 1) . '%'],
]
);
}
/**
* Display detailed breakdown by model and category.
*/
protected function displayDetailedBreakdown(): void
{
$this->info('Detailed Breakdown');
$this->info('------------------');
$this->newLine();
// By model
$byModel = Summary::completed()
->select('model_used', DB::raw('COUNT(*) as count'))
->groupBy('model_used')
->get();
if ($byModel->isNotEmpty()) {
$this->info('Summaries by Model:');
$this->table(
['Model', 'Count'],
$byModel->map(fn($item) => [
$item->model_used ?? 'Unknown',
number_format($item->count),
])->toArray()
);
$this->newLine();
}
// By language
$byLanguage = Book::gutendex()
->select('language', DB::raw('COUNT(*) as count'))
->whereNotNull('language')
->groupBy('language')
->orderByDesc('count')
->limit(10)
->get();
if ($byLanguage->isNotEmpty()) {
$this->info('Books by Language (Top 10):');
$this->table(
['Language', 'Count'],
$byLanguage->map(fn($item) => [
$item->language,
number_format($item->count),
])->toArray()
);
$this->newLine();
}
}
/**
* Display recent activity.
*/
protected function displayRecentActivity(): void
{
$recentCount = (int) $this->option('recent');
$this->info("Recent Activity (Last {$recentCount})");
$this->info('------------------');
$this->newLine();
// Recent completed summaries
$recentCompleted = Summary::completed()
->with('book')
->orderByDesc('processed_at')
->limit($recentCount)
->get();
if ($recentCompleted->isNotEmpty()) {
$this->info('✓ Recently Completed:');
$this->table(
['Book ID', 'Title', 'Processed At', 'Model'],
$recentCompleted->map(fn($s) => [
$s->book_id,
substr($s->book?->title ?? 'Unknown', 0, 40),
$s->processed_at?->diffForHumans() ?? 'N/A',
$s->model_used ?? 'N/A',
])->toArray()
);
$this->newLine();
}
// Recent failures
$recentFailed = Summary::failed()
->with('book')
->orderByDesc('updated_at')
->limit($recentCount)
->get();
if ($recentFailed->isNotEmpty()) {
$this->warn('✗ Recent Failures:');
$this->table(
['Book ID', 'Title', 'Error'],
$recentFailed->map(fn($s) => [
$s->book_id,
substr($s->book?->title ?? 'Unknown', 0, 30),
substr($s->processing_error ?? 'Unknown error', 0, 60),
])->toArray()
);
$this->newLine();
}
}
/**
* Display queue status.
*/
protected function displayQueueStatus(): void
{
$this->info('Queue Status');
$this->info('------------');
$this->newLine();
// Count pending jobs (if using database queue)
try {
$pendingJobs = DB::table('jobs')->count();
$failedJobs = DB::table('failed_jobs')->count();
$this->table(
['Queue', 'Count'],
[
['Pending Jobs', number_format($pendingJobs)],
['Failed Jobs', number_format($failedJobs)],
]
);
if ($pendingJobs > 0) {
$this->comment('Run the queue worker: php artisan queue:work');
}
if ($failedJobs > 0) {
$this->warn("You have {$failedJobs} failed jobs. Review with: php artisan queue:failed");
}
} catch (\Exception $e) {
$this->comment('Queue status unavailable (database driver not configured).');
}
}
}