From 955d4a9e7a5b1c397a701b6c4a1abab114e5e35b Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 10 Aug 2022 10:21:05 +0200 Subject: [PATCH 1/7] exports: add new EvaluationType Filter and Aggregator --- .../EvaluationTypeAggregator.php | 98 +++++++++++++++++++ .../Export/Export/CountEvaluation.php | 4 +- .../EvaluationTypeFilter.php | 96 ++++++++++++++++++ .../config/services/exports_evaluation.yaml | 13 +++ .../translations/messages.fr.yml | 6 ++ 5 files changed, 215 insertions(+), 2 deletions(-) create mode 100644 src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregator.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/EvaluationTypeFilter.php diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregator.php new file mode 100644 index 000000000..d52c410e5 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregator.php @@ -0,0 +1,98 @@ +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; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php b/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php index 985a69df6..22bd07b0c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php @@ -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); } /** diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/EvaluationTypeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/EvaluationTypeFilter.php new file mode 100644 index 000000000..6df5e38bf --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/EvaluationTypeFilter.php @@ -0,0 +1,96 @@ +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; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_evaluation.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_evaluation.yaml index 03cd37ee3..92d9cfd3d 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_evaluation.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_evaluation.yaml @@ -9,5 +9,18 @@ services: - { name: chill.export, alias: count_evaluation } ## Filters + 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 } + ## 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 } \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 750bbf0ba..7ef7cc107 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -515,6 +515,12 @@ 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 + ## aggregators Group people by nationality: Grouper les personnes par nationalités Group by level: Grouper par niveau From 8efbf02f64b4016c8a76ec72a18a9073a052d700 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 10 Aug 2022 10:57:32 +0200 Subject: [PATCH 2/7] exports: add new MaxDate Filter for evaluation --- .../CurrentUserScopeFilter.php | 1 - .../EvaluationFilters/MaxDateFilter.php | 103 ++++++++++++++++++ .../config/services/exports_evaluation.yaml | 7 ++ .../translations/messages.fr.yml | 6 + 4 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/MaxDateFilter.php diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilter.php index b41347026..e6c56f11e 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilter.php @@ -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; diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/MaxDateFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/MaxDateFilter.php new file mode 100644 index 000000000..de84d5877 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/MaxDateFilter.php @@ -0,0 +1,103 @@ + 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; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_evaluation.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_evaluation.yaml index 92d9cfd3d..e21e0b6c1 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_evaluation.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_evaluation.yaml @@ -16,6 +16,13 @@ services: 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 diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 7ef7cc107..af21991d3 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -521,6 +521,12 @@ Accepted evaluationtype: Évaluations 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%" + ## aggregators Group people by nationality: Grouper les personnes par nationalités Group by level: Grouper par niveau From 3f4d4497af46148f8919f23cc37928a024715061 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 10 Aug 2022 14:03:47 +0200 Subject: [PATCH 3/7] exports: add new countHousehold export --- .../ChillPersonExtension.php | 1 + .../ChillPersonBundle/Export/Declarations.php | 2 + .../Export/Export/CountHousehold.php | 139 ++++++++++++++++++ .../config/services/exports_household.yaml | 13 ++ .../translations/messages.fr.yml | 4 + 5 files changed, 159 insertions(+) create mode 100644 src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php create mode 100644 src/Bundle/ChillPersonBundle/config/services/exports_household.yaml diff --git a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php index bf73b4f81..efbb972c1 100644 --- a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php +++ b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php @@ -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'); } /** diff --git a/src/Bundle/ChillPersonBundle/Export/Declarations.php b/src/Bundle/ChillPersonBundle/Export/Declarations.php index 5a88d988b..b8e2cd69c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Declarations.php +++ b/src/Bundle/ChillPersonBundle/Export/Declarations.php @@ -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'; } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php new file mode 100644 index 000000000..0fa708bf0 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php @@ -0,0 +1,139 @@ +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') + ; + + $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'; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_household.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_household.yaml new file mode 100644 index 000000000..45d70f6e4 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/config/services/exports_household.yaml @@ -0,0 +1,13 @@ +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 + + ## Aggregators diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index af21991d3..2f783c1e7 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -354,6 +354,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 From 3463ff8e2e5bba73c51cd0293930296f61bdcb7b Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 10 Aug 2022 16:25:38 +0200 Subject: [PATCH 4/7] exports: add Composition Filter and Aggregator in Household exports --- .../CompositionAggregator.php | 104 +++++++++++++++++ .../Export/Export/CountHousehold.php | 1 + .../HouseholdFilters/CompositionFilter.php | 106 ++++++++++++++++++ .../config/services/exports_household.yaml | 12 ++ .../translations/messages.fr.yml | 6 +- 5 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php new file mode 100644 index 000000000..5718f990d --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php @@ -0,0 +1,104 @@ +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) + { + // TODO: Implement buildForm() method. + } + + /** + * @inheritDoc + */ + public function getTitle(): string + { + return 'Group by composition'; + } + + /** + * @inheritDoc + */ + public function addRole() + { + return null; + } + + /** + * @inheritDoc + */ + public function alterQuery(QueryBuilder $qb, $data) + { + $qb + ->join('household.compositions', 'composition') + //->join('composition.householdCompositionType', 'type') + ; + + $qb->addSelect('IDENTITY(composition.householdCompositionType) AS composition_aggregator'); + + $groupBy = $qb->getDQLPart('groupBy'); + + if (!empty($groupBy)) { + $qb->addGroupBy('composition_aggregator'); + } else { + $qb->groupBy('composition_aggregator'); + } + } + + /** + * @inheritDoc + */ + public function applyOn(): string + { + return Declarations::HOUSEHOLD_TYPE; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php index 0fa708bf0..5bc333e2a 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php @@ -106,6 +106,7 @@ class CountHousehold implements ExportInterface, GroupedExportInterface ->join('acp.participations', 'acppart') ->join('acppart.person', 'person') ->join('person.householdParticipations', 'householdmember') + ->join('householdmember.household', 'household') ; $qb->select('COUNT(DISTINCT householdmember.household) AS export_result'); diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php new file mode 100644 index 000000000..132b878fd --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php @@ -0,0 +1,106 @@ +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, + ]); + } + + /** + * @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%', [ + '%compositions%' => implode(", ou ", $compositions) + ]]; + } + + /** + * @inheritDoc + */ + public function addRole() + { + return null; + } + + /** + * @inheritDoc + */ + public function alterQuery(QueryBuilder $qb, $data) + { + $qb + ->join('household.compositions', 'composition') + //->join('composition.householdCompositionType', 'type') + ; + + $where = $qb->getDQLPart('where'); + + $clause = $qb->expr()->in('composition.householdCompositionType', ':compositions'); + + if ($where instanceof Andx) { + $where->add($clause); + } else { + $where = $qb->expr()->andX($clause); + } + + $qb->add('where', $where); + $qb->setParameter('compositions', $data['accepted_composition']); + } + + /** + * @inheritDoc + */ + public function applyOn(): string + { + return Declarations::HOUSEHOLD_TYPE; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_household.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_household.yaml index 45d70f6e4..b59537717 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_household.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_household.yaml @@ -9,5 +9,17 @@ services: - { 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 } diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 2f783c1e7..a19150477 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -508,7 +508,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%" @@ -531,6 +530,11 @@ 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%": "Filtré par composition familiale: uniquement %compositions%" +Group by composition: Grouper par composition familiale + ## aggregators Group people by nationality: Grouper les personnes par nationalités Group by level: Grouper par niveau From c693dfde66219db20f3d895883dcabacbb237495 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 10 Aug 2022 16:33:05 +0200 Subject: [PATCH 5/7] a same join() in filter and aggregator make error if we combine them --- .../HouseholdAggregators/CompositionAggregator.php | 5 ----- .../ChillPersonBundle/Export/Export/CountHousehold.php | 1 + .../Export/Filter/HouseholdFilters/CompositionFilter.php | 5 ----- 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php index 5718f990d..2a7c89ee3 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php @@ -78,11 +78,6 @@ class CompositionAggregator implements AggregatorInterface */ public function alterQuery(QueryBuilder $qb, $data) { - $qb - ->join('household.compositions', 'composition') - //->join('composition.householdCompositionType', 'type') - ; - $qb->addSelect('IDENTITY(composition.householdCompositionType) AS composition_aggregator'); $groupBy = $qb->getDQLPart('groupBy'); diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php index 5bc333e2a..c9b2b541a 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php @@ -107,6 +107,7 @@ class CountHousehold implements ExportInterface, GroupedExportInterface ->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'); diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php index 132b878fd..ae4427e6d 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php @@ -77,11 +77,6 @@ class CompositionFilter implements FilterInterface */ public function alterQuery(QueryBuilder $qb, $data) { - $qb - ->join('household.compositions', 'composition') - //->join('composition.householdCompositionType', 'type') - ; - $where = $qb->getDQLPart('where'); $clause = $qb->expr()->in('composition.householdCompositionType', ':compositions'); From bc5d610f8065c96dfc548e25f111fac591b710e6 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 10 Aug 2022 16:50:59 +0200 Subject: [PATCH 6/7] exports: add on_date parameter on Composition Filter/Aggregator --- .../CompositionAggregator.php | 27 +++++++++++- .../HouseholdFilters/CompositionFilter.php | 44 +++++++++++++------ .../translations/messages.fr.yml | 2 +- 3 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php index 2a7c89ee3..3e292ceec 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php @@ -3,9 +3,12 @@ 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; @@ -54,7 +57,9 @@ class CompositionAggregator implements AggregatorInterface */ public function buildForm(FormBuilderInterface $builder) { - // TODO: Implement buildForm() method. + $builder->add('on_date', ChillDateType::class, [ + 'data' => new \DateTime('now'), + ]); } /** @@ -87,6 +92,26 @@ class CompositionAggregator implements AggregatorInterface } 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); } /** diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php index ae4427e6d..f8a36e4b9 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php @@ -3,9 +3,11 @@ 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; @@ -26,16 +28,21 @@ class CompositionFilter implements FilterInterface */ 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, - ]); + $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'), + ]) + ; } /** @@ -59,8 +66,9 @@ class CompositionFilter implements FilterInterface ); } - return ['Filtered by composition: only %compositions%', [ - '%compositions%' => implode(", ou ", $compositions) + return ['Filtered by composition: only %compositions% on %ondate%', [ + '%compositions%' => implode(", ou ", $compositions), + '%ondate%' => $data['on_date']->format('d-m-Y') ]]; } @@ -79,7 +87,16 @@ class CompositionFilter implements FilterInterface { $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->in('composition.householdCompositionType', ':compositions'); + $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); @@ -89,6 +106,7 @@ class CompositionFilter implements FilterInterface $qb->add('where', $where); $qb->setParameter('compositions', $data['accepted_composition']); + $qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE); } /** diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index a19150477..f46bb1750 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -532,7 +532,7 @@ is not specified: La date d'échéance n'est pas spécifiée Filter by composition: Filtrer par composition familiale Accepted composition: Composition familiale -"Filtered by composition: only %compositions%": "Filtré par composition familiale: uniquement %compositions%" +"Filtered by composition: only %compositions% on %ondate%": "Filtré par composition familiale: uniquement %compositions%, en date du %ondate%" Group by composition: Grouper par composition familiale ## aggregators From 18c17818f61c81642d540448cadf1966dd5482ce Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 10 Aug 2022 17:17:29 +0200 Subject: [PATCH 7/7] exports: add ChildrenNumber Aggregator --- .../ChildrenNumberAggregator.php | 108 ++++++++++++++++++ .../config/services/exports_household.yaml | 7 ++ .../translations/messages.fr.yml | 2 + 3 files changed, 117 insertions(+) create mode 100644 src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregator.php diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregator.php new file mode 100644 index 000000000..c629fe0bf --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregator.php @@ -0,0 +1,108 @@ +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; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_household.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_household.yaml index b59537717..0b8c5f1ce 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_household.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_household.yaml @@ -23,3 +23,10 @@ services: 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 } diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index f46bb1750..3f895e0c4 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -535,6 +535,8 @@ 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