185 lines
5.6 KiB
PHP
185 lines
5.6 KiB
PHP
<?php
|
|||
|
|
|
||
|
|
namespace App\Console\Commands;
|
||
|
|
|
||
|
|
use App\Jobs\BatchImportFromGutendex;
|
||
|
|
use App\Jobs\ImportBookFromGutendex;
|
||
|
|
use Illuminate\Console\Command;
|
||
|
|
|
||
|
|
class ImportFromGutendex extends Command
|
||
|
|
{
|
||
|
|
/**
|
||
|
|
* The name and signature of the console command.
|
||
|
|
*
|
||
|
|
* @var string
|
||
|
|
*/
|
||
|
|
protected $signature = 'gutendex:import
|
||
|
|
{--language=en : Language code to filter books}
|
||
|
|
{--limit=100 : Maximum number of books to import}
|
||
|
|
{--page=1 : Starting page number}
|
||
|
|
{--queue : Run the import via queue jobs}
|
||
|
|
{--no-summary : Skip summary generation}
|
||
|
|
{--copyright=false : Copyright filter (false for public domain)}';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* The console command description.
|
||
|
|
*
|
||
|
|
* @var string
|
||
|
|
*/
|
||
|
|
protected $description = 'Import books from Gutendex API with optional filters';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Execute the console command.
|
||
|
|
*/
|
||
|
|
public function handle(): int
|
||
|
|
{
|
||
|
|
$language = $this->option('language');
|
||
|
|
$limit = (int) $this->option('limit');
|
||
|
|
$page = (int) $this->option('page');
|
||
|
|
$useQueue = $this->option('queue');
|
||
|
|
$generateSummary = !$this->option('no-summary');
|
||
|
|
$copyright = $this->option('copyright');
|
||
|
|
|
||
|
|
$this->info('Gutendex Import');
|
||
|
|
$this->info('==============');
|
||
|
|
$this->newLine();
|
||
|
|
|
||
|
|
// Build filters
|
||
|
|
$filters = [
|
||
|
|
'languages' => $language,
|
||
|
|
'copyright' => $copyright,
|
||
|
|
];
|
||
|
|
|
||
|
|
$this->table(
|
||
|
|
['Setting', 'Value'],
|
||
|
|
[
|
||
|
|
['Language', $language],
|
||
|
|
['Limit', $limit],
|
||
|
|
['Starting Page', $page],
|
||
|
|
['Mode', $useQueue ? 'Queued' : 'Immediate'],
|
||
|
|
['Generate Summaries', $generateSummary ? 'Yes' : 'No'],
|
||
|
|
['Copyright Filter', $copyright],
|
||
|
|
]
|
||
|
|
);
|
||
|
|
|
||
|
|
if (!$this->confirm('Do you want to proceed with this import?', true)) {
|
||
|
|
$this->warn('Import cancelled.');
|
||
|
|
return self::SUCCESS;
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->newLine();
|
||
|
|
|
||
|
|
if ($useQueue) {
|
||
|
|
return $this->handleQueuedImport($filters, $page, $limit, $generateSummary);
|
||
|
|
} else {
|
||
|
|
return $this->handleImmediateImport($filters, $page, $limit, $generateSummary);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Handle queued import.
|
||
|
|
*/
|
||
|
|
protected function handleQueuedImport(array $filters, int $page, int $limit, bool $generateSummary): int
|
||
|
|
{
|
||
|
|
$this->info('Dispatching batch import job...');
|
||
|
|
|
||
|
|
// Calculate end page based on limit (assuming ~32 books per page)
|
||
|
|
$booksPerPage = 32;
|
||
|
|
$endPage = $page + (int) ceil($limit / $booksPerPage) - 1;
|
||
|
|
|
||
|
|
BatchImportFromGutendex::dispatch(
|
||
|
|
$filters,
|
||
|
|
$page,
|
||
|
|
$endPage,
|
||
|
|
$limit,
|
||
|
|
$generateSummary
|
||
|
|
);
|
||
|
|
|
||
|
|
$this->info('✓ Batch import job dispatched successfully!');
|
||
|
|
$this->newLine();
|
||
|
|
$this->comment('The import will be processed in the background.');
|
||
|
|
$this->comment('Run the queue worker: php artisan queue:work');
|
||
|
|
$this->comment('Monitor progress: php artisan summary:status');
|
||
|
|
|
||
|
|
return self::SUCCESS;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Handle immediate import (synchronous).
|
||
|
|
*/
|
||
|
|
protected function handleImmediateImport(array $filters, int $page, int $limit, bool $generateSummary): int
|
||
|
|
{
|
||
|
|
$this->warn('⚠ Running immediate import. This may take a while...');
|
||
|
|
$this->warn('⚠ For large imports, consider using --queue option.');
|
||
|
|
$this->newLine();
|
||
|
|
|
||
|
|
$gutendexClient = app(\App\Services\GutendexClient::class);
|
||
|
|
|
||
|
|
$totalImported = 0;
|
||
|
|
$totalSkipped = 0;
|
||
|
|
$currentPage = $page;
|
||
|
|
|
||
|
|
$progressBar = $this->output->createProgressBar($limit);
|
||
|
|
$progressBar->start();
|
||
|
|
|
||
|
|
while ($totalImported < $limit) {
|
||
|
|
$response = $gutendexClient->searchBooks(null, $currentPage, $filters);
|
||
|
|
|
||
|
|
if (empty($response['results'])) {
|
||
|
|
$this->newLine();
|
||
|
|
$this->warn('No more results available.');
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach ($response['results'] as $bookData) {
|
||
|
|
if ($totalImported >= $limit) {
|
||
|
|
break 2;
|
||
|
|
}
|
||
|
|
|
||
|
|
$gutenbergId = $bookData['id'] ?? null;
|
||
|
|
if (!$gutenbergId) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check if already exists
|
||
|
|
if (\App\Models\Book::where('gutenberg_id', $gutenbergId)->exists()) {
|
||
|
|
$totalSkipped++;
|
||
|
|
$progressBar->advance();
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Dispatch job
|
||
|
|
ImportBookFromGutendex::dispatch($gutenbergId, $generateSummary);
|
||
|
|
$totalImported++;
|
||
|
|
$progressBar->advance();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (empty($response['next'])) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
$currentPage++;
|
||
|
|
}
|
||
|
|
|
||
|
|
$progressBar->finish();
|
||
|
|
$this->newLine(2);
|
||
|
|
|
||
|
|
$this->info('✓ Import jobs dispatched successfully!');
|
||
|
|
$this->newLine();
|
||
|
|
$this->table(
|
||
|
|
['Metric', 'Count'],
|
||
|
|
[
|
||
|
|
['Jobs Dispatched', $totalImported],
|
||
|
|
['Books Skipped (existing)', $totalSkipped],
|
||
|
|
['Pages Processed', $currentPage - $page + 1],
|
||
|
|
]
|
||
|
|
);
|
||
|
|
|
||
|
|
$this->newLine();
|
||
|
|
$this->comment('Jobs have been dispatched to the queue.');
|
||
|
|
$this->comment('Run the queue worker: php artisan queue:work');
|
||
|
|
|
||
|
|
return self::SUCCESS;
|
||
|
|
}
|
||
|
|
}
|