gutendex integration, phase 6
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
<?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).');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user