argument('book_id'); $all = $this->option('all'); $pending = $this->option('pending'); $failed = $this->option('failed'); $limit = (int) $this->option('limit'); // Handle single book if ($bookId) { return $this->generateForBook((int) $bookId); } // Handle batch operations if ($all) { return $this->generateForBooksWithoutSummaries($limit); } if ($pending) { return $this->generateForPendingSummaries($limit); } if ($failed) { return $this->generateForFailedSummaries($limit); } // No option provided $this->error('Please specify a book ID or use one of the options: --all, --pending, --failed'); $this->newLine(); $this->comment('Examples:'); $this->comment(' php artisan summary:generate 123'); $this->comment(' php artisan summary:generate --all'); $this->comment(' php artisan summary:generate --failed --limit=10'); return self::FAILURE; } /** * Generate summary for a specific book. */ protected function generateForBook(int $bookId): int { $book = Book::find($bookId); if (!$book) { $this->error("Book with ID {$bookId} not found."); return self::FAILURE; } $this->info("Generating summary for: {$book->title}"); $this->newLine(); if (!$book->full_text_url) { $this->error('Book does not have a full text URL. Cannot generate summary.'); return self::FAILURE; } // Check existing summary if ($book->summary) { $this->warn('Book already has a summary:'); $this->table( ['Field', 'Value'], [ ['Status', $book->summary->processing_status], ['Processed At', $book->summary->processed_at?->format('Y-m-d H:i:s') ?? 'N/A'], ['Model Used', $book->summary->model_used ?? 'N/A'], ['Has Error', $book->summary->processing_error ? 'Yes' : 'No'], ] ); $this->newLine(); if (!$this->confirm('Do you want to regenerate the summary?', false)) { $this->info('Operation cancelled.'); return self::SUCCESS; } } $this->info('Dispatching summary generation job...'); GenerateSummary::dispatch($bookId); $this->newLine(); $this->info('✓ Summary generation job dispatched!'); $this->comment('Run the queue worker: php artisan queue:work'); return self::SUCCESS; } /** * Generate summaries for books without summaries. */ protected function generateForBooksWithoutSummaries(int $limit): int { $this->info('Finding books without summaries...'); $books = Book::doesntHave('summary') ->whereNotNull('full_text_url') ->where('source', 'gutendex') ->limit($limit) ->get(); if ($books->isEmpty()) { $this->info('No books found without summaries.'); return self::SUCCESS; } $this->info("Found {$books->count()} books without summaries."); $this->newLine(); if (!$this->confirm("Dispatch {$books->count()} summary generation jobs?", true)) { $this->info('Operation cancelled.'); return self::SUCCESS; } $progressBar = $this->output->createProgressBar($books->count()); $progressBar->start(); foreach ($books as $index => $book) { GenerateSummary::dispatch($book->id) ->delay(now()->addSeconds($index * 10)); // Stagger by 10 seconds $progressBar->advance(); } $progressBar->finish(); $this->newLine(2); $this->info("✓ Dispatched {$books->count()} summary generation jobs!"); $this->comment('Jobs will be processed with 10-second intervals.'); $this->comment('Run the queue worker: php artisan queue:work'); return self::SUCCESS; } /** * Generate summaries for pending summaries. */ protected function generateForPendingSummaries(int $limit): int { $this->info('Finding pending summaries...'); $summaries = Summary::pending() ->with('book') ->limit($limit) ->get(); if ($summaries->isEmpty()) { $this->info('No pending summaries found.'); return self::SUCCESS; } $this->info("Found {$summaries->count()} pending summaries."); $this->newLine(); if (!$this->confirm("Retry {$summaries->count()} pending summaries?", true)) { $this->info('Operation cancelled.'); return self::SUCCESS; } $progressBar = $this->output->createProgressBar($summaries->count()); $progressBar->start(); foreach ($summaries as $index => $summary) { if ($summary->book) { GenerateSummary::dispatch($summary->book->id) ->delay(now()->addSeconds($index * 10)); } $progressBar->advance(); } $progressBar->finish(); $this->newLine(2); $this->info("✓ Dispatched {$summaries->count()} retry jobs!"); $this->comment('Run the queue worker: php artisan queue:work'); return self::SUCCESS; } /** * Generate summaries for failed summaries. */ protected function generateForFailedSummaries(int $limit): int { $this->info('Finding failed summaries...'); $summaries = Summary::failed() ->with('book') ->limit($limit) ->get(); if ($summaries->isEmpty()) { $this->info('No failed summaries found.'); return self::SUCCESS; } $this->warn("Found {$summaries->count()} failed summaries."); $this->newLine(); // Show error details $this->table( ['Book ID', 'Title', 'Error'], $summaries->map(fn($s) => [ $s->book_id, $s->book?->title ?? 'Unknown', substr($s->processing_error ?? 'Unknown error', 0, 60) . '...', ])->toArray() ); $this->newLine(); if (!$this->confirm("Retry {$summaries->count()} failed summaries?", true)) { $this->info('Operation cancelled.'); return self::SUCCESS; } $progressBar = $this->output->createProgressBar($summaries->count()); $progressBar->start(); foreach ($summaries as $index => $summary) { if ($summary->book) { GenerateSummary::dispatch($summary->book->id) ->delay(now()->addSeconds($index * 15)); // Longer delay for failures } $progressBar->advance(); } $progressBar->finish(); $this->newLine(2); $this->info("✓ Dispatched {$summaries->count()} retry jobs!"); $this->comment('Jobs will be processed with 15-second intervals.'); $this->comment('Run the queue worker: php artisan queue:work'); return self::SUCCESS; } }