refactor: track import progress per film instead of per batch

Replace batch-level progress (processedBatches/totalBatches) with
film-level progress (processedFilms/totalFilms) for smoother UI updates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
thibaud-leclere
2026-04-01 19:30:15 +02:00
parent 8c73a22eff
commit 369893a77e
7 changed files with 70 additions and 62 deletions

View File

@@ -56,11 +56,11 @@ export default class extends Controller {
this.importBtnTarget.disabled = true; this.importBtnTarget.disabled = true;
this.importBtnTarget.textContent = 'Import en cours\u2026'; this.importBtnTarget.textContent = 'Import en cours\u2026';
const progress = data.totalBatches > 0 const progress = data.totalFilms > 0
? Math.round((data.processedBatches / data.totalBatches) * 100) ? Math.round((data.processedFilms / data.totalFilms) * 100)
: 0; : 0;
this._setStatus(`${progress}% — ${data.totalFilms} films`, 'active'); this._setStatus(`${progress}% — ${data.processedFilms}/${data.totalFilms} films`, 'active');
} }
_showCompleted(data) { _showCompleted(data) {

View File

@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20260401000002 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add processed_films column to import table';
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE import ADD processed_films INT NOT NULL DEFAULT 0');
$this->addSql('ALTER TABLE import DROP COLUMN total_batches');
$this->addSql('ALTER TABLE import DROP COLUMN processed_batches');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE import DROP COLUMN processed_films');
$this->addSql('ALTER TABLE import ADD total_batches INT NOT NULL DEFAULT 0');
$this->addSql('ALTER TABLE import ADD processed_batches INT NOT NULL DEFAULT 0');
}
}

View File

@@ -37,9 +37,8 @@ class ImportController extends AbstractController
'id' => $import->getId(), 'id' => $import->getId(),
'status' => $import->getStatus(), 'status' => $import->getStatus(),
'totalFilms' => $import->getTotalFilms(), 'totalFilms' => $import->getTotalFilms(),
'processedFilms' => $import->getProcessedFilms(),
'failedFilms' => $import->getFailedFilms(), 'failedFilms' => $import->getFailedFilms(),
'processedBatches' => $import->getProcessedBatches(),
'totalBatches' => $import->getTotalBatches(),
]); ]);
} }

View File

@@ -30,15 +30,12 @@ class Import
#[ORM\Column(length: 20)] #[ORM\Column(length: 20)]
private string $status = self::STATUS_PENDING; private string $status = self::STATUS_PENDING;
#[ORM\Column]
private int $totalBatches = 0;
#[ORM\Column]
private int $processedBatches = 0;
#[ORM\Column] #[ORM\Column]
private int $totalFilms = 0; private int $totalFilms = 0;
#[ORM\Column]
private int $processedFilms = 0;
#[ORM\Column] #[ORM\Column]
private int $failedFilms = 0; private int $failedFilms = 0;
@@ -91,25 +88,14 @@ class Import
return $this; return $this;
} }
public function getTotalBatches(): int public function getProcessedFilms(): int
{ {
return $this->totalBatches; return $this->processedFilms;
} }
public function setTotalBatches(int $totalBatches): static public function setProcessedFilms(int $processedFilms): static
{ {
$this->totalBatches = $totalBatches; $this->processedFilms = $processedFilms;
return $this;
}
public function getProcessedBatches(): int
{
return $this->processedBatches;
}
public function setProcessedBatches(int $processedBatches): static
{
$this->processedBatches = $processedBatches;
return $this; return $this;
} }

View File

@@ -54,12 +54,9 @@ readonly class ImportFilmsBatchMessageHandler
$movie = $this->filmImporter->importFromLtbxdMovie($ltbxdMovie); $movie = $this->filmImporter->importFromLtbxdMovie($ltbxdMovie);
if (!$movie) { if (!$movie) {
$this->importRepository->incrementFailedFilms($import); $this->importRepository->incrementFailedFilms($import);
continue; } else {
}
$this->actorSyncer->syncActorsForMovie($movie); $this->actorSyncer->syncActorsForMovie($movie);
// Import awards for actors of this movie
foreach ($movie->getActors() as $role) { foreach ($movie->getActors() as $role) {
$this->awardImporter->importForActor($role->getActor()); $this->awardImporter->importForActor($role->getActor());
} }
@@ -77,6 +74,7 @@ readonly class ImportFilmsBatchMessageHandler
} }
$this->em->flush(); $this->em->flush();
}
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->logger->warning('Failed to import film', [ $this->logger->warning('Failed to import film', [
'film' => $ltbxdMovie->getName(), 'film' => $ltbxdMovie->getName(),
@@ -86,19 +84,16 @@ readonly class ImportFilmsBatchMessageHandler
$this->importRepository->incrementFailedFilms($import); $this->importRepository->incrementFailedFilms($import);
} }
$processedFilms = $this->importRepository->incrementProcessedFilms($import);
$this->em->clear(); $this->em->clear();
$import = $this->em->getRepository(Import::class)->find($importId); $import = $this->em->getRepository(Import::class)->find($importId);
}
$processedBatches = $this->importRepository->incrementProcessedBatches($import);
if ($processedBatches >= $import->getTotalBatches()) {
// Refresh the entity to get updated failedFilms from DB
$this->em->refresh($import);
if ($processedFilms >= $import->getTotalFilms()) {
$import->setStatus(Import::STATUS_COMPLETED); $import->setStatus(Import::STATUS_COMPLETED);
$import->setCompletedAt(new \DateTimeImmutable()); $import->setCompletedAt(new \DateTimeImmutable());
$this->em->flush(); $this->em->flush();
} }
} }
}
} }

View File

@@ -49,10 +49,8 @@ readonly class ProcessImportMessageHandler
} }
$totalFilms = count($ltbxdMovies); $totalFilms = count($ltbxdMovies);
$totalBatches = (int) ceil($totalFilms / self::BATCH_SIZE);
$import->setTotalFilms($totalFilms); $import->setTotalFilms($totalFilms);
$import->setTotalBatches($totalBatches);
$import->setStatus(Import::STATUS_PROCESSING); $import->setStatus(Import::STATUS_PROCESSING);
$this->em->flush(); $this->em->flush();

View File

@@ -18,10 +18,10 @@ class ImportRepository extends ServiceEntityRepository
parent::__construct($registry, Import::class); parent::__construct($registry, Import::class);
} }
public function incrementProcessedBatches(Import $import): int public function incrementProcessedFilms(Import $import): int
{ {
return (int) $this->getEntityManager()->getConnection()->fetchOne( return (int) $this->getEntityManager()->getConnection()->fetchOne(
'UPDATE import SET processed_batches = processed_batches + 1 WHERE id = :id RETURNING processed_batches', 'UPDATE import SET processed_films = processed_films + 1 WHERE id = :id RETURNING processed_films',
['id' => $import->getId()] ['id' => $import->getId()]
); );
} }