New permission for duplicate & export duplicate persons

Signed-off-by: Mathieu Jaumotte <mathieu.jaumotte@champs-libres.coop>
This commit is contained in:
2021-03-21 14:14:38 +01:00
parent 0149457fba
commit c34b992437
5 changed files with 229 additions and 9 deletions

View File

@@ -0,0 +1,202 @@
<?php
namespace Chill\PersonBundle\Export\Export;
use Chill\MainBundle\Export\DirectExportInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Doctrine\ORM\EntityManagerInterface;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
* Render a list of duplicate peoples
*/
class ListPersonDuplicate implements DirectExportInterface, ExportElementValidatedInterface
{
/**
*
* @var EntityManagerInterface
*/
protected $entityManager;
/**
* @var \Symfony\Component\Translation\TranslatorInterface
*/
private $translator;
/**
* @var \Symfony\Component\Routing\Generator\UrlGeneratorInterface
*/
private $router;
/**
* @var string
*/
protected $baseUrl;
/**
* @var float
*/
private const PRECISION_DEFAULT_VALUE = 0.7;
public function __construct(
EntityManagerInterface $em,
TranslatorInterface $translator,
UrlGeneratorInterface $router,
$routeParameters
) {
$this->entityManager = $em;
$this->translator = $translator;
$this->router = $router;
$this->baseUrl = $routeParameters['scheme'].
'://'.$routeParameters['host'];
}
/**
* {@inheritDoc}
*
* @return string
*/
public function getTitle()
{
return "List duplicates";
}
/**
* {@inheritDoc}
*
* @return string
*/
public function getDescription()
{
return "Create a list of duplicate people.";
}
/**
* {@inheritDoc}
*
* @param FormBuilderInterface $builder
*/
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('precision', NumberType::class, [
'label' => 'Precision',
'data' => self::PRECISION_DEFAULT_VALUE,
]);
}
public function validateForm($data, ExecutionContextInterface $context)
{
}
public function generate(array $acl, array $data = []): Response
{
$values = [];
$values[] = $this->getHeaders();
$result = $this->getResult($data);
foreach ($result as $row) {
$values[] = [
$row['id1'],
$row['firstname1'],
$row['lastname1'],
$this->baseUrl.$this->router->generate('chill_person_view', ['person_id' => $row['id1']]),
$row['id2'],
$row['firstname2'],
$row['lastname2'],
$this->baseUrl.$this->router->generate('chill_person_view', ['person_id' => $row['id2']]),
];
}
$spreadsheet = new Spreadsheet();
$spreadsheet->getActiveSheet()->fromArray($values);
// Make links clickable
for ($i = 1; $i <= $spreadsheet->getActiveSheet()->getHighestDataRow(); $i++) {
$spreadsheet->getActiveSheet()->getCell('D'.$i)->getHyperlink()
->setUrl($spreadsheet->getActiveSheet()->getCell('D'.$i)->getValue());
$spreadsheet->getActiveSheet()->getCell('H'.$i)->getHyperlink()
->setUrl($spreadsheet->getActiveSheet()->getCell('H'.$i)->getValue());
}
$writer = new Xlsx($spreadsheet);
$temp_file = sys_get_temp_dir().'/'.uniqid('export_').'.xlsx';
$writer->save($temp_file);
$response = new BinaryFileResponse($temp_file);
$response->headers->set('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'export_duplicate.xlsx');
return $response;
}
protected function getResult($data = [])
{
$precision = $data['precision'] ?? self::PRECISION_DEFAULT_VALUE;
$sql = 'SELECT
p.id as id1, p.firstname as firstname1, p.lastname as lastname1, p.fullnamecanonical as fullnamecanonical1,
p2.id as id2, p2.firstname as firstname2, p2.lastname as lastname2, p2.fullnamecanonical as fullnamecanonical2,
SIMILARITY(p.fullnamecanonical, p2.fullnamecanonical) AS "similarity nom + prenom",
SIMILARITY(p.lastname, p2.lastname) AS "similarity nom",
SIMILARITY(p.firstname, p2.firstname) AS "similarity prenom"
FROM chill_person_person AS p
JOIN chill_person_person AS p2
ON p.id != p2.id
AND (SIMILARITY(p.fullnamecanonical, p2.fullnamecanonical) > :precision
AND p.id < p2.id)
OR (UNACCENT(LOWER(p.firstname)) = UNACCENT(LOWER(p2.lastname))
AND UNACCENT(LOWER(p.lastname)) = UNACCENT(LOWER(p2.firstname)))
JOIN centers AS p1center
ON p1center.id = p.center_id
JOIN centers AS p2center
ON p2center.id = p2.center_id
WHERE NOT EXISTS (
SELECT id
FROM chill_person_not_duplicate as pnd
WHERE (pnd.person1_id = p.id
AND pnd.person2_id = p2.id)
OR (pnd.person2_id = p.id
AND pnd.person1_id = p2.id)
)
ORDER BY p.fullnamecanonical, p.id, p2.id';
$statement = $this->entityManager->getConnection()->prepare($sql);
$statement->bindValue('precision', $precision);
$statement->execute();
return $statement->fetchAll();
}
protected function getHeaders(): array
{
return [
$this->translator->trans('Departure folder number'),
$this->translator->trans('Last name'),
$this->translator->trans('First name'),
$this->translator->trans('Link'),
$this->translator->trans('Arrival folder number'),
$this->translator->trans('Last name'),
$this->translator->trans('First name'),
$this->translator->trans('Link'),
];
}
public function requiredRole(): Role
{
return new Role(PersonVoter::DUPLICATE);
}
}