230 lines
7.2 KiB
PHP
230 lines
7.2 KiB
PHP
<?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).');
|
|
}
|
|
}
|
|
}
|