Skip to content

Commit

Permalink
Merge pull request #2128 from MTES-MCT/feature/1963-import-signalemen…
Browse files Browse the repository at this point in the history
…t-67

[Signalement - BO] Import Données 67 Bas Rhin
  • Loading branch information
emilschn committed Feb 6, 2024
2 parents ddcd5dc + c845eca commit e433331
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 35 deletions.
22 changes: 21 additions & 1 deletion src/Command/ImportSignalementCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,27 @@ protected function execute(InputInterface $input, OutputInterface $output): int
);

$metadata = $this->signalementImportLoader->getMetadata();

if (\count($metadata['files_not_found'])) {
$msg = [];
foreach ($metadata['files_not_found'] as $fileNotFound) {
$msg[] = sprintf('File "%s" not found', $fileNotFound);
}
$io->warning($msg);
}
if (\count($metadata['motif_cloture_not_found'])) {
$msg = [];
foreach ($metadata['motif_cloture_not_found'] as $partnerNotFound => $nbSignalement) {
$msg[] = sprintf('MotifCloture "%s" not found on %s signalement(s)', $partnerNotFound, $nbSignalement);
}
$io->warning($msg);
}
if (\count($metadata['partners_not_found'])) {
$msg = [];
foreach ($metadata['partners_not_found'] as $partnerNotFound => $nbSignalement) {
$msg[] = sprintf('Partner "%s" not found on %s signalement(s)', $partnerNotFound, $nbSignalement);
}
$io->warning($msg);
}
$io->success(sprintf('%s signalement(s) have been imported', $metadata['count_signalement']));

return Command::SUCCESS;
Expand Down
5 changes: 5 additions & 0 deletions src/Entity/Signalement.php
Original file line number Diff line number Diff line change
Expand Up @@ -2198,6 +2198,11 @@ public function removeDesordrePrecision(DesordrePrecision $desordrePrecision): s
return $this;
}

public function __toString(): string
{
return $this->reference;
}

public function hasQualificaton(Qualification $qualification): bool
{
/** @var SignalementQualification $signalementQualification */
Expand Down
93 changes: 64 additions & 29 deletions src/Service/Import/Signalement/SignalementImportLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,21 @@
use App\Manager\TagManager;
use App\Repository\CritereRepository;
use App\Repository\CriticiteRepository;
use App\Service\ImageManipulationHandler;
use App\Service\Signalement\CriticiteCalculator;
use App\Service\Signalement\Qualification\SignalementQualificationUpdater;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\NonUniqueResultException;
use League\Flysystem\FilesystemOperator;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;

class SignalementImportLoader
{
private const FLUSH_COUNT = 200;
private const FLUSH_COUNT = 20;
private const REGEX_DATE_FORMAT_CSV = '/\d{4}\/\d{2}\/\d{2}/';

private const SITUATIONS = [
Expand All @@ -44,6 +46,9 @@ class SignalementImportLoader

private array $metadata = [
'count_signalement' => 0,
'partners_not_found' => [],
'motif_cloture_not_found' => [],
'files_not_found' => [],
];

private User|null $userSystem = null;
Expand All @@ -60,6 +65,7 @@ public function __construct(
private CriticiteCalculator $criticiteCalculator,
private SignalementQualificationUpdater $signalementQualificationUpdater,
private FileManager $fileManager,
private FilesystemOperator $fileStorage,
) {
}

Expand Down Expand Up @@ -106,8 +112,8 @@ public function load(Territory $territory, array $data, array $headers, ?OutputI
$signalement->addSuivi($suivi);
}

$this->loadFiles($signalement, File::INPUT_NAME_PHOTOS, $dataMapped, File::FILE_TYPE_PHOTO);
$this->loadFiles($signalement, File::INPUT_NAME_DOCUMENTS, $dataMapped, File::FILE_TYPE_DOCUMENT);
$this->loadFiles($signalement, File::INPUT_NAME_PHOTOS, $dataMapped);
$this->loadFiles($signalement, File::INPUT_NAME_DOCUMENTS, $dataMapped);

$this->metadata['count_signalement'] = $countSignalement;
if (0 === $countSignalement % self::FLUSH_COUNT) {
Expand All @@ -117,6 +123,12 @@ public function load(Territory $territory, array $data, array $headers, ?OutputI
$this->signalementManager->persist($signalement);
unset($signalement);
}
if ($dataMapped['motifCloture'] && !MotifCloture::tryFrom($dataMapped['motifCloture'])) {
if (!isset($this->metadata['motif_cloture_not_found'][$dataMapped['motifCloture']])) {
$this->metadata['motif_cloture_not_found'][$dataMapped['motifCloture']] = 1;
}
++$this->metadata['motif_cloture_not_found'][$dataMapped['motifCloture']];
}
}
}

Expand Down Expand Up @@ -145,35 +157,46 @@ private function loadAffectation(Signalement $signalement, Territory $territory,
{
$affectationCollection = new ArrayCollection();
if (isset($dataMapped['partners']) && !empty($dataMapped['partners'])) {
$partnersName = explode(',', $dataMapped['partners']);
if (str_contains($dataMapped['partners'], ',')) {
$partnersName = explode(',', $dataMapped['partners']);
} elseif (str_contains($dataMapped['partners'], '|')) {
$partnersName = explode('|', $dataMapped['partners']);
} else {
$partnersName = [$dataMapped['partners']];
}
foreach ($partnersName as $partnerName) {
$partnerNameCleaned = trim(preg_replace('/[\x00-\x1F\x7F\xA0]/u', '', $partnerName)); // remove non printable chars
$partner = $this->entityManager->getRepository(Partner::class)->findOneBy([
'nom' => trim($partnerName),
'nom' => $partnerNameCleaned,
'territory' => $territory,
]);
if (!$partner) {
if (!isset($this->metadata['partners_not_found'][$partnerNameCleaned])) {
$this->metadata['partners_not_found'][$partnerNameCleaned] = 1;
}
++$this->metadata['partners_not_found'][$partnerNameCleaned];
continue;
}

if (null !== $partner) {
$affectation = $this->affectationManager->createAffectationFrom(
$signalement,
$partner,
$partner?->getUsers()?->first()
);

if ($affectation instanceof Affectation) {
$affectation
->setCreatedAt($dataMapped['createdAt'])
->setAnsweredAt($dataMapped['createdAt']);
if (!empty($dataMapped['motifCloture'])) {
$affectation = $this->affectationManager->closeAffectation(
$affectation,
$this->userSystem,
MotifCloture::tryFrom($dataMapped['motifCloture'])
);
} else {
$affectation->setStatut(Affectation::STATUS_ACCEPTED);
}
$affectationCollection->add($affectation);
$affectation = $this->affectationManager->createAffectationFrom(
$signalement,
$partner,
$partner?->getUsers()?->first()
);
if ($affectation instanceof Affectation) {
$affectation
->setCreatedAt($dataMapped['createdAt'])
->setAnsweredAt($dataMapped['createdAt']);
if (MotifCloture::tryFrom($dataMapped['motifCloture'])) {
$affectation = $this->affectationManager->closeAffectation(
$affectation,
$this->userSystem,
MotifCloture::tryFrom($dataMapped['motifCloture'])
);
} else {
$affectation->setStatut(Affectation::STATUS_ACCEPTED);
}
$affectationCollection->add($affectation);
}
}
}
Expand Down Expand Up @@ -258,23 +281,35 @@ private function loadSuivi(Signalement $signalement, array $dataMapped): ArrayCo
return $suiviCollection;
}

private function loadFiles(Signalement $signalement, string $colName, array $dataMapped, string $fileType): void
private function loadFiles(Signalement $signalement, string $colName, array $dataMapped): void
{
if (empty($dataMapped[$colName])) {
return;
}

$fileList = explode('|', $dataMapped[$colName]);
foreach ($fileList as $filename) {
$exist = $this->entityManager->getRepository(File::class)->findOneBy(['filename' => $filename]);
if ($exist) {
continue;
}
if (!$this->fileStorage->fileExists($filename)) {
$this->metadata['files_not_found'][$filename] = $filename;
continue;
}
$fileType = File::FILE_TYPE_DOCUMENT;
if (\in_array($this->fileStorage->mimeType($filename), ImageManipulationHandler::IMAGE_MIME_TYPES)) {
$fileType = File::FILE_TYPE_PHOTO;
}

$file = $this->fileManager->createOrUpdate(
filename: $filename,
title: $filename,
type: $fileType,
signalement: $signalement,
);
$signalement->addFile($file);
unset($file);
}

$this->fileManager->flush();
}
}
46 changes: 42 additions & 4 deletions src/Service/Import/Signalement/SignalementImportMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ public function map(array $columns, array $data): array
$fieldValue = trim($fieldValue, '"');
switch ($fieldColumn) {
case 'reference':
if (preg_match('/20\d{2}-\d{4}/', $fieldValue)) {
break;
}
$result = preg_split('/[-|\/]/', $fieldValue);
if (\count($result) > 1) {
list($index, $year) = $result;
Expand All @@ -156,13 +159,23 @@ public function map(array $columns, array $data): array
case 'isLogementSocial':
case 'isPreavisDepart':
case 'isRelogement':
case 'isNotOccupant':
case 'isFondSolidariteLogement':
case 'isBailEnCours':
case 'isRefusIntervention':
case 'isCguAccepted':
$fieldValue = 'NSP' !== $data[$fileColumn] ? 'O' === $fieldValue : null;
break;
case 'isNotOccupant':
if ('O' == $fieldValue) {
$fieldValue = false;
break;
}
if ('N' == $fieldValue) {
$fieldValue = true;
break;
}
$fieldValue = null;
break;
case 'createdAt':
case 'modifiedAt':
case 'closedAt':
Expand Down Expand Up @@ -190,9 +203,30 @@ public function map(array $columns, array $data): array
$fieldValue = $this->transformToSignalementStatus($fieldValue);
break;
case 'motifCloture':
$fieldValue = mb_strtoupper($fieldValue);
// TODO : faire un mapping ?
$fieldValue = \array_key_exists($fieldValue, MotifCloture::getLabelList()) ? $fieldValue : null;
if (!$fieldValue) {
break;
}
if ('Abandon de procédure' == $fieldValue) {
$fieldValue = 'ABANDON_DE_PROCEDURE_ABSENCE_DE_REPONSE';
break;
}
if ('Responsabilité de l\'occupant' == $fieldValue) {
$fieldValue = 'RESPONSABILITE_DE_L_OCCUPANT';
break;
}
if ('Logement décent - Pas d\'infraction' == $fieldValue) {
$fieldValue = 'LOGEMENT_DECENT';
break;
}
$listMotifs = MotifCloture::getLabelList();
if (\in_array($fieldValue, $listMotifs)) {
$fieldValue = array_search($fieldValue, $listMotifs);
break;
}
if (isset($listMotifs[mb_strtoupper($fieldValue)])) {
$fieldValue = mb_strtoupper($fieldValue);
break;
}
break;
case self::SITUATION_SECURITE_OCCUPANT:
case self::SITUATION_ETAT_PROPRETE_LOGEMENT:
Expand All @@ -210,6 +244,10 @@ public function map(array $columns, array $data): array
$fieldValue = str_pad($fieldValue, 10, '0', \STR_PAD_LEFT);
}
break;
case 'nomDeclarant':
case 'nomOccupant':
$fieldValue = mb_strimwidth($fieldValue, 0, 50);
break;
default:
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
use Doctrine\ORM\Events;
use Doctrine\ORM\NonUniqueResultException;
use Faker\Factory;
use League\Flysystem\FilesystemOperator;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
Expand All @@ -34,6 +36,7 @@ class SignalementImportLoaderTest extends KernelTestCase
private CriticiteCalculator $criticiteCalculator;
private SignalementQualificationUpdater $signalementQualificationUpdater;
private FileManager $fileManager;
private MockObject|FilesystemOperator $filesystemOperator;

protected function setUp(): void
{
Expand All @@ -48,7 +51,7 @@ protected function setUp(): void
$this->criticiteCalculator = self::getContainer()->get(CriticiteCalculator::class);
$this->signalementQualificationUpdater = self::getContainer()->get(SignalementQualificationUpdater::class);
$this->fileManager = self::getContainer()->get(FileManager::class);

$this->filesystemOperator = $this->createMock(FilesystemOperator::class);
$this->entityManager = $kernel->getContainer()->get('doctrine')->getManager();
}

Expand All @@ -70,6 +73,7 @@ public function testLoadSignalementImport()
$this->criticiteCalculator,
$this->signalementQualificationUpdater,
$this->fileManager,
$this->filesystemOperator
);

$territory = $this->entityManager->getRepository(Territory::class)->findOneBy(['zip' => '01']);
Expand Down
3 changes: 3 additions & 0 deletions tests/Unit/Command/ImportSignalementCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ public function testDisplaySuccessfullyMessage()
->method('getMetaData')
->willReturn([
'count_signalement' => 1,
'partners_not_found' => [],
'motif_cloture_not_found' => [],
'files_not_found' => [],
]);

$csvParser = $this->createMock(CsvParser::class);
Expand Down

0 comments on commit e433331

Please sign in to comment.