Merge branch '111_exports' into social_action_exports

This commit is contained in:
Julie Lenaerts 2022-08-11 14:08:33 +02:00
commit 12cae472d6
14 changed files with 869 additions and 4 deletions

View File

@ -101,6 +101,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
$loader->load('services/exports_accompanying_course.yaml');
$loader->load('services/exports_social_actions.yaml');
$loader->load('services/exports_evaluation.yaml');
$loader->load('services/exports_household.yaml');
}
/**

View File

@ -0,0 +1,98 @@
<?php
namespace Chill\PersonBundle\Export\Aggregator\EvaluationAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Repository\SocialWork\EvaluationRepository;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class EvaluationTypeAggregator implements AggregatorInterface
{
private EvaluationRepository $evaluationRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
EvaluationRepository $evaluationRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->evaluationRepository = $evaluationRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
/**
* @inheritDoc
*/
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ($value === '_header') {
return 'Evaluation type';
}
$ev = $this->evaluationRepository->find($value);
return $this->translatableStringHelper->localize($ev->getTitle());
};
}
/**
* @inheritDoc
*/
public function getQueryKeys($data): array
{
return ['evaluationtype_aggregator'];
}
/**
* @inheritDoc
*/
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
/**
* @inheritDoc
*/
public function getTitle(): string
{
return 'Group by evaluation type';
}
/**
* @inheritDoc
*/
public function addRole()
{
return null;
}
/**
* @inheritDoc
*/
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->join('eval.evaluation', 'ev');
$qb->addSelect('ev.id AS evaluationtype_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('evaluationtype_aggregator');
} else {
$qb->groupBy('evaluationtype_aggregator');
}
}
/**
* @inheritDoc
*/
public function applyOn(): string
{
return Declarations::EVAL_TYPE;
}
}

View File

@ -0,0 +1,108 @@
<?php
namespace Chill\PersonBundle\Export\Aggregator\HouseholdAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class ChildrenNumberAggregator implements AggregatorInterface
{
private TranslatorInterface $translator;
public function __construct(
TranslatorInterface $translator
) {
$this->translator = $translator;
}
/**
* @inheritDoc
*/
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ($value === '_header') {
return 'Number of children';
}
return $this->translator->trans(
'household_composition.numberOfChildren children in household', [
'numberOfChildren' => $value
]);
};
}
/**
* @inheritDoc
*/
public function getQueryKeys($data): array
{
return ['childrennumber_aggregator'];
}
/**
* @inheritDoc
*/
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('on_date', ChillDateType::class, [
'data' => new \DateTime('now'),
]);
}
/**
* @inheritDoc
*/
public function getTitle(): string
{
return 'Group by number of children';
}
/**
* @inheritDoc
*/
public function addRole()
{
return null;
}
/**
* @inheritDoc
*/
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->addSelect('composition.numberOfChildren AS childrennumber_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('childrennumber_aggregator');
} else {
$qb->groupBy('childrennumber_aggregator');
}
// add date in where clause
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->andX(
$qb->expr()->lte('composition.startDate', ':ondate'),
$qb->expr()->orX(
$qb->expr()->gt('composition.endDate', ':ondate'),
$qb->expr()->isNull('composition.endDate')
)
);
}
/**
* @inheritDoc
*/
public function applyOn(): string
{
return Declarations::HOUSEHOLD_TYPE;
}
}

View File

@ -0,0 +1,124 @@
<?php
namespace Chill\PersonBundle\Export\Aggregator\HouseholdAggregators;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Repository\Household\HouseholdCompositionTypeRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class CompositionAggregator implements AggregatorInterface
{
private HouseholdCompositionTypeRepository $typeRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
HouseholdCompositionTypeRepository $typeRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->typeRepository = $typeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
/**
* @inheritDoc
*/
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ($value === '_header') {
return 'Composition';
}
$c = $this->typeRepository->find($value);
return $this->translatableStringHelper->localize(
$c->getLabel()
);
};
}
/**
* @inheritDoc
*/
public function getQueryKeys($data): array
{
return ['composition_aggregator'];
}
/**
* @inheritDoc
*/
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('on_date', ChillDateType::class, [
'data' => new \DateTime('now'),
]);
}
/**
* @inheritDoc
*/
public function getTitle(): string
{
return 'Group by composition';
}
/**
* @inheritDoc
*/
public function addRole()
{
return null;
}
/**
* @inheritDoc
*/
public function alterQuery(QueryBuilder $qb, $data)
{
$qb->addSelect('IDENTITY(composition.householdCompositionType) AS composition_aggregator');
$groupBy = $qb->getDQLPart('groupBy');
if (!empty($groupBy)) {
$qb->addGroupBy('composition_aggregator');
} else {
$qb->groupBy('composition_aggregator');
}
// add date in where clause
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->andX(
$qb->expr()->lte('composition.startDate', ':ondate'),
$qb->expr()->orX(
$qb->expr()->gt('composition.endDate', ':ondate'),
$qb->expr()->isNull('composition.endDate')
)
);
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE);
}
/**
* @inheritDoc
*/
public function applyOn(): string
{
return Declarations::HOUSEHOLD_TYPE;
}
}

View File

@ -27,4 +27,6 @@ abstract class Declarations
public const SOCIAL_WORK_ACTION_TYPE = 'social_actions';
public const EVAL_TYPE = 'evaluation';
public const HOUSEHOLD_TYPE = 'household';
}

View File

@ -5,7 +5,7 @@ namespace Chill\PersonBundle\Export\Export;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Doctrine\ORM\EntityManagerInterface;
@ -22,7 +22,7 @@ class CountEvaluation implements ExportInterface, GroupedExportInterface
public function __construct(
EntityManagerInterface $em
) {
$this->evaluationRepository = $em->getRepository(Evaluation::class);
$this->evaluationRepository = $em->getRepository(AccompanyingPeriodWorkEvaluation::class);
}
/**

View File

@ -0,0 +1,141 @@
<?php
namespace Chill\PersonBundle\Export\Export;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
class CountHousehold implements ExportInterface, GroupedExportInterface
{
private EntityRepository $acpRepository;
public function __construct(
EntityManagerInterface $em
) {
$this->acpRepository = $em->getRepository(AccompanyingPeriod::class);
}
/**
* @inheritDoc
*/
public function buildForm(FormBuilderInterface $builder)
{
// TODO: Implement buildForm() method.
}
/**
* @inheritDoc
*/
public function getTitle(): string
{
return 'Count households';
}
/**
* @inheritDoc
*/
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
}
/**
* @inheritDoc
*/
public function getDescription(): string
{
return 'Count household by various parameters.';
}
/**
* @inheritDoc
*/
public function getLabels($key, array $values, $data)
{
if ('export_result' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
}
$labels = array_combine($values, $values);
$labels['_header'] = $this->getTitle();
return static function ($value) use ($labels) {
return $labels[$value];
};
}
/**
* @inheritDoc
*/
public function getQueryKeys($data): array
{
return ['export_result'];
}
/**
* @inheritDoc
*/
public function getResult($qb, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
/**
* @inheritDoc
*/
public function getType(): string
{
return Declarations::HOUSEHOLD_TYPE;
}
/**
* @inheritDoc
*/
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$qb = $this->acpRepository->createQueryBuilder('acp')
->join('acp.participations', 'acppart')
->join('acppart.person', 'person')
->join('person.householdParticipations', 'householdmember')
->join('householdmember.household', 'household')
->join('household.compositions', 'composition')
;
$qb->select('COUNT(DISTINCT householdmember.household) AS export_result');
return $qb;
}
/**
* @inheritDoc
*/
public function requiredRole()
{
// TODO HouseholdVoter::STATS !??
return new Role(AccompanyingPeriodVoter::STATS);
}
/**
* @inheritDoc
*/
public function supportsModifiers(): array
{
return [
Declarations::HOUSEHOLD_TYPE,
];
}
public function getGroup(): string
{
return 'Exports of households';
}
}

View File

@ -11,7 +11,6 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
use Chill\CustomFieldsBundle\Form\Type\ChoicesType;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Export\FilterInterface;

View File

@ -0,0 +1,96 @@
<?php
namespace Chill\PersonBundle\Export\Filter\EvaluationFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
final class EvaluationTypeFilter implements FilterInterface
{
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
TranslatableStringHelper $translatableStringHelper
) {
$this->translatableStringHelper = $translatableStringHelper;
}
/**
* @inheritDoc
*/
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_evaluationtype', EntityType::class, [
'class' => Evaluation::class,
'choice_label' => function (Evaluation $ev): string {
return $this->translatableStringHelper->localize($ev->getTitle());
},
'multiple' => true,
'expanded' => true
]);
}
/**
* @inheritDoc
*/
public function getTitle(): string
{
return 'Filter by evaluation type';
}
/**
* @inheritDoc
*/
public function describeAction($data, $format = 'string'): array
{
$evals = [];
foreach ($data['accepted_evaluationtype'] as $ev) {
$evals[] = $this->translatableStringHelper->localize($ev->getTitle());
}
return ['Filtered by evaluation type: only %evals%', [
'%evals%' => implode(", ou ", $evals)
]];
}
/**
* @inheritDoc
*/
public function addRole()
{
return null;
}
/**
* @inheritDoc
*/
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('eval.evaluation', ':evaluationtype');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('evaluationtype', $data['accepted_evaluationtype']);
}
/**
* @inheritDoc
*/
public function applyOn(): string
{
return Declarations::EVAL_TYPE;
}
}

View File

@ -0,0 +1,103 @@
<?php
namespace Chill\PersonBundle\Export\Filter\EvaluationFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class MaxDateFilter implements FilterInterface
{
private const MAXDATE_CHOICES = [
'is specified' => true,
'is not specified' => false,
];
private TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
/**
* @inheritDoc
*/
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('maxdate', ChoiceType::class, [
'choices' => self::MAXDATE_CHOICES,
'multiple' => false,
'expanded' => true
]);
}
/**
* @inheritDoc
*/
public function getTitle(): string
{
return 'Filter by maxdate';
}
/**
* @inheritDoc
*/
public function describeAction($data, $format = 'string'): array
{
foreach (self::MAXDATE_CHOICES as $k => $v) {
if ($v === $data['maxdate']) {
$choice = $k;
}
}
return ['Filtered by maxdate: only %choice%', [
'%choice%' => $this->translator->trans($choice)
]];
}
/**
* @inheritDoc
*/
public function addRole()
{
return null;
}
/**
* @inheritDoc
*/
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
if ($data['maxdate'] === true) {
$clause = $qb->expr()->isNotNull('eval.maxDate');
} else {
$clause = $qb->expr()->isNull('eval.maxDate');
}
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
dump($data['maxdate']);
}
/**
* @inheritDoc
*/
public function applyOn(): string
{
return Declarations::EVAL_TYPE;
}
}

View File

@ -0,0 +1,119 @@
<?php
namespace Chill\PersonBundle\Export\Filter\HouseholdFilters;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\Household\HouseholdCompositionType;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
class CompositionFilter implements FilterInterface
{
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
TranslatableStringHelper $translatableStringHelper
) {
$this->translatableStringHelper = $translatableStringHelper;
}
/**
* @inheritDoc
*/
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('accepted_composition', EntityType::class, [
'class' => HouseholdCompositionType::class,
'choice_label' => function (HouseholdCompositionType $type) {
return $this->translatableStringHelper->localize(
$type->getLabel()
);
},
'multiple' => true,
'expanded' => true,
])
->add('on_date', ChillDateType::class, [
'data' => new \DateTime('now'),
])
;
}
/**
* @inheritDoc
*/
public function getTitle(): string
{
return 'Filter by composition';
}
/**
* @inheritDoc
*/
public function describeAction($data, $format = 'string'): array
{
$compositions = [];
foreach ($data['accepted_composition'] as $c) {
$compositions[] = $this->translatableStringHelper->localize(
$c->getLabel()
);
}
return ['Filtered by composition: only %compositions% on %ondate%', [
'%compositions%' => implode(", ou ", $compositions),
'%ondate%' => $data['on_date']->format('d-m-Y')
]];
}
/**
* @inheritDoc
*/
public function addRole()
{
return null;
}
/**
* @inheritDoc
*/
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->andX(
$qb->expr()->in('composition.householdCompositionType', ':compositions'),
$qb->expr()->andX(
$qb->expr()->lte('composition.startDate', ':ondate'),
$qb->expr()->orX(
$qb->expr()->gt('composition.endDate', ':ondate'),
$qb->expr()->isNull('composition.endDate')
)
)
);
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('compositions', $data['accepted_composition']);
$qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE);
}
/**
* @inheritDoc
*/
public function applyOn(): string
{
return Declarations::HOUSEHOLD_TYPE;
}
}

View File

@ -9,5 +9,25 @@ services:
- { name: chill.export, alias: count_evaluation }
## Filters
## Aggregators
chill.person.export.filter_evaluationtype:
class: Chill\PersonBundle\Export\Filter\EvaluationFilters\EvaluationTypeFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: accompanyingcourse_evaluationtype_filter }
chill.person.export.filter_maxdate:
class: Chill\PersonBundle\Export\Filter\EvaluationFilters\MaxDateFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: accompanyingcourse_maxdate_filter }
## Aggregators
chill.person.export.aggregator_evaluationtype:
class: Chill\PersonBundle\Export\Aggregator\EvaluationAggregators\EvaluationTypeAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: accompanyingcourse_evaluationtype_aggregator }

View File

@ -0,0 +1,32 @@
services:
## Indicators
chill.person.export.count_household:
class: Chill\PersonBundle\Export\Export\CountHousehold
autowire: true
autoconfigure: true
tags:
- { name: chill.export, alias: count_household }
## Filters
chill.person.export.filter_household_composition:
class: Chill\PersonBundle\Export\Filter\HouseholdFilters\CompositionFilter
autowire: true
autoconfigure: true
tags:
- { name: chill.export_filter, alias: household_composition_filter }
## Aggregators
chill.person.export.aggregator_household_composition:
class: Chill\PersonBundle\Export\Aggregator\HouseholdAggregators\CompositionAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: household_composition_aggregator }
chill.person.export.aggregator_household_childrennumber:
class: Chill\PersonBundle\Export\Aggregator\HouseholdAggregators\ChildrenNumberAggregator
autowire: true
autoconfigure: true
tags:
- { name: chill.export_aggregator, alias: household_childrennumber_aggregator }

View File

@ -355,6 +355,10 @@ Exports of evaluations: Exports des évaluations
Count evaluations: Nombre d'évaluations
Count evaluation by various parameters.: Compte le nombre d'évaluations selon différents filtres.
Exports of households: Exports des ménages
Count households: Nombre de ménages
Count household by various parameters.: Compte le nombre de ménages selon différents filtres.
## filters
Filter by person gender: Filtrer par genre de la personne
Accepted genders: Genres acceptés
@ -514,7 +518,6 @@ On date: Actifs à cette date
Filtered by active at least one day between dates: Filtrer les parcours actifs au moins un jour dans la période
"Filtered by actives courses: at least one day between %datefrom% and %dateto%": "Filtrer les parcours actifs: au moins un jour entre le %datefrom% et le %dateto%"
Filtered by referrers: Filtrer par référent
Accepted referrers: Référents
"Filtered by referrer: only %referrers%": "Filtré par référent: uniquement %referrers%"
@ -525,6 +528,25 @@ Date from: Date de début
Date to: Date de fin
"Filtered by opening dates: between %datefrom% and %dateto%": "Filtrer les parcours ouverts entre deux dates: entre le %datefrom% et le %dateto%"
Filter by evaluation type: Filtrer par type d'évaluation
Accepted evaluationtype: Évaluations
"Filtered by evaluation type: only %evals%": "Filtré par type d'évaluation: uniquement %evals%"
Group by evaluation type: Grouper par type d'évaluation
Evaluation type: Type d'évaluation
Filter by maxdate: Filtrer par date d'échéance
Maxdate: ''
is specified: La date d'échéance est spécifiée
is not specified: La date d'échéance n'est pas spécifiée
"Filtered by maxdate: only %choice%": "Filtré par date d'échéance: uniquement si %choice%"
Filter by composition: Filtrer par composition familiale
Accepted composition: Composition familiale
"Filtered by composition: only %compositions% on %ondate%": "Filtré par composition familiale: uniquement %compositions%, en date du %ondate%"
Group by composition: Grouper par composition familiale
Group by number of children: Grouper par nombre d'enfants
## aggregators
Group people by nationality: Grouper les personnes par nationalités
Group by level: Grouper par niveau