From ddb011ef42f95eac731dcb82193f252561de7f36 Mon Sep 17 00:00:00 2001 From: drejmanMacopedia Date: Mon, 5 Jun 2023 11:52:20 +0200 Subject: [PATCH] Translate attributes of one produt at once. Refactor translation process --- .../MassEdit/TranslateAttributesProcessor.php | 4 +- .../MacopediaTranslatorExtension.php | 3 +- .../Repository/AttributeRepository.php | 20 +++++++ .../Resources/config/connector.yml | 2 +- .../Resources/config/repositories.yml | 6 ++ .../Resources/config/services.yml | 2 +- .../Service/TranslateAttributesService.php | 59 +++++++++++++++---- .../Translator/OpenAiTranslator.php | 2 +- 8 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 src/Macopedia/OpenAiTranslator/Repository/AttributeRepository.php create mode 100644 src/Macopedia/OpenAiTranslator/Resources/config/repositories.yml diff --git a/src/Macopedia/OpenAiTranslator/Connector/Processor/MassEdit/TranslateAttributesProcessor.php b/src/Macopedia/OpenAiTranslator/Connector/Processor/MassEdit/TranslateAttributesProcessor.php index 01eb9d2..83ff5c3 100644 --- a/src/Macopedia/OpenAiTranslator/Connector/Processor/MassEdit/TranslateAttributesProcessor.php +++ b/src/Macopedia/OpenAiTranslator/Connector/Processor/MassEdit/TranslateAttributesProcessor.php @@ -45,6 +45,8 @@ public function process(mixed $item): ProductInterface|ProductModelInterface */ private function translateAttributes(mixed $product, array $action): ProductInterface|ProductModelInterface { - return $this->translateAttributesService->translateAttributes($product, $action); + return $this->translateAttributesService + ->setStepExecution($this->stepExecution) + ->translateAttributes($product, $action); } } diff --git a/src/Macopedia/OpenAiTranslator/DependencyInjection/MacopediaTranslatorExtension.php b/src/Macopedia/OpenAiTranslator/DependencyInjection/MacopediaTranslatorExtension.php index ffdfca4..3ce0cc3 100644 --- a/src/Macopedia/OpenAiTranslator/DependencyInjection/MacopediaTranslatorExtension.php +++ b/src/Macopedia/OpenAiTranslator/DependencyInjection/MacopediaTranslatorExtension.php @@ -22,7 +22,8 @@ final class MacopediaTranslatorExtension extends Extension public function load(array $configs, ContainerBuilder $container): void { $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); - $loader->load('connector.yml'); $loader->load('services.yml'); + $loader->load('connector.yml'); + $loader->load('repositories.yml'); } } diff --git a/src/Macopedia/OpenAiTranslator/Repository/AttributeRepository.php b/src/Macopedia/OpenAiTranslator/Repository/AttributeRepository.php new file mode 100644 index 0000000..a9ec30b --- /dev/null +++ b/src/Macopedia/OpenAiTranslator/Repository/AttributeRepository.php @@ -0,0 +1,20 @@ +_em->createQueryBuilder() + ->select('att') + ->from($this->_entityName, 'att', 'att.code') + ->where('att.code IN (:codes)')->setParameter('codes', $codes) + ->getQuery() + ->getResult(); + } +} diff --git a/src/Macopedia/OpenAiTranslator/Resources/config/connector.yml b/src/Macopedia/OpenAiTranslator/Resources/config/connector.yml index 8a73b34..ab7b1b2 100644 --- a/src/Macopedia/OpenAiTranslator/Resources/config/connector.yml +++ b/src/Macopedia/OpenAiTranslator/Resources/config/connector.yml @@ -30,7 +30,7 @@ services: macopedia.step.update_product_translations.mass_edit: class: '%pim_connector.step.item_step.class%' arguments: - - 'perform' + - 'translate' - '@event_dispatcher' - '@akeneo_batch.job_repository' - '@pim_enrich.reader.database.product_and_product_model' diff --git a/src/Macopedia/OpenAiTranslator/Resources/config/repositories.yml b/src/Macopedia/OpenAiTranslator/Resources/config/repositories.yml new file mode 100644 index 0000000..6c2cac2 --- /dev/null +++ b/src/Macopedia/OpenAiTranslator/Resources/config/repositories.yml @@ -0,0 +1,6 @@ +services: + Macopedia\OpenAiTranslator\Repository\AttributeRepository: + factory: [ '@doctrine.orm.entity_manager', 'getRepository' ] + arguments: [ '%pim_catalog.entity.attribute.class%' ] + tags: + - { name: 'pim_repository' } diff --git a/src/Macopedia/OpenAiTranslator/Resources/config/services.yml b/src/Macopedia/OpenAiTranslator/Resources/config/services.yml index cd9cd38..cbf70eb 100644 --- a/src/Macopedia/OpenAiTranslator/Resources/config/services.yml +++ b/src/Macopedia/OpenAiTranslator/Resources/config/services.yml @@ -14,6 +14,6 @@ services: Macopedia\OpenAiTranslator\Service\TranslateAttributesService: arguments: - '@Macopedia\OpenAiTranslator\Translator\OpenAiTranslator' - - '@pim_catalog.repository.attribute' + - '@Macopedia\OpenAiTranslator\Repository\AttributeRepository' - '@pim_catalog.entity_with_family_variant.check_attribute_editable' - '@pim_catalog.updater.property_setter' diff --git a/src/Macopedia/OpenAiTranslator/Service/TranslateAttributesService.php b/src/Macopedia/OpenAiTranslator/Service/TranslateAttributesService.php index 39dd98f..7b3b7d5 100644 --- a/src/Macopedia/OpenAiTranslator/Service/TranslateAttributesService.php +++ b/src/Macopedia/OpenAiTranslator/Service/TranslateAttributesService.php @@ -7,16 +7,22 @@ use Akeneo\Pim\Enrichment\Component\Product\EntityWithFamilyVariant\CheckAttributeEditable; use Akeneo\Pim\Enrichment\Component\Product\Model\ProductInterface; use Akeneo\Pim\Enrichment\Component\Product\Model\ProductModelInterface; -use Akeneo\Pim\Enrichment\Component\Product\Value\ScalarValue; use Akeneo\Pim\Structure\Component\AttributeTypes; use Akeneo\Pim\Structure\Component\Repository\AttributeRepositoryInterface; +use Akeneo\Tool\Component\Batch\Item\DataInvalidItem; +use Akeneo\Tool\Component\Batch\Job\BatchStatus; +use Akeneo\Tool\Component\Batch\Job\ExitStatus; +use Akeneo\Tool\Component\Batch\Model\StepExecution; use Akeneo\Tool\Component\StorageUtils\Updater\PropertySetterInterface; use Macopedia\OpenAiTranslator\Translator\Language; use Macopedia\OpenAiTranslator\Translator\TranslatorInterface; +use Macopedia\OpenAiTranslator\Exception\InvalidOpenAiResponseException; use Webmozart\Assert\Assert; class TranslateAttributesService { + private StepExecution $stepExecution; + public function __construct( private TranslatorInterface $translator, private AttributeRepositoryInterface $attributeRepository, @@ -25,6 +31,13 @@ public function __construct( ) { } + public function setStepExecution(StepExecution $stepExecution): self + { + $this->stepExecution = $stepExecution; + + return $this; + } + /** * @param ProductInterface|ProductModelInterface $product * @param array> $action @@ -32,14 +45,24 @@ public function __construct( public function translateAttributes(mixed $product, array $action): ProductInterface|ProductModelInterface { [$sourceScope, $targetScope, $sourceLocaleAkeneo, $targetLocaleAkeneo, $targetLocale, $attributesToTranslate] = $this->extractVariables($action); + $translations = []; + + $attributes = $this->attributeRepository->getAttributesByCodes($attributesToTranslate); + $summary = []; + $scopes = []; - foreach ($attributesToTranslate as $attributeCode) { - $attribute = $this->attributeRepository->findOneByIdentifier($attributeCode); + foreach ($attributes as $attribute) { if (!$this->checkAttributeEditable->isEditable($product, $attribute)) { + $this->stepExecution->addWarning('Attribute is not editable', [], new DataInvalidItem($attribute)); + $this->stepExecution->incrementSummaryInfo('skip'); + $this->stepExecution->incrementProcessedItems(); continue; } if (!($attribute->getType() === AttributeTypes::TEXT || $attribute->getType() === AttributeTypes::TEXTAREA)) { + $this->stepExecution->addWarning('Attribute is not text', [], new DataInvalidItem($attribute)); + $this->stepExecution->incrementSummaryInfo('skip'); + $this->stepExecution->incrementProcessedItems(); continue; } @@ -48,26 +71,38 @@ public function translateAttributes(mixed $product, array $action): ProductInter $targetScope = null; } + $attributeCode = $attribute->getCode(); $attributeValue = $product->getValue($attributeCode, $sourceLocaleAkeneo, $sourceScope); if ($attributeValue === null) { + $this->stepExecution->addWarning('Attribute value is empty', [], new DataInvalidItem($attribute)); + $this->stepExecution->incrementSummaryInfo('skip'); + $this->stepExecution->incrementProcessedItems(); continue; } - $translatedText = $this->translator->translate( - $attributeValue->getData(), - $targetLocale - ); + $translations[$attributeCode] = $attributeValue->getData(); + $scopes[$attributeCode] = $targetScope; + } - if ($translatedText === null) { - continue; - } + $translatedText = $this->translator->translate( + json_encode($translations), + $targetLocale + ); - $this->propertySetter->setData($product, $attributeCode, $translatedText, [ + if ($translatedText === null) { + throw new InvalidOpenAiResponseException($translations); + } + + foreach (json_decode($translatedText) as $key => $translation) { + $summary[$key] = [[$translations[$key] => $translation]]; + $this->propertySetter->setData($product, $key, $translation, [ 'locale' => $targetLocaleAkeneo, - 'scope' => $targetScope, + 'scope' => $scopes[$key], ]); } + $this->stepExecution->setExitStatus(new ExitStatus(ExitStatus::COMPLETED, json_encode($summary))); + return $product; } diff --git a/src/Macopedia/OpenAiTranslator/Translator/OpenAiTranslator.php b/src/Macopedia/OpenAiTranslator/Translator/OpenAiTranslator.php index 6ac423e..ad920ba 100644 --- a/src/Macopedia/OpenAiTranslator/Translator/OpenAiTranslator.php +++ b/src/Macopedia/OpenAiTranslator/Translator/OpenAiTranslator.php @@ -8,7 +8,7 @@ class OpenAiTranslator implements TranslatorInterface { - private const MESSAGE = 'Translate text betweeen and to %s. Keep HTMl unchanged. %s'; + private const MESSAGE = 'Translate all values of given JSON betweeen and to %s. Keep HTML unchanged. Return valid JSON. %s'; public function __construct( private OpenAiClient $openAiClient