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;
/**
* @ORM\ManyToOne(targetEntity=GeographicalUnitLayer::class)
* @ORM\ManyToOne(targetEntity=GeographicalUnitLayer::class, inversedBy="units")
*/
private ?GeographicalUnitLayer $layer;

View File

@ -2,6 +2,8 @@
namespace Chill\MainBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
@ -29,6 +31,16 @@ class GeographicalUnitLayer
*/
private string $refId = '';
/**
* @ORM\OneToMany(targetEntity=GeographicalUnit::class, mappedBy="layer")
*/
private Collection $units;
public function __construct()
{
$this->units = new ArrayCollection();
}
/**
* @return int|null
*/
@ -54,4 +66,20 @@ class GeographicalUnitLayer
$this->name = $name;
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;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Entity\GeographicalUnit;
use Chill\MainBundle\Entity\GeographicalUnitLayer;
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 Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
final class GeographicalUnitStatAggregator implements AggregatorInterface
{
/*
private EntityRepository $repository;
private GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
EntityManagerInterface $em
GeographicalUnitLayerRepositoryInterface $geographicalUnitLayerRepository,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->repository = $em->getRepository(...::class);
$this->geographicalUnitLayerRepository = $geographicalUnitLayerRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
*/
/**
* @inheritDoc
*/
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ('_header' === $value) {
return 'Geographical unit';
}
switch ($key) {
case 'acp_geog_agg_unitname':
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
{
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)
{
// 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)
{
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');
if (!empty($groupBy)) {
$qb->addGroupBy('geographicalunitstat_aggregator');
} else {
$qb->groupBy('geographicalunitstat_aggregator');
$qb->setParameter('acp_geog_aggregator_date', $data['date_calc']);
}
// 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;
}
}
}

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
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
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
Accepted socialactions: Actions d'accompagnement