Skip to content

Commit

Permalink
Copy image jobs. (#90)
Browse files Browse the repository at this point in the history
* Copy image jobs.
* Remove cache dir validation
* Create keyword checks if same keword with reqested name exists
  • Loading branch information
TomasHermanek authored Nov 18, 2024
1 parent 674bb8d commit ba3f982
Show file tree
Hide file tree
Showing 21 changed files with 730 additions and 26 deletions.
140 changes: 140 additions & 0 deletions src/Command/GenerateCopyJobCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
<?php

declare(strict_types=1);

namespace AnzuSystems\CoreDamBundle\Command;

use AnzuSystems\Contracts\AnzuApp;
use AnzuSystems\CoreDamBundle\Command\Traits\OutputUtilTrait;
use AnzuSystems\CoreDamBundle\Domain\Job\JobImageCopyFactory;
use AnzuSystems\CoreDamBundle\Entity\Asset;
use AnzuSystems\CoreDamBundle\Entity\AssetFile;
use AnzuSystems\CoreDamBundle\Entity\AssetLicence;
use AnzuSystems\CoreDamBundle\Repository\AssetFileRepository;
use AnzuSystems\CoreDamBundle\Repository\AssetLicenceRepository;
use AnzuSystems\CoreDamBundle\Repository\AssetRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use League\Flysystem\FilesystemException;
use SplFileObject;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;

#[AsCommand(
name: 'anzu:job:copy-asset-files',
description: 'Create JobImageCopy based on licenceId and assetLicenceIds from file'
)]
final class GenerateCopyJobCommand extends Command
{
use OutputUtilTrait;
private const string USERS_FILE_PATH_OPT = 'file';
private const string LICENCE_ID_ARG = 'licence';
private const string USERS_FILE_PATH_DEFAULT = 'image_split.csv';

private const int MAX_ASSETS_PER_JOB = 20;

public function __construct(
private readonly Connection $damMediaApiMigConnection,
private readonly Connection $defaultConnection,
private readonly JobImageCopyFactory $imageCopyFactory,
private readonly AssetRepository $assetRepository,
private readonly AssetLicenceRepository $assetLicenceRepository,
private readonly AssetFileRepository $assetFileRepository,
private readonly EntityManagerInterface $entityManager,
) {
parent::__construct();
}

public function configure(): void
{
$this
->addArgument(
name: self::LICENCE_ID_ARG,
mode: InputArgument::REQUIRED,
)
->addOption(
name: self::USERS_FILE_PATH_OPT,
mode: InputOption::VALUE_REQUIRED,
default: AnzuApp::getDataDir() . '/' . self::USERS_FILE_PATH_DEFAULT
);
}

/**
* @throws Exception
* @throws FilesystemException
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$licenceId = $input->getArgument(self::LICENCE_ID_ARG);
$licence = $this->assetLicenceRepository->find($licenceId);

if (null === $licence) {
$output->writeln("<error>Licence not found: ({$licenceId})</error>");

return Command::FAILURE;
}
$filePath = $input->getOption(self::USERS_FILE_PATH_OPT);
if (false === file_exists($filePath)) {
$output->writeln("<error>File not found at path: ({$filePath})</error>");

return Command::FAILURE;
}

/** @var QuestionHelper $helper */
$helper = $this->getHelper('question');
$question = new ConfirmationQuestion(sprintf('Copy to licence (%s)? [y/n] ', $licence->getName()), false);

if (false === $helper->ask($input, $output, $question)) {
return Command::SUCCESS;
}

$csv = new SplFileObject($filePath);
$csv->setFlags(SplFileObject::SKIP_EMPTY);
$progress = new ProgressBar($output);
$progress->start();

/** @var array<int|string, Asset> $assets */
$assets = [];
while (false === $csv->eof()) {
$row = $csv->fgetcsv();

if (false === is_array($row) || false === isset($row[0])) {
continue;
}

$assetFile = $this->assetFileRepository->find($row[0]);
if (false === ($assetFile instanceof AssetFile)) {
continue;
}

$assets[(string) $assetFile->getAsset()->getId()] = $assetFile->getAsset();
if (count($assets) >= self::MAX_ASSETS_PER_JOB) {
$this->imageCopyFactory->createPodcastSynchronizerJob($licence, new ArrayCollection($assets));
$assets = [];
$this->entityManager->clear();
/** @var AssetLicence $licence */
$licence = $this->assetLicenceRepository->find($licenceId);
}

$progress->advance();
}

if (false === empty($assets)) {
$this->imageCopyFactory->createPodcastSynchronizerJob($licence, new ArrayCollection($assets));
}

$progress->finish();
$output->writeln('');

return Command::SUCCESS;
}
}
19 changes: 19 additions & 0 deletions src/Controller/Api/Adm/V1/JobController.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use AnzuSystems\CommonBundle\Model\OpenApi\Response\OAResponseValidation;
use AnzuSystems\Contracts\AnzuApp;
use AnzuSystems\Contracts\Exception\AppReadOnlyModeException;
use AnzuSystems\CoreDamBundle\Entity\JobImageCopy;
use AnzuSystems\CoreDamBundle\Entity\JobPodcastSynchronizer;
use AnzuSystems\CoreDamBundle\Security\Permission\DamPermissions;
use AnzuSystems\SerializerBundle\Attributes\SerializeParam;
Expand Down Expand Up @@ -41,6 +42,24 @@ public function createPodcastSynchronizer(#[SerializeParam] JobPodcastSynchroniz
);
}

/**
* Create JobPodcastSynchronizer item.
*
* @throws ValidationException
* @throws AppReadOnlyModeException
*/
#[Route('/image-copy', 'create_job_image_copy', methods: [Request::METHOD_POST])]
#[OARequest(JobImageCopy::class), OAResponseCreated(JobImageCopy::class), OAResponseValidation]
public function createImageCopyJob(#[SerializeParam] JobImageCopy $job): JsonResponse
{
AnzuApp::throwOnReadOnlyMode();
$this->denyAccessUnlessGranted($this->getCreateAcl());

return $this->createdResponse(
$this->jobFacade->create($job)
);
}

protected function getCreateAcl(): string
{
return DamPermissions::DAM_JOB_CREATE;
Expand Down
9 changes: 6 additions & 3 deletions src/Controller/Api/Adm/V1/KeywordController.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use AnzuSystems\CoreDamBundle\Entity\Author;
use AnzuSystems\CoreDamBundle\Entity\ExtSystem;
use AnzuSystems\CoreDamBundle\Entity\Keyword;
use AnzuSystems\CoreDamBundle\Exception\KeywordExistsException;
use AnzuSystems\CoreDamBundle\Security\Permission\DamPermissions;
use AnzuSystems\SerializerBundle\Attributes\SerializeParam;
use AnzuSystems\SerializerBundle\Exception\SerializerException;
Expand Down Expand Up @@ -74,9 +75,11 @@ public function create(#[SerializeParam] Keyword $keyword): JsonResponse
App::throwOnReadOnlyMode();
$this->denyAccessUnlessGranted(DamPermissions::DAM_KEYWORD_CREATE, $keyword);

return $this->createdResponse(
$this->keywordFacade->create($keyword)
);
try {
return $this->createdResponse($this->keywordFacade->create($keyword));
} catch (KeywordExistsException $exception) {
return $this->okResponse($exception->getExistingKeyword());
}
}

/**
Expand Down
8 changes: 7 additions & 1 deletion src/Domain/AssetMetadata/Suggestion/KeywordSuggester.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use AnzuSystems\CoreDamBundle\Entity\Asset;
use AnzuSystems\CoreDamBundle\Entity\AssetFile;
use AnzuSystems\CoreDamBundle\Entity\Keyword;
use AnzuSystems\CoreDamBundle\Exception\KeywordExistsException;
use AnzuSystems\CoreDamBundle\Model\Configuration\ExtSystemAssetTypeExifMetadataConfiguration;
use AnzuSystems\CoreDamBundle\Repository\KeywordRepository;
use Doctrine\ORM\EntityManagerInterface;
Expand Down Expand Up @@ -58,7 +59,12 @@ protected function suggestIdsByTag(string $name, Asset $asset): array

// 2. Entity doesn't exist, create it.
$keyword = $this->keywordFactory->create($name, $extSystem);
$this->keywordFacade->create($keyword);

try {
$this->keywordFacade->create($keyword);
} catch (KeywordExistsException $exception) {
$ids[] = (string) $exception->getExistingKeyword()->getId();
}

return $ids;
}
Expand Down
15 changes: 9 additions & 6 deletions src/Domain/Image/Crop/CropCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
use AnzuSystems\CoreDamBundle\Model\Dto\Image\ImageCropDto;
use League\Flysystem\FilesystemException;

final class CropCache
final readonly class CropCache
{
public function __construct(
private readonly FileSystemProvider $fileSystemProvider,
private readonly NameGenerator $nameGenerator,
private FileSystemProvider $fileSystemProvider,
private NameGenerator $nameGenerator,
) {
}

Expand Down Expand Up @@ -69,11 +69,14 @@ public function removeCache(ImageFile $image): void
*/
public function removeCacheByOriginFilePath(string $extSystemSlug, string $path): void
{
$cacheDir = $this->getCacheDir($path);
if (0 === strlen($cacheDir)) {
return;
}

$this->fileSystemProvider
->getCropFilesystemByExtSystemSlug($extSystemSlug)
->deleteDirectory(
$this->getCacheDir($path)
);
->deleteDirectory($cacheDir);
}

private function getCacheDir(string $path): string
Expand Down
14 changes: 7 additions & 7 deletions src/Domain/Image/ImageCopyFacade.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
use AnzuSystems\CoreDamBundle\Messenger\Message\CopyAssetFileMessage;
use AnzuSystems\CoreDamBundle\Model\Dto\Image\AssetFileCopyResultDto;
use AnzuSystems\CoreDamBundle\Model\Dto\Image\ImageCopyDto;
use AnzuSystems\CoreDamBundle\Model\Enum\AssetFileCopyResult;
use AnzuSystems\CoreDamBundle\Model\Enum\AssetFileCopyStatus;
use AnzuSystems\CoreDamBundle\Model\Enum\AssetFileFailedType;
use AnzuSystems\CoreDamBundle\Repository\ImageFileRepository;
use AnzuSystems\CoreDamBundle\Security\AccessDenier;
Expand Down Expand Up @@ -88,7 +88,7 @@ public function prepareCopyList(Collection $collection): Collection
}

foreach ($res as $imageCopyResultDto) {
if ($imageCopyResultDto->getResult()->is(AssetFileCopyResult::Copying) && $imageCopyResultDto->getTargetAsset()) {
if ($imageCopyResultDto->getResult()->is(AssetFileCopyStatus::Copy) && $imageCopyResultDto->getTargetAsset()) {
$this->messageBus->dispatch(new CopyAssetFileMessage(
$imageCopyResultDto->getAsset(),
$imageCopyResultDto->getTargetAsset()
Expand Down Expand Up @@ -123,7 +123,7 @@ public function copyAssetFiles(Asset $asset, Asset $copyAsset): void
}
}

private function prepareCopy(ImageCopyDto $copyDto): AssetFileCopyResultDto
public function prepareCopy(ImageCopyDto $copyDto): AssetFileCopyResultDto
{
/** @var array<string, Asset> $foundAssets */
$foundAssets = [];
Expand All @@ -148,7 +148,7 @@ private function prepareCopy(ImageCopyDto $copyDto): AssetFileCopyResultDto
return AssetFileCopyResultDto::create(
asset: $copyDto->getAsset(),
targetAssetLicence: $copyDto->getTargetAssetLicence(),
result: AssetFileCopyResult::Copying,
result: AssetFileCopyStatus::Copy,
targetMainFile: $assetCopy->getMainFile(),
targetAsset: $assetCopy,
);
Expand All @@ -158,21 +158,21 @@ private function prepareCopy(ImageCopyDto $copyDto): AssetFileCopyResultDto
return AssetFileCopyResultDto::create(
asset: $copyDto->getAsset(),
targetAssetLicence: $copyDto->getTargetAssetLicence(),
result: AssetFileCopyResult::NotAllowed,
result: AssetFileCopyStatus::NotAllowed,
assetConflicts: array_values($foundAssets)
);
}

return AssetFileCopyResultDto::create(
asset: $copyDto->getAsset(),
targetAssetLicence: $copyDto->getTargetAssetLicence(),
result: AssetFileCopyResult::Exists,
result: AssetFileCopyStatus::Exists,
targetMainFile: $firstFoundAsset->getMainFile(),
targetAsset: $firstFoundAsset,
);
}

private function copyAssetSlots(Asset $asset, Asset $copyAsset): void
public function copyAssetSlots(Asset $asset, Asset $copyAsset): void
{
foreach ($copyAsset->getSlots() as $targetSlot) {
$assetSlot = $asset->getSlots()->findFirst(
Expand Down
26 changes: 26 additions & 0 deletions src/Domain/Job/JobImageCopyFacade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace AnzuSystems\CoreDamBundle\Domain\Job;

use AnzuSystems\CommonBundle\Entity\Interfaces\JobInterface;
use AnzuSystems\CommonBundle\Validator\Validator;
use AnzuSystems\CoreDamBundle\Entity\JobImageCopy;

final readonly class JobImageCopyFacade
{
public function __construct(
private JobImageCopyManager $manager,
private Validator $validator,
) {
}

public function create(JobImageCopy $job): JobInterface
{
$this->validator->validate($job);
$this->manager->create($job);

return $job;
}
}
38 changes: 38 additions & 0 deletions src/Domain/Job/JobImageCopyFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace AnzuSystems\CoreDamBundle\Domain\Job;

use AnzuSystems\CoreDamBundle\Entity\Asset;
use AnzuSystems\CoreDamBundle\Entity\AssetLicence;
use AnzuSystems\CoreDamBundle\Entity\JobImageCopy;
use AnzuSystems\CoreDamBundle\Entity\JobImageCopyItem;
use Doctrine\Common\Collections\Collection;

final readonly class JobImageCopyFactory
{
public function __construct(
private JobImageCopyFacade $imageCopyFacade,
) {
}

/**
* @param Collection<int|string, Asset> $assets
*/
public function createPodcastSynchronizerJob(AssetLicence $licence, Collection $assets): JobImageCopy
{
$job = (new JobImageCopy())
->setLicence($licence)
->setItems(
$assets->map(
fn (Asset $asset): JobImageCopyItem => (new JobImageCopyItem())->setSourceAsset($asset)
)
)
;

$this->imageCopyFacade->create($job);

return $job;
}
}
Loading

0 comments on commit ba3f982

Please sign in to comment.