[export][person] Feature: allow to filter accompanying period by

geographical unit
This commit is contained in:
Julien Fastré 2022-10-04 15:07:07 +02:00
parent 52435f5331
commit fc567868c1
4 changed files with 164 additions and 49 deletions

View File

@ -53,20 +53,36 @@ class GeographicalUnit
return $this->id; return $this->id;
} }
public function getLayerName(): ?string
{
return $this->layerName;
}
public function getUnitName(): ?string public function getUnitName(): ?string
{ {
return $this->unitName; return $this->unitName;
} }
public function setLayerName(?string $layerName): self /**
* @return GeographicalUnitLayer|null
*/
public function getLayer(): ?GeographicalUnitLayer
{ {
$this->layerName = $layerName; return $this->layer;
}
/**
* @param string $unitRefId
* @return GeographicalUnit
*/
public function setUnitRefId(string $unitRefId): GeographicalUnit
{
$this->unitRefId = $unitRefId;
return $this;
}
/**
* @param GeographicalUnitLayer|null $layer
* @return GeographicalUnit
*/
public function setLayer(?GeographicalUnitLayer $layer): GeographicalUnit
{
$this->layer = $layer;
return $this; return $this;
} }

View File

@ -0,0 +1,57 @@
<?php
namespace Chill\MainBundle\Repository;
use Chill\MainBundle\Entity\GeographicalUnit;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
class GeographicalUnitRepository implements GeographicalUnitRepositoryInterface
{
private EntityRepository $repository;
private EntityManagerInterface $em;
public function __construct(EntityManagerInterface $em)
{
$this->repository = $em->getRepository($this->getClassName());
$this->em = $em;
}
public function find($id): ?GeographicalUnit
{
return $this->repository->find($id);
}
/**
* Will return only partial object, where the @link{GeographicalUnit::geom} property is not loaded
*
* @return array|GeographicalUnit[]
*/
public function findAll(): array
{
return $this->repository
->createQueryBuilder('gu')
->addSelect('PARTIAL gu.{id,unitName,unitRefId,layer}')
->addOrderBy('IDENTITY(gu.layer)')
->addOrderBy(('gu.unitName'))
->getQuery()
->getResult();
}
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): ?GeographicalUnit
{
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
}
public function findOneBy(array $criteria): ?GeographicalUnit
{
return $this->repository->findOneBy($criteria);
}
public function getClassName(): string
{
return GeographicalUnit::class;
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace Chill\MainBundle\Repository;
use Doctrine\Persistence\ObjectRepository;
interface GeographicalUnitRepositoryInterface extends ObjectRepository
{
}

View File

@ -11,12 +11,18 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Entity\GeographicalUnit; use Chill\MainBundle\Entity\GeographicalUnit;
use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Repository\GeographicalUnitRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Export\Declarations;
use DateTime; use DateTime;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
@ -24,17 +30,27 @@ use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
/** /**
* e) par zone géographique. * Filter accompanying period by geographical zone
*
* Paramètre:
* * Date
* * Choix unique entre: territoire, epci, canton, commune, secteur d'intervention
* * une fois le premier choix effectué, l'utilisateur choisi parmi les zones (choix multiple)
*
* Le filtre retiendra les parcours localisé dans un des territoires cochés, à la date indiquée en paramètre.
*/ */
class GeographicalUnitStatFilter implements FilterInterface class GeographicalUnitStatFilter implements FilterInterface
{ {
private GeographicalUnitRepositoryInterface $geographicalUnitRepository;
private TranslatableStringHelperInterface $translatableStringHelper;
private EntityManagerInterface $em;
public function __construct(
EntityManagerInterface $em,
GeographicalUnitRepositoryInterface $geographicalUnitRepository,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->em = $em;
$this->geographicalUnitRepository = $geographicalUnitRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string public function addRole(): ?string
{ {
return null; return null;
@ -42,33 +58,32 @@ class GeographicalUnitStatFilter implements FilterInterface
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
if (!in_array('location', $qb->getAllAliases(), true)) { $subQueryDql =
$qb->join('acp.administrativeLocation', 'location'); 'SELECT
} 1
FROM '.AccompanyingPeriod\AccompanyingPeriodLocationHistory::class.' acp_geog_filter_location_history
LEFT JOIN '.PersonHouseholdAddress::class.' acp_geog_filter_address_person_location
WITH IDENTITY(acp_geog_filter_location_history.personLocation) = IDENTITY(acp_geog_filter_address_person_location.person)
LEFT JOIN '.Address::class.' acp_geog_filter_address
WITH COALESCE(IDENTITY(acp_geog_filter_address_person_location.address), IDENTITY(acp_geog_filter_location_history.addressLocation)) = acp_geog_filter_address.id
LEFT JOIN '.GeographicalUnit::class.' acp_geog_filter_units WITH ST_CONTAINS(acp_geog_units.geom, acp_geog_filter_address.point) = TRUE
WHERE
(acp_geog_filter_location_history.startDate <= :acp_geog_filter_date AND (
acp_geog_filter_location_history.endDate IS NULL OR acp_geog_filter_location_history.endDate < :acp_geog_filter_date
))
AND
(acp_geog_filter_address_person_location.validFrom < :acp_geog_filter_date AND (
acp_geog_filter_address_person_location.validTo IS NULL OR acp_geog_filter_address_person_location.validTo < :acp_geog_filter_date
))
AND acp_geog_filter_units IN (:acp_geog_filter_units)
AND acp_geog_filter_location_history.period = acp.id
';
if (!in_array('address', $qb->getAllAliases(), true)) { $qb
$qb->join('location.address', 'address'); ->andWhere($qb->expr()->exists($subQueryDql))
} ->setParameter('acp_geog_filter_date', $data['date_calc'])
->setParameter('acp_geog_filter_units', $data['units'])
if (!in_array('geounit', $qb->getAllAliases(), true)) { ;
$qb->join(GeographicalUnit::class, 'geounit', Expr\Join::WITH, 'ST_CONTAINS(address.point, geounit.geom) = TRUE');
}
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->andX(
$qb->expr()->eq($x, ':date'),
$qb->expr()->in($x, ':loctype')
);
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('date', $data['date'], Types::DATE_MUTABLE);
$qb->setParameter('loctype', $data['accepted_loctype']);
} }
public function applyOn(): string public function applyOn(): string
@ -79,23 +94,40 @@ class GeographicalUnitStatFilter implements FilterInterface
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
$builder $builder
->add('date', ChillDateType::class, [ ->add('date_calc', ChillDateType::class, [
'data' => new DateTime(), 'label' => 'Compute geographical location at date',
'required' => true,
'data' => new \DateTimeImmutable('today'),
'input' => 'datetime_immutable',
]) ])
->add('accepted_loctype', EntityType::class, [ ->add('units', EntityType::class, [
'label' => 'Geographical unit',
'placeholder' => 'Select a geographical unit',
'class' => GeographicalUnit::class, 'class' => GeographicalUnit::class,
'choice_label' => static function (GeographicalUnit $u) { 'choices' => $this->geographicalUnitRepository->findAll(),
return $u->getUnitName(); 'choice_label' => function(GeographicalUnit $item) {
return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName();
}, },
'attr' => [
'class' => 'select2',
],
'multiple' => true, 'multiple' => true,
'expanded' => true,
]); ]);
} }
public function describeAction($data, $format = 'string'): array public function describeAction($data, $format = 'string'): array
{ {
return ['Filtered by geographic unit: only %date%', [ return ['Filtered by geographic unit: computed at %date%, only in %units%', [
'%date%' => $data['date']->format('d-m-Y'), '%date%' => $data['date_calc']->format('d-m-Y'),
'%units' => implode(
', ',
array_map(
function(GeographicalUnit $item) {
return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName();
},
$data['units']
)
)
]]; ]];
} }