Feature: aggregator for accompanying course by geographical level

This commit is contained in:
Julien Fastré 2022-10-03 18:20:13 +02:00
parent 65f6712a15
commit 52435f5331
6 changed files with 228 additions and 23 deletions

View File

@ -44,7 +44,7 @@ class GeographicalUnit
private string $unitRefId; private string $unitRefId;
/** /**
* @ORM\ManyToOne(targetEntity=GeographicalUnitLayer::class) * @ORM\ManyToOne(targetEntity=GeographicalUnitLayer::class, inversedBy="units")
*/ */
private ?GeographicalUnitLayer $layer; private ?GeographicalUnitLayer $layer;

View File

@ -2,6 +2,8 @@
namespace Chill\MainBundle\Entity; namespace Chill\MainBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
@ -29,6 +31,16 @@ class GeographicalUnitLayer
*/ */
private string $refId = ''; private string $refId = '';
/**
* @ORM\OneToMany(targetEntity=GeographicalUnit::class, mappedBy="layer")
*/
private Collection $units;
public function __construct()
{
$this->units = new ArrayCollection();
}
/** /**
* @return int|null * @return int|null
*/ */
@ -54,4 +66,20 @@ class GeographicalUnitLayer
$this->name = $name; $this->name = $name;
return $this; return $this;
} }
/**
* @return string
*/
public function getRefId(): string
{
return $this->refId;
}
/**
* @return Collection
*/
public function getUnits(): Collection
{
return $this->units;
}
} }

View File

@ -0,0 +1,59 @@
<?php
namespace Chill\MainBundle\Repository;
use Chill\MainBundle\Entity\GeographicalUnit;
use Chill\MainBundle\Entity\GeographicalUnitLayer;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use UnexpectedValueException;
final class GeographicalUnitLayerLayerRepository implements GeographicalUnitLayerRepositoryInterface
{
private EntityRepository $repository;
public function __construct(EntityManagerInterface $em)
{
$this->repository = $em->getRepository($this->getClassName());
}
public function find($id): ?GeographicalUnitLayer
{
return $this->repository->find($id);
}
/**
* @return array|GeographicalUnitLayer[]
*/
public function findAll(): array
{
return $this->repository->findAll();
}
/**
* @return array|GeographicalUnitLayer[]
*/
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
{
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
}
public function findOneBy(array $criteria): ?GeographicalUnitLayer
{
return $this->repository->findOneBy($criteria);
}
public function getClassName(): string
{
return GeographicalUnitLayer::class;
}
public function findAllHavingUnits(): array
{
$qb = $this->repository->createQueryBuilder('l');
return $qb->where($qb->expr()->gt('SIZE(l.units)', 0))
->getQuery()
->getResult();
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace Chill\MainBundle\Repository;
use Chill\MainBundle\Entity\GeographicalUnitLayer;
use Doctrine\Persistence\ObjectRepository;
interface GeographicalUnitLayerRepositoryInterface extends ObjectRepository
{
/**
* @return array|GeographicalUnitLayer[]
*/
public function findAllHavingUnits(): array;
}

View File

@ -2,39 +2,69 @@
namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators; namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Entity\GeographicalUnit;
use Chill\MainBundle\Entity\GeographicalUnitLayer;
use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Repository\GeographicalUnitLayerRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository; use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
final class GeographicalUnitStatAggregator implements AggregatorInterface final class GeographicalUnitStatAggregator implements AggregatorInterface
{ {
/* private GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository;
private EntityRepository $repository;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct( public function __construct(
EntityManagerInterface $em GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository,
TranslatableStringHelperInterface $translatableStringHelper
) { ) {
$this->repository = $em->getRepository(...::class); $this->geographicalUnitLayerRepository = $geographicalUnitLayerRepository;
$this->translatableStringHelper = $translatableStringHelper;
} }
*/
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getLabels($key, array $values, $data) public function getLabels($key, array $values, $data)
{ {
return function ($value): string { switch ($key) {
if ('_header' === $value) { case 'acp_geog_agg_unitname':
return 'Geographical unit'; return function ($value): string {
} if ('_header' === $value) {
return 'acp_geog_agg_unitname';
}
$g = $this->repository->find($value); if (null === $value) {
return '';
}
return $g; //... return $value;
}; };
case 'acp_geog_agg_unitrefid':
return function ($value): string {
if ('_header' === $value) {
return 'acp_geog_agg_unitrefid';
}
if (null === $value) {
return '';
}
return $value;
};
default:
throw new \UnexpectedValueException('this value should not happens');
}
} }
/** /**
@ -42,7 +72,7 @@ final class GeographicalUnitStatAggregator implements AggregatorInterface
*/ */
public function getQueryKeys($data): array public function getQueryKeys($data): array
{ {
return ['geographicalunitstat_aggregator']; return ['acp_geog_agg_unitname', 'acp_geog_agg_unitrefid'];
} }
/** /**
@ -50,7 +80,22 @@ final class GeographicalUnitStatAggregator implements AggregatorInterface
*/ */
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
// TODO: Implement buildForm() method. $builder
->add('date_calc', ChillDateType::class, [
'label' => 'Compute geographical location at date',
'required' => true,
'data' => new \DateTimeImmutable('today'),
'input' => 'datetime_immutable',
])
->add('level', EntityType::class, [
'label' => 'Geographical layer',
'placeholder' => 'Select a geographical layer',
'class' => GeographicalUnitLayer::class,
'choices' => $this->geographicalUnitLayerRepository->findAllHavingUnits(),
'choice_label' => function(GeographicalUnitLayer $item) {
return $this->translatableStringHelper->localize($item->getName());
},
]);
} }
/** /**
@ -74,16 +119,70 @@ final class GeographicalUnitStatAggregator implements AggregatorInterface
*/ */
public function alterQuery(QueryBuilder $qb, $data) public function alterQuery(QueryBuilder $qb, $data)
{ {
if (!in_array('acp_geog_agg_location_history', $qb->getAllAliases(), true)) {
$qb->leftJoin('acp.locationHistories', 'acp_geog_agg_location_history');
//$qb->addSelect('... AS geographicalunitstat_aggregator'); $qb->andWhere(
$qb->expr()->andX(
'acp_geog_agg_location_history.startDate <= :acp_geog_aggregator_date',
$qb->expr()->orX(
'acp_geog_agg_location_history.endDate IS NULL',
'acp_geog_agg_location_history.endDate > :acp_geog_aggregator_date'
)
)
);
$groupby = $qb->getDQLPart('groupBy'); $qb->setParameter('acp_geog_aggregator_date', $data['date_calc']);
if (!empty($groupBy)) {
$qb->addGroupBy('geographicalunitstat_aggregator');
} else {
$qb->groupBy('geographicalunitstat_aggregator');
} }
// link between location history and person
if (!in_array('acp_geog_agg_address_person_location', $qb->getAllAliases(), true)) {
$qb->leftJoin(
PersonHouseholdAddress::class,
'acp_geog_agg_address_person_location',
Join::WITH,
$qb->expr()->andX(
'IDENTITY(acp_geog_agg_address_person_location.person) = IDENTITY(acp_geog_agg_location_history.personLocation)',
'acp_geog_agg_address_person_location.validFrom < :acp_geog_aggregator_date',
$qb->expr()->orX(
'acp_geog_agg_address_person_location.validTo > :acp_geog_aggregator_date',
$qb->expr()->isNull('acp_geog_agg_address_person_location.validTo')
)
)
);
$qb->setParameter('acp_geog_aggregator_date', $data['date_calc']);
}
// we finally find an address
if (!in_array('acp_geog_agg_address', $qb->getAllAliases(), true)) {
$qb->leftJoin(
Address::class,
'acp_geog_agg_address',
Join::WITH,
'COALESCE(IDENTITY(acp_geog_agg_address_person_location.address), IDENTITY(acp_geog_agg_location_history.addressLocation)) = acp_geog_agg_address.id'
);
}
// and we do a join with units
$qb->leftJoin(
GeographicalUnit::class,
'acp_geog_units',
Join::WITH,
'ST_CONTAINS(acp_geog_units.geom, acp_geog_agg_address.point) = TRUE'
);
$qb->andWhere($qb->expr()->eq('acp_geog_units.layer', ':acp_geog_unit_layer'));
$qb->setParameter('acp_geog_unit_layer', $data['level']);
// we add group by
$qb
->addSelect('acp_geog_units.unitName AS acp_geog_agg_unitname')
->addSelect('acp_geog_units.unitRefId AS acp_geog_agg_unitrefid')
->addGroupBy('acp_geog_agg_unitname')
->addGroupBy('acp_geog_agg_unitrefid')
;
} }
/** /**
@ -93,4 +192,4 @@ final class GeographicalUnitStatAggregator implements AggregatorInterface
{ {
return Declarations::ACP_TYPE; return Declarations::ACP_TYPE;
} }
} }

View File

@ -456,7 +456,12 @@ Group by step: Grouper les parcours par statut du parcours
Filter by geographical unit: Filtrer les parcours par zone géographique Filter by geographical unit: Filtrer les parcours par zone géographique
Group by geographical unit: Grouper les parcours par zone géographique Group by geographical unit: Grouper les parcours par zone géographique
Compute geographical location at date: Date de calcul de la localisation géographique
Geographical unit: Zone géographique Geographical unit: Zone géographique
acp_geog_agg_unitname: Zone géographique
acp_geog_agg_unitrefid: Clé de la zone géographique
Geographical layer: Couche géographique
Select a geographical layer: Choisir une couche géographique
Filter by socialaction: Filtrer les parcours par action d'accompagnement Filter by socialaction: Filtrer les parcours par action d'accompagnement
Accepted socialactions: Actions d'accompagnement Accepted socialactions: Actions d'accompagnement