From 648ec68cfb6358eba6a6328572f0cf8a396f7521 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Thu, 1 Sep 2022 12:28:54 +0200 Subject: [PATCH 001/120] export SocialWorkTypeFilter: create dynamic Form Action-goal-result - initiate new Vue component - cleaning buildForm in Filter - add HiddenType with ModelTransformer - use ObjectToIdTransformer in HiddenType fields - setting mount() and teleport - list fetch url in api.js - html/bootstrap form structure - add Multiselect actionType - i18n import - add multiselect for goals and results - add and remove options in multiselect (wip) - fix basic vue add/remove element from data store - vue cleaning - add ids in value hiddenFields - adapt Filter in AlterQuery (wip) - improve code lisibility --- .../public/vuejs/FormActionGoalResult/App.vue | 295 ++++++++++++++++++ .../public/vuejs/FormActionGoalResult/api.js | 41 +++ .../vuejs/FormActionGoalResult/index.js | 13 + .../Resources/views/Export/new.html.twig | 3 + .../ChillMainBundle/chill.webpack.config.js | 1 + .../SocialWorkTypeFilter.php | 143 +++++---- 6 files changed, 442 insertions(+), 54 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue create mode 100644 src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/api.js create mode 100644 src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/index.js diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue new file mode 100644 index 000000000..39a7c023a --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue @@ -0,0 +1,295 @@ + + + + + \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/api.js b/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/api.js new file mode 100644 index 000000000..afcece4f4 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/api.js @@ -0,0 +1,41 @@ +import { fetchResults } from 'ChillMainAssets/lib/api/apiMethods'; + +const getSocialActions = () => fetchResults( + '/api/1.0/person/social/social-action.json', { + item_per_page: 200 + } +); + +const getGoalByAction = (id) => { + let url = `/api/1.0/person/social-work/goal/by-social-action/${id}.json`; + return fetch(url) + .then(response => { + if (response.ok) { return response.json(); } + throw Error('Error with request resource response'); + }); +}; + +const getResultByAction = (id) => { + let url = `/api/1.0/person/social-work/result/by-social-action/${id}.json`; + return fetch(url) + .then(response => { + if (response.ok) { return response.json(); } + throw Error('Error with request resource response'); + }); +}; + +const getResultByGoal = (id) => { + let url = `/api/1.0/person/social-work/result/by-goal/${id}.json`; + return fetch(url) + .then(response => { + if (response.ok) { return response.json(); } + throw Error('Error with request resource response'); + }); +}; + +export { + getSocialActions, + getGoalByAction, + getResultByAction, + getResultByGoal, +} \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/index.js b/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/index.js new file mode 100644 index 000000000..d838fb0be --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/index.js @@ -0,0 +1,13 @@ +import { createApp } from "vue"; +import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n'; +import App from './App.vue'; + +const i18n = _createI18n({}); + +const app = createApp({ + template: ``, +}) +.use(i18n) +.component('app', App) +.mount('#export_export') +; \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig index 8aac6c253..a8b5e8ea8 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig @@ -22,6 +22,9 @@ {% block js %} {{ encore_entry_script_tags('page_export') }} + {% if export_alias == 'count_social_work_actions' %} + {{ encore_entry_script_tags('vue_form_action_goal_result') }} + {% endif %} {% endblock js %} {% block content %} diff --git a/src/Bundle/ChillMainBundle/chill.webpack.config.js b/src/Bundle/ChillMainBundle/chill.webpack.config.js index 628f04ba5..c4c445d31 100644 --- a/src/Bundle/ChillMainBundle/chill.webpack.config.js +++ b/src/Bundle/ChillMainBundle/chill.webpack.config.js @@ -74,4 +74,5 @@ module.exports = function(encore, entries) // Vue entrypoints encore.addEntry('vue_address', __dirname + '/Resources/public/vuejs/Address/index.js'); encore.addEntry('vue_onthefly', __dirname + '/Resources/public/vuejs/OnTheFly/index.js'); + encore.addEntry('vue_form_action_goal_result', __dirname + '/Resources/public/vuejs/FormActionGoalResult/index.js'); }; diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php index 3e5889cb0..ac584ea40 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php @@ -1,27 +1,22 @@ socialActionRender = $socialActionRender; $this->translatableStringHelper = $translatableStringHelper; - $this->socialActionRepository = $socialActionRepository; + $this->em = $em; } public function buildForm(FormBuilderInterface $builder) { - $socialActions = $this->socialActionRepository->findAll(); - - $builder->add('actionType', ChoiceType::class, [ - 'choices' => $socialActions, - 'choice_label' => function (SocialAction $sa) { - return $this->socialActionRender->renderString($sa, []); - }, - 'multiple' => true, - 'expanded' => true - ]); - - /* - $refreshGoals = function (FormInterface $form, SocialAction $actionType = null) { - - $goals = null === $actionType ? [] : $actionType->getGoals(); - - $form->add('goal', ChoiceType::class, [ - 'placeholder' => '', - 'choices' => $goals, - 'choice_label' => function (Goal $g) { - return $this->translatableStringHelper->localize($g->getTitle()); + $builder + ->add('actionType', HiddenType::class) + ->get('actionType') + ->addModelTransformer(new CallbackTransformer( + static function (?iterable $actionsAsIterable): string { + if (null === $actionsAsIterable) { return ''; } + $actionIds = []; + foreach ($actionsAsIterable as $value) { + $actionIds[] = $value->getId(); + } + return implode(',', $actionIds); }, - ]); - }; + function (?string $actionsAsString): array { + if (null === $actionsAsString) { return []; } + return array_map( + fn (string $id): ?SocialAction + => $this->socialActionRepository->findOneBy(['id' => (int) $id]), + explode(',', $actionsAsString) + ); + } + )) + ; - $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($refreshGoals) { - $data = $event->getData(); - dump($data); + $builder + ->add('goal', HiddenType::class) + ->get('goal') + ->addModelTransformer(new CallbackTransformer( + static function (?iterable $goalsAsIterable): string { + if (null === $goalsAsIterable) { return ''; } + $goalIds = []; + foreach ($goalsAsIterable as $value) { + $goalIds[] = $value->getId(); + } + return implode(',', $goalIds); + }, + function (?string $goalsAsString): array { + if (null === $goalsAsString) { return []; } + return array_map( + fn (string $id): ?Goal + => $this->socialActionRepository->findOneBy(['id' => (int) $id]), + explode(',', $goalsAsString) + ); + } + )) + ; - $refreshGoals($event->getForm(), $data); - }); + $builder + ->add('result', HiddenType::class) + ->get('result') + ->addModelTransformer(new CallbackTransformer( + static function (?iterable $resultsAsIterable): string { + if (null === $resultsAsIterable) { return ''; } + $resultIds = []; + foreach ($resultsAsIterable as $value) { + $resultIds[] = $value->getId(); + } + return implode(',', $resultIds); + }, + function (?string $resultsAsString): array { + if (null === $resultsAsString) { return []; } + return array_map( + fn (string $id): ?Result + => $this->socialActionRepository->findOneBy(['id' => (int) $id]), + explode(',', $resultsAsString) + ); + } + )) + ; - $builder->get('actionType')->addEventListener( - FormEvents::POST_SUBMIT, - function (FormEvent $event) use ($refreshGoals) { - $actionType = $event->getForm()->getData(); - dump($actionType); - $refreshGoals($event->getForm()->getParent(), $actionType); - } - ); - */ } public function getTitle(): string @@ -122,7 +145,15 @@ class SocialWorkTypeFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->in('r', ':referrers'); + $clause = $qb->expr()->eq('acpw.socialAction',':actionType'); + + /* + if (!empty($data['goal'])) { + $clause + $qb->expr()->in('acpw.goals', ':goals'); + } + $qb->expr()->in('acpw.results', ':results'); + */ if ($where instanceof Andx) { $where->add($clause); @@ -131,7 +162,11 @@ class SocialWorkTypeFilter implements FilterInterface } $qb->add('where', $where); - $qb->setParameter('referrers', $data['referrers']); + $qb->setParameters([ + 'actionType' => $data['actionType'], + 'goal' => $data['goal'], + 'result' => $data['result'], + ]); } public function applyOn(): string From da2d68a45d74bd68e001f5343e01f3656ab54334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 6 Sep 2022 15:22:30 +0200 Subject: [PATCH 002/120] [documentation] new how-to to switch into branch, and add info about gitlab tokens --- docs/source/installation/index.rst | 42 ++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/docs/source/installation/index.rst b/docs/source/installation/index.rst index a962148f7..e3791394d 100644 --- a/docs/source/installation/index.rst +++ b/docs/source/installation/index.rst @@ -104,6 +104,29 @@ The password is always ``password``. Now, read `Operations` below. +Prepare for development +*********************** + +Add a Gitlab token to ensure that you get always the source code: + +1. generate a gitlab token there: https://gitlab.com/oauth/token +2. run this command (in php container, at the app's root): :code:`composer config gitlab-token.gitlab.com ` + +The auth token should appears now in the directory :code:`.composer`: + +... code-block: bash + + $ cat .composer/auth.json + { + "gitlab-token": { + "gitlab.com": "" + } + } + + +See also "how to switch branch and get new dependencies". + + Operations ********** @@ -211,6 +234,25 @@ How to run webpack interactively Executing :code:`bash docker-node.sh` will open a terminal in a node container, with volumes mounted. +How to switch the branch for chill-bundles, and get new dependencies +==================================================================== + +During development, you will switch to new branches for chill-bundles. As long as the dependencies are equals, this does not cause any problem. But sometimes, a new branch introduces a new dependency, and you must download it. + +In order to do that without pain, use those steps: + +0. Ensuire you have a token, set +1. at the app's root, update the `composer.json` to your current branch: + +.. code-block:: json + { + "require": { + "chill-bundles": "dev-@dev" + } +``` + +2. mount into the php container, and run `composer update` + Build the documentation API =========================== From 0f0ec10857f74dc1516a361ae015576421afdaf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 7 Sep 2022 12:18:07 +0200 Subject: [PATCH 003/120] fix rst syntax --- docs/source/installation/index.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/installation/index.rst b/docs/source/installation/index.rst index e3791394d..8d84b795f 100644 --- a/docs/source/installation/index.rst +++ b/docs/source/installation/index.rst @@ -114,7 +114,7 @@ Add a Gitlab token to ensure that you get always the source code: The auth token should appears now in the directory :code:`.composer`: -... code-block: bash +.. code-block: bash $ cat .composer/auth.json { @@ -245,11 +245,11 @@ In order to do that without pain, use those steps: 1. at the app's root, update the `composer.json` to your current branch: .. code-block:: json + { "require": { "chill-bundles": "dev-@dev" } -``` 2. mount into the php container, and run `composer update` From f10ec3991dcd8267ddffa8dde7d345baebd23447 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Thu, 8 Sep 2022 11:04:38 +0200 Subject: [PATCH 004/120] exports: put breadcrumb in an include, add a link to go back to the list --- .../Resources/views/Export/_breadcrumb.html.twig | 6 ++++++ .../Resources/views/Export/download.html.twig | 5 +---- .../ChillMainBundle/Resources/views/Export/new.html.twig | 5 +---- .../Resources/views/Export/new_centers_step.html.twig | 7 ++----- .../Resources/views/Export/new_formatter_step.html.twig | 5 +---- 5 files changed, 11 insertions(+), 17 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Resources/views/Export/_breadcrumb.html.twig diff --git a/src/Bundle/ChillMainBundle/Resources/views/Export/_breadcrumb.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Export/_breadcrumb.html.twig new file mode 100644 index 000000000..1d4a6573c --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/Export/_breadcrumb.html.twig @@ -0,0 +1,6 @@ +
+ + + + {{ export_group|trans }} +
diff --git a/src/Bundle/ChillMainBundle/Resources/views/Export/download.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Export/download.html.twig index fa568d857..84eb85100 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Export/download.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Export/download.html.twig @@ -36,10 +36,7 @@ window.addEventListener("DOMContentLoaded", function(e) { {% block content %}
-
- - {{ export_group|trans }} -
+ {{ include('@ChillMain/Export/_breadcrumb.html.twig') }}

{{ export.title|trans }}

{{ "Download export"|trans }}

diff --git a/src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig index 8aac6c253..7f7efae9a 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig @@ -27,10 +27,7 @@ {% block content %}
-
- - {{ export_group|trans }} -
+ {{ include('@ChillMain/Export/_breadcrumb.html.twig') }}

{{ export.title|trans }}

diff --git a/src/Bundle/ChillMainBundle/Resources/views/Export/new_centers_step.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Export/new_centers_step.html.twig index 4a2d9ab9e..8874744c4 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Export/new_centers_step.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Export/new_centers_step.html.twig @@ -22,11 +22,8 @@ {% block content %}
- -
- - {{ export_group|trans }} -
+ + {{ include('@ChillMain/Export/_breadcrumb.html.twig') }}

{{ export.title|trans }}

diff --git a/src/Bundle/ChillMainBundle/Resources/views/Export/new_formatter_step.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Export/new_formatter_step.html.twig index 443816611..1c3209455 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Export/new_formatter_step.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Export/new_formatter_step.html.twig @@ -23,10 +23,7 @@ {% block content %}
-
- - {{ export_group|trans }} -
+ {{ include('@ChillMain/Export/_breadcrumb.html.twig') }}

{{ export.title|trans }}

From a817b0bf4cf093b6fa26601ab580ad4ebc0697e1 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Thu, 8 Sep 2022 11:31:23 +0200 Subject: [PATCH 005/120] exports: re-enable all modifiers stack with shared filters/aggrs --- .../Export/Export/LinkedToACP/AvgActivityDuration.php | 2 +- .../Export/Export/LinkedToACP/AvgActivityVisitDuration.php | 2 +- .../Export/Export/LinkedToACP/CountActivity.php | 2 +- .../Export/Export/LinkedToACP/SumActivityDuration.php | 2 +- .../Export/Export/LinkedToACP/SumActivityVisitDuration.php | 2 +- .../ChillPersonBundle/Export/Export/CountEvaluation.php | 4 ++-- src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php | 2 +- .../Export/Export/CountPersonWithAccompanyingCourse.php | 2 +- .../Export/Export/CountSocialWorkActions.php | 1 + src/Bundle/ChillPersonBundle/translations/messages.fr.yml | 4 ++-- 10 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php index be20d2b9c..b2346b8fd 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php @@ -101,7 +101,7 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface return [ Declarations::ACTIVITY, Declarations::ACTIVITY_ACP, - //PersonDeclarations::ACP_TYPE, + PersonDeclarations::ACP_TYPE, ]; } } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php index 021c82d72..0195992fe 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php @@ -101,7 +101,7 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac return [ Declarations::ACTIVITY, Declarations::ACTIVITY_ACP, - //PersonDeclarations::ACP_TYPE, + PersonDeclarations::ACP_TYPE, ]; } } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php index 1f8b742dd..e4202c33a 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php @@ -105,7 +105,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface return [ Declarations::ACTIVITY, Declarations::ACTIVITY_ACP, - //PersonDeclarations::ACP_TYPE, + PersonDeclarations::ACP_TYPE, ]; } } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php index 4d9b43034..2e87623ef 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php @@ -104,7 +104,7 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface return [ Declarations::ACTIVITY, Declarations::ACTIVITY_ACP, - //PersonDeclarations::ACP_TYPE, + PersonDeclarations::ACP_TYPE, ]; } } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php index 212b1384f..5bb6d542e 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php @@ -104,7 +104,7 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac return [ Declarations::ACTIVITY, Declarations::ACTIVITY_ACP, - //PersonDeclarations::ACP_TYPE, + PersonDeclarations::ACP_TYPE, ]; } } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php b/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php index 5b2ab65eb..7178a54c8 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php @@ -112,8 +112,8 @@ class CountEvaluation implements ExportInterface, GroupedExportInterface { return [ Declarations::EVAL_TYPE, - //Declarations::ACP_TYPE, - //Declarations::SOCIAL_WORK_ACTION_TYPE, + Declarations::SOCIAL_WORK_ACTION_TYPE, + Declarations::ACP_TYPE, ]; } } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php index f60f66310..18ddbaea1 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php @@ -121,7 +121,7 @@ class CountHousehold implements ExportInterface, GroupedExportInterface { return [ Declarations::HOUSEHOLD_TYPE, - //Declarations::ACP_TYPE + Declarations::ACP_TYPE, ]; } } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php b/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php index 800023fc5..51d85c479 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php @@ -111,8 +111,8 @@ class CountPersonWithAccompanyingCourse implements ExportInterface, GroupedExpor public function supportsModifiers(): array { return [ - Declarations::ACP_TYPE, Declarations::PERSON_TYPE, + Declarations::ACP_TYPE, ]; } } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php b/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php index 17de7e5ae..2ad6e9f1b 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php @@ -110,6 +110,7 @@ class CountSocialWorkActions implements ExportInterface, GroupedExportInterface { return [ Declarations::SOCIAL_WORK_ACTION_TYPE, + Declarations::ACP_TYPE, ]; } } diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index e909f0ab7..70a0cda42 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -456,7 +456,7 @@ Filter by socialaction: Filtrer les parcours par action d'accompagnement Accepted socialactions: Actions d'accompagnement "Filtered by socialactions: only %socialactions%": "Filtré par action d'accompagnement: uniquement %socialactions%" Group by social action: Grouper les parcours par action d'accompagnement -Filter by type of action, objectives and results: "Filtrer par type d'action, objectif et résultat" +Filter by type of action, objectives and results: "Filtrer les actions par type, objectif et résultat" Filter by evaluation: Filtrer les parcours par évaluation Accepted evaluations: Évaluations @@ -576,7 +576,7 @@ Group by composition: Grouper les ménages par composition familiale Group by number of children: Grouper les ménages par nombre d'enfants ## persons aggregators -Group by duration: Grouper par durée du parcours +Group by duration: Grouper les parcours par durée Rounded month duration: Durée en mois (arrondie) current duration: en cours duration 0 month: 0 mois (<15 jours) From 5c2b2105b2c858e7fc6d759b3477a5e28a0c6623 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Thu, 8 Sep 2022 11:47:41 +0200 Subject: [PATCH 006/120] exports: improve formatter twig template (when multiple order rows) --- .../views/Export/new_formatter_step.html.twig | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Resources/views/Export/new_formatter_step.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Export/new_formatter_step.html.twig index 1c3209455..8ecaf6dd8 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Export/new_formatter_step.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Export/new_formatter_step.html.twig @@ -33,19 +33,21 @@

{{ 'Formatter'| trans }}

-
{% if form.children.formatter.children|length == 0 %}

{{ "No options availables. Your report is fully configured."|trans }}

{{ form_widget(form.children.formatter) }} {% else %} - {# we always have to render children, to mark as rendered #} - {% for input in form.children.formatter.children %} - {{ form_row(input) }} - {% endfor %} +
+ {# we always have to render children, to mark as rendered #} + {% for input in form.children.formatter.children %} +
+ {{ form_row(input) }} +
+ {% endfor %} +
{% endif %} -
From 712c7bc49208c2a1bc335285a25a0f3647605e64 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 8 Sep 2022 11:03:20 +0200 Subject: [PATCH 007/120] add actionRender in construct, it was missing --- .../Aggregator/SocialWorkAggregators/ActionTypeAggregator.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php index b76db495d..fb64b3e01 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php @@ -29,6 +29,7 @@ final class ActionTypeAggregator implements AggregatorInterface SocialActionRender $actionRender ) { $this->socialActionRepository = $socialActionRepository; + $this->actionRender = $actionRender; } public function addRole(): ?string From 5f2622d0d2fd7b10a4bb79ce1026f46f07b5f38c Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 8 Sep 2022 11:08:34 +0200 Subject: [PATCH 008/120] rename files for coherence with naming elsewhere --- .../Export/{CountAppointments.php => CountCalendars.php} | 3 +-- ...tAppointmentAvgDuration.php => StatCalendarAvgDuration.php} | 2 +- ...tAppointmentSumDuration.php => StatCalendarSumDuration.php} | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) rename src/Bundle/ChillCalendarBundle/Export/Export/{CountAppointments.php => CountCalendars.php} (95%) rename src/Bundle/ChillCalendarBundle/Export/Export/{StatAppointmentAvgDuration.php => StatCalendarAvgDuration.php} (96%) rename src/Bundle/ChillCalendarBundle/Export/Export/{StatAppointmentSumDuration.php => StatCalendarSumDuration.php} (96%) diff --git a/src/Bundle/ChillCalendarBundle/Export/Export/CountAppointments.php b/src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php similarity index 95% rename from src/Bundle/ChillCalendarBundle/Export/Export/CountAppointments.php rename to src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php index b9da17114..edf654a10 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Export/CountAppointments.php +++ b/src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php @@ -22,9 +22,8 @@ use Doctrine\ORM\AbstractQuery; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Process\Exception\LogicException; -use Symfony\Component\Security\Core\Role\Role; -class CountAppointments implements ExportInterface, GroupedExportInterface +class CountCalendars implements ExportInterface, GroupedExportInterface { private CalendarRepository $calendarRepository; diff --git a/src/Bundle/ChillCalendarBundle/Export/Export/StatAppointmentAvgDuration.php b/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarAvgDuration.php similarity index 96% rename from src/Bundle/ChillCalendarBundle/Export/Export/StatAppointmentAvgDuration.php rename to src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarAvgDuration.php index 491cb38b9..51e94a288 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Export/StatAppointmentAvgDuration.php +++ b/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarAvgDuration.php @@ -22,7 +22,7 @@ use Doctrine\ORM\QueryBuilder; use LogicException; use Symfony\Component\Form\FormBuilderInterface; -class StatAppointmentAvgDuration implements ExportInterface, GroupedExportInterface +class StatCalendarAvgDuration implements ExportInterface, GroupedExportInterface { private CalendarRepository $calendarRepository; diff --git a/src/Bundle/ChillCalendarBundle/Export/Export/StatAppointmentSumDuration.php b/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarSumDuration.php similarity index 96% rename from src/Bundle/ChillCalendarBundle/Export/Export/StatAppointmentSumDuration.php rename to src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarSumDuration.php index 286c73be5..51591c670 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Export/StatAppointmentSumDuration.php +++ b/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarSumDuration.php @@ -22,7 +22,7 @@ use Doctrine\ORM\QueryBuilder; use LogicException; use Symfony\Component\Form\FormBuilderInterface; -class StatAppointmentSumDuration implements ExportInterface, GroupedExportInterface +class StatCalendarSumDuration implements ExportInterface, GroupedExportInterface { private CalendarRepository $calendarRepository; From ff5fab5f502efabd4547c870368641877b31d0bd Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 8 Sep 2022 11:08:34 +0200 Subject: [PATCH 009/120] rename files for coherence with naming elsewhere --- .../Resources/config/services/exports.yaml | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Bundle/ChillCalendarBundle/Resources/config/services/exports.yaml b/src/Bundle/ChillCalendarBundle/Resources/config/services/exports.yaml index 56580dba1..a7508f3b5 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/config/services/exports.yaml +++ b/src/Bundle/ChillCalendarBundle/Resources/config/services/exports.yaml @@ -1,26 +1,26 @@ services: ## Indicators - chill.calendar.export.count_appointments: - class: Chill\CalendarBundle\Export\Export\CountAppointments + chill.calendar.export.count_calendars: + class: Chill\CalendarBundle\Export\Export\CountCalendars autowire: true autoconfigure: true tags: - - { name: chill.export, alias: count_appointments } + - { name: chill.export, alias: count_calendars } - chill.calendar.export.average_duration_appointments: - class: Chill\CalendarBundle\Export\Export\StatAppointmentAvgDuration + chill.calendar.export.average_duration_calendars: + class: Chill\CalendarBundle\Export\Export\StatCalendarAvgDuration autowire: true autoconfigure: true tags: - - { name: chill.export, alias: average_duration_appointments } + - { name: chill.export, alias: average_duration_calendars } - chill.calendar.export.sum_duration_appointments: - class: Chill\CalendarBundle\Export\Export\StatAppointmentSumDuration + chill.calendar.export.sum_duration_calendars: + class: Chill\CalendarBundle\Export\Export\StatCalendarSumDuration autowire: true autoconfigure: true tags: - - { name: chill.export, alias: sum_duration_appointments } + - { name: chill.export, alias: sum_duration_calendars } ## Filters @@ -101,4 +101,4 @@ services: autowire: true autoconfigure: true tags: - - { name: chill.export_aggregator, alias: month_aggregator } \ No newline at end of file + - { name: chill.export_aggregator, alias: month_aggregator } From ebfb030ba6e377c556b251a3a5c1d592fef18715 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 8 Sep 2022 11:20:27 +0200 Subject: [PATCH 010/120] add querybuilder method to repository --- .../Repository/CalendarRepository.php | 236 +++++++++++++++--- 1 file changed, 202 insertions(+), 34 deletions(-) diff --git a/src/Bundle/ChillCalendarBundle/Repository/CalendarRepository.php b/src/Bundle/ChillCalendarBundle/Repository/CalendarRepository.php index 55fba5f80..626a9f29f 100644 --- a/src/Bundle/ChillCalendarBundle/Repository/CalendarRepository.php +++ b/src/Bundle/ChillCalendarBundle/Repository/CalendarRepository.php @@ -12,52 +12,220 @@ declare(strict_types=1); namespace Chill\CalendarBundle\Repository; use Chill\CalendarBundle\Entity\Calendar; -use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; +use Chill\MainBundle\Entity\User; +use Chill\PersonBundle\Entity\AccompanyingPeriod; +use DateTimeImmutable; +use Doctrine\DBAL\Types\Types; +use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; -use Doctrine\Persistence\ManagerRegistry; +use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\QueryBuilder; +use Doctrine\Persistence\ObjectRepository; +use function count; -/** - * @method Calendar|null find($id, $lockMode = null, $lockVersion = null) - * @method Calendar|null findOneBy(array $criteria, array $orderBy = null) - * @method Calendar[] findAll() - * @method Calendar[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) - */ -class CalendarRepository extends ServiceEntityRepository +class CalendarRepository implements ObjectRepository { - // private EntityRepository $repository; + private EntityManagerInterface $em; - public function __construct(ManagerRegistry $registry) + private EntityRepository $repository; + + public function __construct(EntityManagerInterface $entityManager) { - parent::__construct($registry, Calendar::class); - // $this->repository = $entityManager->getRepository(AccompanyingPeriodWork::class); + $this->repository = $entityManager->getRepository(Calendar::class); + $this->em = $entityManager; } - // /** - // * @return Calendar[] Returns an array of Calendar objects - // */ - /* - public function findByExampleField($value) + public function countByAccompanyingPeriod(AccompanyingPeriod $period): int { - return $this->createQueryBuilder('c') - ->andWhere('c.exampleField = :val') - ->setParameter('val', $value) - ->orderBy('c.id', 'ASC') - ->setMaxResults(10) + return $this->repository->count(['accompanyingPeriod' => $period]); + } + + public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder + { + return $this->repository->createQueryBuilder($alias, $indexBy); + } + + public function countByUser(User $user, DateTimeImmutable $from, DateTimeImmutable $to): int + { + return $this->buildQueryByUser($user, $from, $to) + ->select('COUNT(c)') ->getQuery() - ->getResult() - ; + ->getSingleScalarResult(); } - */ - /* - public function findOneBySomeField($value): ?Calendar + public function find($id): ?Calendar { - return $this->createQueryBuilder('c') - ->andWhere('c.exampleField = :val') - ->setParameter('val', $value) - ->getQuery() - ->getOneOrNullResult() - ; + return $this->repository->find($id); } + + /** + * @return array|Calendar[] */ + public function findAll(): array + { + return $this->repository->findAll(); + } + + /** + * @return array|Calendar[] + */ + public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + /** + * @return array|Calendar[] + */ + public function findByAccompanyingPeriod(AccompanyingPeriod $period, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array + { + return $this->findBy( + [ + 'accompanyingPeriod' => $period, + ], + $orderBy, + $limit, + $orderBy + ); + } + + public function findByNotificationAvailable(DateTimeImmutable $startDate, DateTimeImmutable $endDate, ?int $limit = null, ?int $offset = null): array + { + $qb = $this->queryByNotificationAvailable($startDate, $endDate)->select('c'); + + if (null !== $limit) { + $qb->setMaxResults($limit); + } + + if (null !== $offset) { + $qb->setFirstResult($offset); + } + + return $qb->getQuery()->getResult(); + } + + /** + * @return array|Calendar[] + */ + public function findByUser(User $user, DateTimeImmutable $from, DateTimeImmutable $to, ?int $limit = null, ?int $offset = null): array + { + $qb = $this->buildQueryByUser($user, $from, $to)->select('c'); + + if (null !== $limit) { + $qb->setMaxResults($limit); + } + + if (null !== $offset) { + $qb->setFirstResult($offset); + } + + return $qb->getQuery()->getResult(); + } + + public function findOneBy(array $criteria): ?Calendar + { + return $this->repository->findOneBy($criteria); + } + + /** + * Given a list of remote ids, return an array where + * keys are the remoteIds, and value is a boolean, true if the + * id is present in database. + * + * @param array|list $remoteIds + * + * @return array + */ + public function findRemoteIdsPresent(array $remoteIds): array + { + if (0 === count($remoteIds)) { + return []; + } + + $remoteIdsStr = implode( + ', ', + array_fill(0, count($remoteIds), '((?))') + ); + + $sql = "SELECT + sq.remoteId as remoteid, + EXISTS (SELECT 1 FROM chill_calendar.calendar c WHERE c.remoteId = sq.remoteId) AS present + FROM + ( + VALUES {$remoteIdsStr} + ) AS sq(remoteId); + "; + + $rsm = new ResultSetMapping(); + $rsm + ->addScalarResult('remoteid', 'remoteId', Types::STRING) + ->addScalarResult('present', 'present', Types::BOOLEAN); + + $rows = $this->em + ->createNativeQuery( + $sql, + $rsm + ) + ->setParameters(array_values($remoteIds)) + ->getResult(); + + $results = []; + + foreach ($rows as $r) { + $results[$r['remoteId']] = $r['present']; + } + + return $results; + } + + public function getClassName() + { + return Calendar::class; + } + + private function buildQueryByUser(User $user, DateTimeImmutable $from, DateTimeImmutable $to): QueryBuilder + { + $qb = $this->repository->createQueryBuilder('c'); + + return $qb + ->where( + $qb->expr()->andX( + $qb->expr()->eq('c.mainUser', ':user'), + $qb->expr()->gte('c.startDate', ':startDate'), + $qb->expr()->lte('c.endDate', ':endDate'), + ) + ) + ->setParameters([ + 'user' => $user, + 'startDate' => $from, + 'endDate' => $to, + ]); + } + + private function queryByNotificationAvailable(DateTimeImmutable $startDate, DateTimeImmutable $endDate): QueryBuilder + { + $qb = $this->repository->createQueryBuilder('c'); + + $qb->where( + $qb->expr()->andX( + $qb->expr()->eq('c.sendSMS', ':true'), + $qb->expr()->gte('c.startDate', ':startDate'), + $qb->expr()->lt('c.startDate', ':endDate'), + $qb->expr()->orX( + $qb->expr()->eq('c.smsStatus', ':pending'), + $qb->expr()->eq('c.smsStatus', ':cancel_pending') + ) + ) + ); + + $qb->setParameters([ + 'true' => true, + 'startDate' => $startDate, + 'endDate' => $endDate, + 'pending' => Calendar::SMS_PENDING, + 'cancel_pending' => Calendar::SMS_CANCEL_PENDING, + ]); + + return $qb; + } } From 822b96f87fa2351bac329e0626ec88eed233fb17 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 8 Sep 2022 11:25:50 +0200 Subject: [PATCH 011/120] adjust property name to make it work with changes calendar bundle --- .../ChillCalendarBundle/Export/Aggregator/AgentAggregator.php | 2 +- .../ChillCalendarBundle/Export/Aggregator/JobAggregator.php | 2 +- .../ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php | 2 +- src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php | 2 +- src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php | 2 +- src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php index 57ec33cdd..2974fb875 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php @@ -41,7 +41,7 @@ final class AgentAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('caluser', $qb->getAllAliases(), true)) { - $qb->join('cal.user', 'caluser'); + $qb->join('cal.mainUser', 'caluser'); } $qb->addSelect('caluser.id AS agent_aggregator'); diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php index 17905cf35..46fc6e63d 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php @@ -41,7 +41,7 @@ final class JobAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('caluser', $qb->getAllAliases(), true)) { - $qb->join('cal.user', 'caluser'); + $qb->join('cal.mainUser', 'caluser'); } $qb->addSelect('IDENTITY(caluser.userJob) as job_aggregator'); diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php index 7605c3d5d..351451ead 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php @@ -41,7 +41,7 @@ final class ScopeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('caluser', $qb->getAllAliases(), true)) { - $qb->join('cal.user', 'caluser'); + $qb->join('cal.mainUser', 'caluser'); } $qb->addSelect('IDENTITY(caluser.mainScope) as scope_aggregator'); diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php index 263b1e160..fe33c1210 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php @@ -37,7 +37,7 @@ class AgentFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->in('cal.user', ':agents'); + $clause = $qb->expr()->in('cal.mainUser', ':agents'); if ($where instanceof Andx) { $where->add($clause); diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php index 03cd4857d..9248143c7 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php @@ -43,7 +43,7 @@ class JobFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('caluser', $qb->getAllAliases(), true)) { - $qb->join('cal.user', 'caluser'); + $qb->join('cal.mainUser', 'caluser'); } $where = $qb->getDQLPart('where'); diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php index cbd566e9e..10e9e699c 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php @@ -43,7 +43,7 @@ class ScopeFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('caluser', $qb->getAllAliases(), true)) { - $qb->join('cal.user', 'caluser'); + $qb->join('cal.mainUser', 'caluser'); } $where = $qb->getDQLPart('where'); From 12c37ddb2c47b6ba3b394ecbf1eb400da402c94d Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 8 Sep 2022 11:59:44 +0200 Subject: [PATCH 012/120] adjust property name to make it work with changes calendar bundle --- .../Export/Aggregator/AgentAggregator.php | 2 +- .../Export/Aggregator/CancelReasonAggregator.php | 4 ++-- .../Export/Aggregator/JobAggregator.php | 2 +- .../Export/Aggregator/LocationAggregator.php | 2 +- .../Export/Aggregator/LocationTypeAggregator.php | 2 +- .../Export/Aggregator/MonthYearAggregator.php | 8 ++++---- .../Export/Aggregator/ScopeAggregator.php | 2 +- .../ChillCalendarBundle/Export/Export/CountCalendars.php | 7 ++++--- .../Export/Export/StatCalendarAvgDuration.php | 4 ++-- .../Export/Export/StatCalendarSumDuration.php | 4 ++-- .../ChillCalendarBundle/Export/Filter/AgentFilter.php | 2 +- .../Export/Filter/BetweenDatesFilter.php | 4 ++-- .../ChillCalendarBundle/Export/Filter/JobFilter.php | 2 +- .../ChillCalendarBundle/Export/Filter/ScopeFilter.php | 2 +- 14 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php index 2974fb875..3c2b54855 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php @@ -85,6 +85,6 @@ final class AgentAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group appointments by agent'; + return 'Group calendars by agent'; } } diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php index a07f052bf..854710a79 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php @@ -40,7 +40,7 @@ class CancelReasonAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - // TODO: still needs to take into account appointments without a cancel reason somehow + // TODO: still needs to take into account calendars without a cancel reason somehow if (!in_array('calcancel', $qb->getAllAliases(), true)) { $qb->join('cal.cancelReason', 'calcancel'); } @@ -88,6 +88,6 @@ class CancelReasonAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group appointments by cancel reason'; + return 'Group calendars by cancel reason'; } } diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php index 46fc6e63d..51500f45f 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php @@ -87,6 +87,6 @@ final class JobAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group appointments by agent job'; + return 'Group calendars by agent job'; } } diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php index 236b1b74f..287dccec7 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php @@ -80,6 +80,6 @@ final class LocationAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group appointments by location'; + return 'Group calendars by location'; } } diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php index c8d02160f..5d7559b2d 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php @@ -87,6 +87,6 @@ final class LocationTypeAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group appointments by location type'; + return 'Group calendars by location type'; } } diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php index 26329ad13..b4f01db12 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php @@ -55,10 +55,10 @@ class MonthYearAggregator implements AggregatorInterface return 'by month and year'; } - $month = substr($value, 0, 2); - $year = substr($value, 3, 4); + $month = (int)substr($value, 0, 2); + $year = (int)substr($value, 3, 4); - return strftime('%B %G', mktime(0, 0, 0, $month, '1', $year)); + return strftime('%B %G', mktime(0, 0, 0, $month, 1, $year)); }; } @@ -69,6 +69,6 @@ class MonthYearAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group appointments by month and year'; + return 'Group calendars by month and year'; } } diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php index 351451ead..8d3685f96 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php @@ -87,6 +87,6 @@ final class ScopeAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group appointments by agent scope'; + return 'Group calendars by agent scope'; } } diff --git a/src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php b/src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php index edf654a10..9d78d0f26 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php +++ b/src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php @@ -21,7 +21,8 @@ use Closure; use Doctrine\ORM\AbstractQuery; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Process\Exception\LogicException; +use Symfony\Component\Validator\Exception\LogicException; + class CountCalendars implements ExportInterface, GroupedExportInterface { @@ -44,7 +45,7 @@ class CountCalendars implements ExportInterface, GroupedExportInterface public function getDescription(): string { - return 'Count appointments by various parameters.'; + return 'Count calendars by various parameters.'; } public function getGroup(): string @@ -78,7 +79,7 @@ class CountCalendars implements ExportInterface, GroupedExportInterface public function getTitle(): string { - return 'Count appointments'; + return 'Count calendars'; } public function getType(): string diff --git a/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarAvgDuration.php b/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarAvgDuration.php index 51e94a288..4649ef571 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarAvgDuration.php +++ b/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarAvgDuration.php @@ -44,7 +44,7 @@ class StatCalendarAvgDuration implements ExportInterface, GroupedExportInterface public function getDescription(): string { - return 'Get the average of appointment duration according to various filters'; + return 'Get the average of calendar duration according to various filters'; } public function getGroup(): string @@ -78,7 +78,7 @@ class StatCalendarAvgDuration implements ExportInterface, GroupedExportInterface public function getTitle(): string { - return 'Average appointment duration'; + return 'Average calendar duration'; } public function getType(): string diff --git a/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarSumDuration.php b/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarSumDuration.php index 51591c670..4e2ed6a63 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarSumDuration.php +++ b/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarSumDuration.php @@ -44,7 +44,7 @@ class StatCalendarSumDuration implements ExportInterface, GroupedExportInterface public function getDescription(): string { - return 'Get the sum of appointment durations according to various filters'; + return 'Get the sum of calendar durations according to various filters'; } public function getGroup(): string @@ -78,7 +78,7 @@ class StatCalendarSumDuration implements ExportInterface, GroupedExportInterface public function getTitle(): string { - return 'Sum of appointment durations'; + return 'Sum of calendar durations'; } public function getType(): string diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php index fe33c1210..a0b77ece9 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php @@ -82,6 +82,6 @@ class AgentFilter implements FilterInterface public function getTitle(): string { - return 'Filter appointments by agent'; + return 'Filter calendars by agent'; } } diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/BetweenDatesFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/BetweenDatesFilter.php index c3aafc192..23e3ac4e6 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/BetweenDatesFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/BetweenDatesFilter.php @@ -66,7 +66,7 @@ class BetweenDatesFilter implements FilterInterface public function describeAction($data, $format = 'string'): array { - return ['Filtered by appointments between %dateFrom% and %dateTo%', [ + return ['Filtered by calendars between %dateFrom% and %dateTo%', [ '%dateFrom%' => $data['date_from']->format('d-m-Y'), '%dateTo%' => $data['date_to']->format('d-m-Y'), ]]; @@ -74,6 +74,6 @@ class BetweenDatesFilter implements FilterInterface public function getTitle(): string { - return 'Filter appointments between certain dates'; + return 'Filter calendars between certain dates'; } } diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php index 9248143c7..d5a70b3da 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php @@ -95,6 +95,6 @@ class JobFilter implements FilterInterface public function getTitle(): string { - return 'Filter appointments by agent job'; + return 'Filter calendars by agent job'; } } diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php index 10e9e699c..9f12cbf19 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php @@ -95,6 +95,6 @@ class ScopeFilter implements FilterInterface public function getTitle() { - return 'Filter appointments by agent scope'; + return 'Filter calendars by agent scope'; } } From 967c8c62d469142b625379b859e6c53de190d124 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 8 Sep 2022 12:45:59 +0200 Subject: [PATCH 013/120] Revert "adjust property name to make it work with changes calendar bundle" This reverts commit 12c37ddb2c47b6ba3b394ecbf1eb400da402c94d. --- .../Export/Aggregator/AgentAggregator.php | 2 +- .../Export/Aggregator/CancelReasonAggregator.php | 4 ++-- .../Export/Aggregator/JobAggregator.php | 2 +- .../Export/Aggregator/LocationAggregator.php | 2 +- .../Export/Aggregator/LocationTypeAggregator.php | 2 +- .../Export/Aggregator/MonthYearAggregator.php | 8 ++++---- .../Export/Aggregator/ScopeAggregator.php | 2 +- .../ChillCalendarBundle/Export/Export/CountCalendars.php | 7 +++---- .../Export/Export/StatCalendarAvgDuration.php | 4 ++-- .../Export/Export/StatCalendarSumDuration.php | 4 ++-- .../ChillCalendarBundle/Export/Filter/AgentFilter.php | 2 +- .../Export/Filter/BetweenDatesFilter.php | 4 ++-- .../ChillCalendarBundle/Export/Filter/JobFilter.php | 2 +- .../ChillCalendarBundle/Export/Filter/ScopeFilter.php | 2 +- 14 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php index 3c2b54855..2974fb875 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php @@ -85,6 +85,6 @@ final class AgentAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group calendars by agent'; + return 'Group appointments by agent'; } } diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php index 854710a79..a07f052bf 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php @@ -40,7 +40,7 @@ class CancelReasonAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - // TODO: still needs to take into account calendars without a cancel reason somehow + // TODO: still needs to take into account appointments without a cancel reason somehow if (!in_array('calcancel', $qb->getAllAliases(), true)) { $qb->join('cal.cancelReason', 'calcancel'); } @@ -88,6 +88,6 @@ class CancelReasonAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group calendars by cancel reason'; + return 'Group appointments by cancel reason'; } } diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php index 51500f45f..46fc6e63d 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php @@ -87,6 +87,6 @@ final class JobAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group calendars by agent job'; + return 'Group appointments by agent job'; } } diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php index 287dccec7..236b1b74f 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php @@ -80,6 +80,6 @@ final class LocationAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group calendars by location'; + return 'Group appointments by location'; } } diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php index 5d7559b2d..c8d02160f 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php @@ -87,6 +87,6 @@ final class LocationTypeAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group calendars by location type'; + return 'Group appointments by location type'; } } diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php index b4f01db12..26329ad13 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php @@ -55,10 +55,10 @@ class MonthYearAggregator implements AggregatorInterface return 'by month and year'; } - $month = (int)substr($value, 0, 2); - $year = (int)substr($value, 3, 4); + $month = substr($value, 0, 2); + $year = substr($value, 3, 4); - return strftime('%B %G', mktime(0, 0, 0, $month, 1, $year)); + return strftime('%B %G', mktime(0, 0, 0, $month, '1', $year)); }; } @@ -69,6 +69,6 @@ class MonthYearAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group calendars by month and year'; + return 'Group appointments by month and year'; } } diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php index 8d3685f96..351451ead 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php @@ -87,6 +87,6 @@ final class ScopeAggregator implements AggregatorInterface public function getTitle(): string { - return 'Group calendars by agent scope'; + return 'Group appointments by agent scope'; } } diff --git a/src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php b/src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php index 9d78d0f26..edf654a10 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php +++ b/src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php @@ -21,8 +21,7 @@ use Closure; use Doctrine\ORM\AbstractQuery; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Validator\Exception\LogicException; - +use Symfony\Component\Process\Exception\LogicException; class CountCalendars implements ExportInterface, GroupedExportInterface { @@ -45,7 +44,7 @@ class CountCalendars implements ExportInterface, GroupedExportInterface public function getDescription(): string { - return 'Count calendars by various parameters.'; + return 'Count appointments by various parameters.'; } public function getGroup(): string @@ -79,7 +78,7 @@ class CountCalendars implements ExportInterface, GroupedExportInterface public function getTitle(): string { - return 'Count calendars'; + return 'Count appointments'; } public function getType(): string diff --git a/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarAvgDuration.php b/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarAvgDuration.php index 4649ef571..51e94a288 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarAvgDuration.php +++ b/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarAvgDuration.php @@ -44,7 +44,7 @@ class StatCalendarAvgDuration implements ExportInterface, GroupedExportInterface public function getDescription(): string { - return 'Get the average of calendar duration according to various filters'; + return 'Get the average of appointment duration according to various filters'; } public function getGroup(): string @@ -78,7 +78,7 @@ class StatCalendarAvgDuration implements ExportInterface, GroupedExportInterface public function getTitle(): string { - return 'Average calendar duration'; + return 'Average appointment duration'; } public function getType(): string diff --git a/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarSumDuration.php b/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarSumDuration.php index 4e2ed6a63..51591c670 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarSumDuration.php +++ b/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarSumDuration.php @@ -44,7 +44,7 @@ class StatCalendarSumDuration implements ExportInterface, GroupedExportInterface public function getDescription(): string { - return 'Get the sum of calendar durations according to various filters'; + return 'Get the sum of appointment durations according to various filters'; } public function getGroup(): string @@ -78,7 +78,7 @@ class StatCalendarSumDuration implements ExportInterface, GroupedExportInterface public function getTitle(): string { - return 'Sum of calendar durations'; + return 'Sum of appointment durations'; } public function getType(): string diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php index a0b77ece9..fe33c1210 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php @@ -82,6 +82,6 @@ class AgentFilter implements FilterInterface public function getTitle(): string { - return 'Filter calendars by agent'; + return 'Filter appointments by agent'; } } diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/BetweenDatesFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/BetweenDatesFilter.php index 23e3ac4e6..c3aafc192 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/BetweenDatesFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/BetweenDatesFilter.php @@ -66,7 +66,7 @@ class BetweenDatesFilter implements FilterInterface public function describeAction($data, $format = 'string'): array { - return ['Filtered by calendars between %dateFrom% and %dateTo%', [ + return ['Filtered by appointments between %dateFrom% and %dateTo%', [ '%dateFrom%' => $data['date_from']->format('d-m-Y'), '%dateTo%' => $data['date_to']->format('d-m-Y'), ]]; @@ -74,6 +74,6 @@ class BetweenDatesFilter implements FilterInterface public function getTitle(): string { - return 'Filter calendars between certain dates'; + return 'Filter appointments between certain dates'; } } diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php index d5a70b3da..9248143c7 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php @@ -95,6 +95,6 @@ class JobFilter implements FilterInterface public function getTitle(): string { - return 'Filter calendars by agent job'; + return 'Filter appointments by agent job'; } } diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php index 9f12cbf19..10e9e699c 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php @@ -95,6 +95,6 @@ class ScopeFilter implements FilterInterface public function getTitle() { - return 'Filter calendars by agent scope'; + return 'Filter appointments by agent scope'; } } From b0d77a16569626ad54d728553b121c3bb8d6b16c Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 8 Sep 2022 12:46:15 +0200 Subject: [PATCH 014/120] Revert "adjust property name to make it work with changes calendar bundle" This reverts commit 822b96f87fa2351bac329e0626ec88eed233fb17. --- .../ChillCalendarBundle/Export/Aggregator/AgentAggregator.php | 2 +- .../ChillCalendarBundle/Export/Aggregator/JobAggregator.php | 2 +- .../ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php | 2 +- src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php | 2 +- src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php | 2 +- src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php index 2974fb875..57ec33cdd 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php @@ -41,7 +41,7 @@ final class AgentAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('caluser', $qb->getAllAliases(), true)) { - $qb->join('cal.mainUser', 'caluser'); + $qb->join('cal.user', 'caluser'); } $qb->addSelect('caluser.id AS agent_aggregator'); diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php index 46fc6e63d..17905cf35 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php @@ -41,7 +41,7 @@ final class JobAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('caluser', $qb->getAllAliases(), true)) { - $qb->join('cal.mainUser', 'caluser'); + $qb->join('cal.user', 'caluser'); } $qb->addSelect('IDENTITY(caluser.userJob) as job_aggregator'); diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php index 351451ead..7605c3d5d 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php @@ -41,7 +41,7 @@ final class ScopeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('caluser', $qb->getAllAliases(), true)) { - $qb->join('cal.mainUser', 'caluser'); + $qb->join('cal.user', 'caluser'); } $qb->addSelect('IDENTITY(caluser.mainScope) as scope_aggregator'); diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php index fe33c1210..263b1e160 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php @@ -37,7 +37,7 @@ class AgentFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->in('cal.mainUser', ':agents'); + $clause = $qb->expr()->in('cal.user', ':agents'); if ($where instanceof Andx) { $where->add($clause); diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php index 9248143c7..03cd4857d 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php @@ -43,7 +43,7 @@ class JobFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('caluser', $qb->getAllAliases(), true)) { - $qb->join('cal.mainUser', 'caluser'); + $qb->join('cal.user', 'caluser'); } $where = $qb->getDQLPart('where'); diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php index 10e9e699c..cbd566e9e 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php @@ -43,7 +43,7 @@ class ScopeFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('caluser', $qb->getAllAliases(), true)) { - $qb->join('cal.mainUser', 'caluser'); + $qb->join('cal.user', 'caluser'); } $where = $qb->getDQLPart('where'); From 8cf9bf4a5f82db693d49d508fd03c1013b98757b Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 8 Sep 2022 12:46:19 +0200 Subject: [PATCH 015/120] Revert "add querybuilder method to repository" This reverts commit ebfb030ba6e377c556b251a3a5c1d592fef18715. --- .../Repository/CalendarRepository.php | 238 +++--------------- 1 file changed, 35 insertions(+), 203 deletions(-) diff --git a/src/Bundle/ChillCalendarBundle/Repository/CalendarRepository.php b/src/Bundle/ChillCalendarBundle/Repository/CalendarRepository.php index 626a9f29f..55fba5f80 100644 --- a/src/Bundle/ChillCalendarBundle/Repository/CalendarRepository.php +++ b/src/Bundle/ChillCalendarBundle/Repository/CalendarRepository.php @@ -12,220 +12,52 @@ declare(strict_types=1); namespace Chill\CalendarBundle\Repository; use Chill\CalendarBundle\Entity\Calendar; -use Chill\MainBundle\Entity\User; -use Chill\PersonBundle\Entity\AccompanyingPeriod; -use DateTimeImmutable; -use Doctrine\DBAL\Types\Types; -use Doctrine\ORM\EntityManagerInterface; +use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\ORM\EntityRepository; -use Doctrine\ORM\Query\ResultSetMapping; -use Doctrine\ORM\QueryBuilder; -use Doctrine\Persistence\ObjectRepository; -use function count; +use Doctrine\Persistence\ManagerRegistry; -class CalendarRepository implements ObjectRepository +/** + * @method Calendar|null find($id, $lockMode = null, $lockVersion = null) + * @method Calendar|null findOneBy(array $criteria, array $orderBy = null) + * @method Calendar[] findAll() + * @method Calendar[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class CalendarRepository extends ServiceEntityRepository { - private EntityManagerInterface $em; + // private EntityRepository $repository; - private EntityRepository $repository; - - public function __construct(EntityManagerInterface $entityManager) + public function __construct(ManagerRegistry $registry) { - $this->repository = $entityManager->getRepository(Calendar::class); - $this->em = $entityManager; + parent::__construct($registry, Calendar::class); + // $this->repository = $entityManager->getRepository(AccompanyingPeriodWork::class); } - public function countByAccompanyingPeriod(AccompanyingPeriod $period): int + // /** + // * @return Calendar[] Returns an array of Calendar objects + // */ + /* + public function findByExampleField($value) { - return $this->repository->count(['accompanyingPeriod' => $period]); - } - - public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder - { - return $this->repository->createQueryBuilder($alias, $indexBy); - } - - public function countByUser(User $user, DateTimeImmutable $from, DateTimeImmutable $to): int - { - return $this->buildQueryByUser($user, $from, $to) - ->select('COUNT(c)') + return $this->createQueryBuilder('c') + ->andWhere('c.exampleField = :val') + ->setParameter('val', $value) + ->orderBy('c.id', 'ASC') + ->setMaxResults(10) ->getQuery() - ->getSingleScalarResult(); + ->getResult() + ; } - - public function find($id): ?Calendar - { - return $this->repository->find($id); - } - - /** - * @return array|Calendar[] */ - public function findAll(): array - { - return $this->repository->findAll(); - } - /** - * @return array|Calendar[] + /* + public function findOneBySomeField($value): ?Calendar + { + return $this->createQueryBuilder('c') + ->andWhere('c.exampleField = :val') + ->setParameter('val', $value) + ->getQuery() + ->getOneOrNullResult() + ; + } */ - public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array - { - return $this->repository->findBy($criteria, $orderBy, $limit, $offset); - } - - /** - * @return array|Calendar[] - */ - public function findByAccompanyingPeriod(AccompanyingPeriod $period, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array - { - return $this->findBy( - [ - 'accompanyingPeriod' => $period, - ], - $orderBy, - $limit, - $orderBy - ); - } - - public function findByNotificationAvailable(DateTimeImmutable $startDate, DateTimeImmutable $endDate, ?int $limit = null, ?int $offset = null): array - { - $qb = $this->queryByNotificationAvailable($startDate, $endDate)->select('c'); - - if (null !== $limit) { - $qb->setMaxResults($limit); - } - - if (null !== $offset) { - $qb->setFirstResult($offset); - } - - return $qb->getQuery()->getResult(); - } - - /** - * @return array|Calendar[] - */ - public function findByUser(User $user, DateTimeImmutable $from, DateTimeImmutable $to, ?int $limit = null, ?int $offset = null): array - { - $qb = $this->buildQueryByUser($user, $from, $to)->select('c'); - - if (null !== $limit) { - $qb->setMaxResults($limit); - } - - if (null !== $offset) { - $qb->setFirstResult($offset); - } - - return $qb->getQuery()->getResult(); - } - - public function findOneBy(array $criteria): ?Calendar - { - return $this->repository->findOneBy($criteria); - } - - /** - * Given a list of remote ids, return an array where - * keys are the remoteIds, and value is a boolean, true if the - * id is present in database. - * - * @param array|list $remoteIds - * - * @return array - */ - public function findRemoteIdsPresent(array $remoteIds): array - { - if (0 === count($remoteIds)) { - return []; - } - - $remoteIdsStr = implode( - ', ', - array_fill(0, count($remoteIds), '((?))') - ); - - $sql = "SELECT - sq.remoteId as remoteid, - EXISTS (SELECT 1 FROM chill_calendar.calendar c WHERE c.remoteId = sq.remoteId) AS present - FROM - ( - VALUES {$remoteIdsStr} - ) AS sq(remoteId); - "; - - $rsm = new ResultSetMapping(); - $rsm - ->addScalarResult('remoteid', 'remoteId', Types::STRING) - ->addScalarResult('present', 'present', Types::BOOLEAN); - - $rows = $this->em - ->createNativeQuery( - $sql, - $rsm - ) - ->setParameters(array_values($remoteIds)) - ->getResult(); - - $results = []; - - foreach ($rows as $r) { - $results[$r['remoteId']] = $r['present']; - } - - return $results; - } - - public function getClassName() - { - return Calendar::class; - } - - private function buildQueryByUser(User $user, DateTimeImmutable $from, DateTimeImmutable $to): QueryBuilder - { - $qb = $this->repository->createQueryBuilder('c'); - - return $qb - ->where( - $qb->expr()->andX( - $qb->expr()->eq('c.mainUser', ':user'), - $qb->expr()->gte('c.startDate', ':startDate'), - $qb->expr()->lte('c.endDate', ':endDate'), - ) - ) - ->setParameters([ - 'user' => $user, - 'startDate' => $from, - 'endDate' => $to, - ]); - } - - private function queryByNotificationAvailable(DateTimeImmutable $startDate, DateTimeImmutable $endDate): QueryBuilder - { - $qb = $this->repository->createQueryBuilder('c'); - - $qb->where( - $qb->expr()->andX( - $qb->expr()->eq('c.sendSMS', ':true'), - $qb->expr()->gte('c.startDate', ':startDate'), - $qb->expr()->lt('c.startDate', ':endDate'), - $qb->expr()->orX( - $qb->expr()->eq('c.smsStatus', ':pending'), - $qb->expr()->eq('c.smsStatus', ':cancel_pending') - ) - ) - ); - - $qb->setParameters([ - 'true' => true, - 'startDate' => $startDate, - 'endDate' => $endDate, - 'pending' => Calendar::SMS_PENDING, - 'cancel_pending' => Calendar::SMS_CANCEL_PENDING, - ]); - - return $qb; - } } From 0a0a692eae24864d640efe489163c6398dc7d1e0 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 8 Sep 2022 12:46:23 +0200 Subject: [PATCH 016/120] Revert "rename files for coherence with naming elsewhere" This reverts commit ff5fab5f502efabd4547c870368641877b31d0bd. --- .../Resources/config/services/exports.yaml | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Bundle/ChillCalendarBundle/Resources/config/services/exports.yaml b/src/Bundle/ChillCalendarBundle/Resources/config/services/exports.yaml index a7508f3b5..56580dba1 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/config/services/exports.yaml +++ b/src/Bundle/ChillCalendarBundle/Resources/config/services/exports.yaml @@ -1,26 +1,26 @@ services: ## Indicators - chill.calendar.export.count_calendars: - class: Chill\CalendarBundle\Export\Export\CountCalendars + chill.calendar.export.count_appointments: + class: Chill\CalendarBundle\Export\Export\CountAppointments autowire: true autoconfigure: true tags: - - { name: chill.export, alias: count_calendars } + - { name: chill.export, alias: count_appointments } - chill.calendar.export.average_duration_calendars: - class: Chill\CalendarBundle\Export\Export\StatCalendarAvgDuration + chill.calendar.export.average_duration_appointments: + class: Chill\CalendarBundle\Export\Export\StatAppointmentAvgDuration autowire: true autoconfigure: true tags: - - { name: chill.export, alias: average_duration_calendars } + - { name: chill.export, alias: average_duration_appointments } - chill.calendar.export.sum_duration_calendars: - class: Chill\CalendarBundle\Export\Export\StatCalendarSumDuration + chill.calendar.export.sum_duration_appointments: + class: Chill\CalendarBundle\Export\Export\StatAppointmentSumDuration autowire: true autoconfigure: true tags: - - { name: chill.export, alias: sum_duration_calendars } + - { name: chill.export, alias: sum_duration_appointments } ## Filters @@ -101,4 +101,4 @@ services: autowire: true autoconfigure: true tags: - - { name: chill.export_aggregator, alias: month_aggregator } + - { name: chill.export_aggregator, alias: month_aggregator } \ No newline at end of file From d81afb89f26eeeb99665bb8e1d06b91520c43433 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 8 Sep 2022 12:48:19 +0200 Subject: [PATCH 017/120] Revert "rename files for coherence with naming elsewhere" This reverts commit 5f2622d0d2fd7b10a4bb79ce1026f46f07b5f38c. --- .../Export/{CountCalendars.php => CountAppointments.php} | 3 ++- ...tCalendarAvgDuration.php => StatAppointmentAvgDuration.php} | 2 +- ...tCalendarSumDuration.php => StatAppointmentSumDuration.php} | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) rename src/Bundle/ChillCalendarBundle/Export/Export/{CountCalendars.php => CountAppointments.php} (95%) rename src/Bundle/ChillCalendarBundle/Export/Export/{StatCalendarAvgDuration.php => StatAppointmentAvgDuration.php} (96%) rename src/Bundle/ChillCalendarBundle/Export/Export/{StatCalendarSumDuration.php => StatAppointmentSumDuration.php} (96%) diff --git a/src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php b/src/Bundle/ChillCalendarBundle/Export/Export/CountAppointments.php similarity index 95% rename from src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php rename to src/Bundle/ChillCalendarBundle/Export/Export/CountAppointments.php index edf654a10..b9da17114 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php +++ b/src/Bundle/ChillCalendarBundle/Export/Export/CountAppointments.php @@ -22,8 +22,9 @@ use Doctrine\ORM\AbstractQuery; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Process\Exception\LogicException; +use Symfony\Component\Security\Core\Role\Role; -class CountCalendars implements ExportInterface, GroupedExportInterface +class CountAppointments implements ExportInterface, GroupedExportInterface { private CalendarRepository $calendarRepository; diff --git a/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarAvgDuration.php b/src/Bundle/ChillCalendarBundle/Export/Export/StatAppointmentAvgDuration.php similarity index 96% rename from src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarAvgDuration.php rename to src/Bundle/ChillCalendarBundle/Export/Export/StatAppointmentAvgDuration.php index 51e94a288..491cb38b9 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarAvgDuration.php +++ b/src/Bundle/ChillCalendarBundle/Export/Export/StatAppointmentAvgDuration.php @@ -22,7 +22,7 @@ use Doctrine\ORM\QueryBuilder; use LogicException; use Symfony\Component\Form\FormBuilderInterface; -class StatCalendarAvgDuration implements ExportInterface, GroupedExportInterface +class StatAppointmentAvgDuration implements ExportInterface, GroupedExportInterface { private CalendarRepository $calendarRepository; diff --git a/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarSumDuration.php b/src/Bundle/ChillCalendarBundle/Export/Export/StatAppointmentSumDuration.php similarity index 96% rename from src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarSumDuration.php rename to src/Bundle/ChillCalendarBundle/Export/Export/StatAppointmentSumDuration.php index 51591c670..286c73be5 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Export/StatCalendarSumDuration.php +++ b/src/Bundle/ChillCalendarBundle/Export/Export/StatAppointmentSumDuration.php @@ -22,7 +22,7 @@ use Doctrine\ORM\QueryBuilder; use LogicException; use Symfony\Component\Form\FormBuilderInterface; -class StatCalendarSumDuration implements ExportInterface, GroupedExportInterface +class StatAppointmentSumDuration implements ExportInterface, GroupedExportInterface { private CalendarRepository $calendarRepository; From 661e5458ee9a376f85610a5b6420f98ba3e09b65 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Thu, 8 Sep 2022 14:17:17 +0200 Subject: [PATCH 018/120] bam --- src/Bundle/ChillPersonBundle/translations/messages.fr.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 70a0cda42..98facddf9 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -552,6 +552,7 @@ Group by treating agent: Grouper les actions par agent traitant Group social work actions by action type: Grouper les actions par type Group social work actions by goal: Grouper les actions par objectif +Goal Type: Objectif Group social work actions by result: Grouper les actions par résultat ## evaluations filters/aggr From eafe68973ac13ad06c8a07e352cb3cd7f25217ae Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Thu, 8 Sep 2022 14:35:48 +0200 Subject: [PATCH 019/120] [bug] Temporary bypass Vue component --- .../ChillMainBundle/Resources/views/Export/new.html.twig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig index c6adb241d..8bab23d21 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig @@ -23,7 +23,13 @@ {% block js %} {{ encore_entry_script_tags('page_export') }} {% if export_alias == 'count_social_work_actions' %} + {# + TODO: [debug] comprendre pq l'activation du composant Vue bloque le passage de + http://localhost:8001/fr/exports/new/count_social_work_actions?step=export + vers http://localhost:8001/fr/exports/new/count_social_work_actions?step=formatter + {{ encore_entry_script_tags('vue_form_action_goal_result') }} + #} {% endif %} {% endblock js %} From ef7a388f384ea891f044d3517596b30c1e9747de Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Tue, 13 Sep 2022 12:37:25 +0200 Subject: [PATCH 020/120] exports: fix resetDQLPart('from') issue (632) https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/632 --- exports_alias_conventions.csv | 10 +++---- exports_alias_conventions.md | 10 +++---- .../HouseholdPositionAggregator.php | 30 ++++++++++--------- .../ActivityTypeFilter.php | 12 +++----- .../ResidentialAddressAtThirdpartyFilter.php | 12 ++++---- .../ResidentialAddressAtUserFilter.php | 13 ++++---- 6 files changed, 39 insertions(+), 48 deletions(-) diff --git a/exports_alias_conventions.csv b/exports_alias_conventions.csv index 1215479d8..a835c383a 100644 --- a/exports_alias_conventions.csv +++ b/exports_alias_conventions.csv @@ -23,17 +23,15 @@ Goal::class,,,goal ,Result::class,goal.results,goalresult Person::class,,,person ,Center::class,person.center,center -,HouseholdMember::class,partperson.householdParticipations,member +,HouseholdMember::class,partperson.householdParticipations,householdmember ,MaritalStatus::class,person.maritalStatus,personmarital ResidentialAddress::class,,,resaddr -,Person::class,resaddr.person,resaddrperson -,Center::class,resaddrperson.center,resaddrcenter ,ThirdParty::class,resaddr.hostThirdParty,tparty ThirdParty::class,,,tparty ,ThirdPartyCategory::class,tparty.categories,tpartycat -HouseholdMember::class,,,member -,Household::class,member.household,household -,Person::class,member.person,memberperson +HouseholdMember::class,,,householdmember +,Household::class,householdmember.household,household +,Person::class,householdmember.person,memberperson ,,memberperson.center,membercenter Household::class,,,household ,HouseholdComposition::class,household.compositions,composition diff --git a/exports_alias_conventions.md b/exports_alias_conventions.md index 85973eec1..404156bff 100644 --- a/exports_alias_conventions.md +++ b/exports_alias_conventions.md @@ -31,17 +31,15 @@ These are alias conventions : | | Result::class | goal.results | goalresult | | Person::class | | | person | | | Center::class | person.center | center | -| | HouseholdMember::class | partperson.householdParticipations | member | +| | HouseholdMember::class | partperson.householdParticipations | householdmember | | | MaritalStatus::class | person.maritalStatus | personmarital | | ResidentialAddress::class | | | resaddr | -| | Person::class | resaddr.person | resaddrperson | -| | Center::class | resaddrperson.center | resaddrcenter | | | ThirdParty::class | resaddr.hostThirdParty | tparty | | ThirdParty::class | | | tparty | | | ThirdPartyCategory::class | tparty.categories | tpartycat | -| HouseholdMember::class | | | member | -| | Household::class | member.household | household | -| | Person::class | member.person | memberperson | +| HouseholdMember::class | | | householdmember | +| | Household::class | householdmember.household | household | +| | Person::class | householdmember.person | memberperson | | | | memberperson.center | membercenter | | Household::class | | | household | | | HouseholdComposition::class | household.compositions | composition | diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php index c0b52053d..89c0bb8f5 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php @@ -16,8 +16,10 @@ use Chill\MainBundle\Export\ExportElementValidatedInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\Household\HouseholdMember; +use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Repository\Household\PositionRepository; use DateTime; +use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface; @@ -31,8 +33,11 @@ final class HouseholdPositionAggregator implements AggregatorInterface, ExportEl private TranslatorInterface $translator; - public function __construct(TranslatorInterface $translator, TranslatableStringHelper $translatableStringHelper, PositionRepository $positionRepository) - { + public function __construct( + TranslatorInterface $translator, + TranslatableStringHelper $translatableStringHelper, + PositionRepository $positionRepository + ) { $this->translator = $translator; $this->positionRepository = $positionRepository; $this->translatableStringHelper = $translatableStringHelper; @@ -45,28 +50,25 @@ final class HouseholdPositionAggregator implements AggregatorInterface, ExportEl public function alterQuery(QueryBuilder $qb, $data) { - $qb->resetDQLPart('from'); - $qb->from(HouseholdMember::class, 'member'); - - if (!in_array('memberperson', $qb->getAllAliases(), true)) { - $qb->join('member.person', 'memberperson'); + if (!in_array('householdmember', $qb->getAllAliases(), true)) { + $qb->join(HouseholdMember::class, 'householdmember', Expr\Join::WITH, 'householdmember.person = person'); } - if (!in_array('membercenter', $qb->getAllAliases(), true)) { - $qb->join('memberperson.center', 'membercenter'); + if (!in_array('center', $qb->getAllAliases(), true)) { + $qb->join('person.center', 'center'); } $qb->andWhere($qb->expr()->andX( - $qb->expr()->lte('member.startDate', ':date'), + $qb->expr()->lte('householdmember.startDate', ':date'), $qb->expr()->orX( - $qb->expr()->isNull('member.endDate'), - $qb->expr()->gte('member.endDate', ':date') + $qb->expr()->isNull('householdmember.endDate'), + $qb->expr()->gte('householdmember.endDate', ':date') ) )); $qb->setParameter('date', $data['date_position']); - $qb->addSelect('IDENTITY(member.position) AS household_position_aggregator'); + $qb->addSelect('IDENTITY(householdmember.position) AS household_position_aggregator'); $groupBy = $qb->getDQLPart('groupBy'); @@ -79,7 +81,7 @@ final class HouseholdPositionAggregator implements AggregatorInterface, ExportEl public function applyOn() { - return 'person'; + return Declarations::PERSON_TYPE; } public function buildForm(FormBuilderInterface $builder) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilter.php index f76a00c33..fab99df5f 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilter.php @@ -11,10 +11,12 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; +use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\ActivityType; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Export\Declarations; +use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; @@ -39,14 +41,8 @@ class ActivityTypeFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - // One2many between activity and accompanyingperiod is not reversed ! - // we replace indicator 'from' clause by 'act', and put 'acp' in a join - - $qb->resetDQLPart('from'); - $qb->from('ChillActivityBundle:Activity', 'activity'); - - if (!in_array('actacp', $qb->getAllAliases(), true)) { - $qb->join('activity.accompanyingPeriod', 'actacp'); + if (!in_array('activity', $qb->getAllAliases(), true)) { + $qb->join(Activity::class, 'activity', Expr\Join::WITH, 'activity.accompanyingPeriod = acp'); } if (!in_array('acttype', $qb->getAllAliases(), true)) { diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php index 73c551286..c38ce6a5b 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Export\Filter\PersonFilters; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Templating\TranslatableStringHelper; +use Chill\PersonBundle\Entity\Person\ResidentialAddress; use Chill\PersonBundle\Export\Declarations; use Chill\ThirdPartyBundle\Entity\ThirdPartyCategory; use DateTime; @@ -38,15 +39,12 @@ class ResidentialAddressAtThirdpartyFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->resetDQLPart('from'); - $qb->from('ChillPersonBundle:Person\ResidentialAddress', 'resaddr'); - - if (!in_array('resaddrperson', $qb->getAllAliases(), true)) { - $qb->join('resaddr.person', 'resaddrperson'); + if (!in_array('resaddr', $qb->getAllAliases(), true)) { + $qb->join(ResidentialAddress::class, 'resaddr', Expr\Join::WITH, 'resaddr.person = person'); } - if (!in_array('resaddrcenter', $qb->getAllAliases(), true)) { - $qb->join('resaddrperson.center', 'resaddrcenter'); + if (!in_array('center', $qb->getAllAliases(), true)) { + $qb->join('person.center', 'center'); } if (!in_array('tparty', $qb->getAllAliases(), true)) { diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php index 5e78e574e..48275eb12 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php @@ -12,7 +12,9 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\PersonFilters; use Chill\MainBundle\Export\FilterInterface; +use Chill\PersonBundle\Entity\Person\ResidentialAddress; use Chill\PersonBundle\Export\Declarations; +use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; @@ -25,15 +27,12 @@ class ResidentialAddressAtUserFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->resetDQLPart('from'); - $qb->from('ChillPersonBundle:Person\ResidentialAddress', 'resaddr'); - - if (!in_array('resaddrperson', $qb->getAllAliases(), true)) { - $qb->join('resaddr.person', 'resaddrperson'); + if (!in_array('resaddr', $qb->getAllAliases(), true)) { + $qb->join(ResidentialAddress::class, 'resaddr', Expr\Join::WITH, 'resaddr.person = person'); } - if (!in_array('resaddrcenter', $qb->getAllAliases(), true)) { - $qb->join('resaddrperson.center', 'resaddrcenter'); + if (!in_array('center', $qb->getAllAliases(), true)) { + $qb->join('person.center', 'center'); } $where = $qb->getDQLPart('where'); From 42ea1f581319f33ffa27f0bb652096a96c4efb19 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Tue, 13 Sep 2022 11:12:42 +0200 Subject: [PATCH 021/120] exports: better lisibility of exports index page --- .../ChillMainBundle/Resources/public/chill/chillmain.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Bundle/ChillMainBundle/Resources/public/chill/chillmain.scss b/src/Bundle/ChillMainBundle/Resources/public/chill/chillmain.scss index f8bd42442..5c302bf83 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/chill/chillmain.scss +++ b/src/Bundle/ChillMainBundle/Resources/public/chill/chillmain.scss @@ -517,3 +517,9 @@ div.popover { div.v-toast { z-index: 10000!important; } + +div.grouped { + padding: 1em; + border: 1px solid black; + margin-bottom: 2em; +} \ No newline at end of file From d85be8a92eba9257500366c5eaa3531901602722 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Tue, 13 Sep 2022 13:09:40 +0200 Subject: [PATCH 022/120] update exports_alias_conventions --- exports_alias_conventions.csv | 7 +- exports_alias_conventions.md | 131 +++++++++++++++++----------------- 2 files changed, 66 insertions(+), 72 deletions(-) diff --git a/exports_alias_conventions.csv b/exports_alias_conventions.csv index a835c383a..4b791463a 100644 --- a/exports_alias_conventions.csv +++ b/exports_alias_conventions.csv @@ -25,6 +25,8 @@ Person::class,,,person ,Center::class,person.center,center ,HouseholdMember::class,partperson.householdParticipations,householdmember ,MaritalStatus::class,person.maritalStatus,personmarital +,VendeePerson::class,,vp +,VendeePersonMineur::class,,vpm ResidentialAddress::class,,,resaddr ,ThirdParty::class,resaddr.hostThirdParty,tparty ThirdParty::class,,,tparty @@ -56,11 +58,6 @@ Calendar::class,,,cal ,Location::class,cal.location,calloc ,User::class,cal.user,caluser VendeePerson::class,,,vp -,Person::class,vp.person,vpperson -,Center::class,vpperson.center,vpcenter ,SituationProfessionelle::class,vp.situationProfessionelle,vpprof ,StatutLogement::class,vp.statutLogement,vplog ,TempsDeTravail::class,vp.tempsDeTravail,vptt -VendeePersonMineur::class,,,vpm -,Person::class,vpm.person,vpmperson -,Center::class,vpmperson.center,vpmcenter diff --git a/exports_alias_conventions.md b/exports_alias_conventions.md index 404156bff..f2dbb481e 100644 --- a/exports_alias_conventions.md +++ b/exports_alias_conventions.md @@ -5,70 +5,67 @@ Add condition with distinct alias on each export join clauses (Indicators + Filt These are alias conventions : -| Entity | Join | Attribute | Alias | -| :--- | :--- |:-------------------------------------------|:----------------------------------| -| AccompanyingPeriod::class | | | acp | -| | AccompanyingPeriodWork::class | acp.works | acpw | -| | AccompanyingPeriodParticipation::class | acp.participations | acppart | -| | Location::class | acp.administrativeLocation | acploc | -| | ClosingMotive::class | acp.closingMotive | acpmotive | -| | UserJob::class | acp.job | acpjob | -| | Origin::class | acp.origin | acporigin | -| | Scope::class | acp.scopes | acpscope | -| | SocialIssue::class | acp.socialIssues | acpsocialissue | -| | User::class | acp.user | acpuser | -| AccompanyingPeriodWork::class | | | acpw | -| | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval | -| | Goal::class | acpw.goals | goal | -| | User::class | acpw.referrers | acpwuser | -| | Result::class | acpw.results | acpwresult | -| | SocialAction::class | acpw.socialAction | acpwsocialaction | -| AccompanyingPeriodParticipation::class | | | acppart | -| | Person::class | acppart.person | partperson | -| AccompanyingPeriodWorkEvaluation::class | | | workeval | -| | Evaluation::class | workeval.evaluation | eval | -| Goal::class | | | goal | -| | Result::class | goal.results | goalresult | -| Person::class | | | person | -| | Center::class | person.center | center | -| | HouseholdMember::class | partperson.householdParticipations | householdmember | -| | MaritalStatus::class | person.maritalStatus | personmarital | -| ResidentialAddress::class | | | resaddr | -| | ThirdParty::class | resaddr.hostThirdParty | tparty | -| ThirdParty::class | | | tparty | -| | ThirdPartyCategory::class | tparty.categories | tpartycat | -| HouseholdMember::class | | | householdmember | -| | Household::class | householdmember.household | household | -| | Person::class | householdmember.person | memberperson | -| | | memberperson.center | membercenter | -| Household::class | | | household | -| | HouseholdComposition::class | household.compositions | composition | -| Activity::class | | | activity | -| | Person::class | activity.person | actperson | -| | AccompanyingPeriod::class | activity.accompanyingPeriod | actacp | -| | Person::class | activity\_person\_having\_activity.person | person\_person\_having\_activity | -| | ActivityReason::class | activity\_person\_having\_activity.reasons | reasons\_person\_having\_activity | -| | ActivityType::class | activity.activityType | acttype | -| | Location::class | activity.location | actloc | -| | SocialAction::class | activity.socialActions | actsocialaction | -| | SocialIssue::class | activity.socialIssues | actsocialssue | -| | ThirdParty::class | activity.thirdParties | acttparty | -| | User::class | activity.user | actuser | -| | User::class | activity.users | actusers | -| | ActivityReason::class | activity.reasons | actreasons | -| | Center::class | actperson.center | actcenter | -| ActivityReason::class | | | actreasons | -| | ActivityReasonCategory::class | actreason.category | actreasoncat | -| Calendar::class | | | cal | -| | CancelReason::class | cal.cancelReason | calcancel | -| | Location::class | cal.location | calloc | -| | User::class | cal.user | caluser | -| VendeePerson::class | | | vp | -| | Person::class | vp.person | vpperson | -| | Center::class | vpperson.center | vpcenter | -| | SituationProfessionelle::class | vp.situationProfessionelle | vpprof | -| | StatutLogement::class | vp.statutLogement | vplog | -| | TempsDeTravail::class | vp.tempsDeTravail | vptt | -| VendeePersonMineur::class | | | vpm | -| | Person::class | vpm.person | vpmperson | -| | Center::class | vpmperson.center | vpmcenter | +| Entity | Join | Attribute | Alias | +|:----------------------------------------|:----------------------------------------|:-------------------------------------------|:----------------------------------| +| AccompanyingPeriod::class | | | acp | +| | AccompanyingPeriodWork::class | acp.works | acpw | +| | AccompanyingPeriodParticipation::class | acp.participations | acppart | +| | Location::class | acp.administrativeLocation | acploc | +| | ClosingMotive::class | acp.closingMotive | acpmotive | +| | UserJob::class | acp.job | acpjob | +| | Origin::class | acp.origin | acporigin | +| | Scope::class | acp.scopes | acpscope | +| | SocialIssue::class | acp.socialIssues | acpsocialissue | +| | User::class | acp.user | acpuser | +| AccompanyingPeriodWork::class | | | acpw | +| | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval | +| | Goal::class | acpw.goals | goal | +| | User::class | acpw.referrers | acpwuser | +| | Result::class | acpw.results | acpwresult | +| | SocialAction::class | acpw.socialAction | acpwsocialaction | +| AccompanyingPeriodParticipation::class | | | acppart | +| | Person::class | acppart.person | partperson | +| AccompanyingPeriodWorkEvaluation::class | | | workeval | +| | Evaluation::class | workeval.evaluation | eval | +| Goal::class | | | goal | +| | Result::class | goal.results | goalresult | +| Person::class | | | person | +| | Center::class | person.center | center | +| | HouseholdMember::class | partperson.householdParticipations | householdmember | +| | MaritalStatus::class | person.maritalStatus | personmarital | +| | VendeePerson::class | | vp | +| | VendeePersonMineur::class | | vpm | +| ResidentialAddress::class | | | resaddr | +| | ThirdParty::class | resaddr.hostThirdParty | tparty | +| ThirdParty::class | | | tparty | +| | ThirdPartyCategory::class | tparty.categories | tpartycat | +| HouseholdMember::class | | | householdmember | +| | Household::class | householdmember.household | household | +| | Person::class | householdmember.person | memberperson | +| | | memberperson.center | membercenter | +| Household::class | | | household | +| | HouseholdComposition::class | household.compositions | composition | +| Activity::class | | | activity | +| | Person::class | activity.person | actperson | +| | AccompanyingPeriod::class | activity.accompanyingPeriod | actacp | +| | Person::class | activity\_person\_having\_activity.person | person\_person\_having\_activity | +| | ActivityReason::class | activity\_person\_having\_activity.reasons | reasons\_person\_having\_activity | +| | ActivityType::class | activity.activityType | acttype | +| | Location::class | activity.location | actloc | +| | SocialAction::class | activity.socialActions | actsocialaction | +| | SocialIssue::class | activity.socialIssues | actsocialssue | +| | ThirdParty::class | activity.thirdParties | acttparty | +| | User::class | activity.user | actuser | +| | User::class | activity.users | actusers | +| | ActivityReason::class | activity.reasons | actreasons | +| | Center::class | actperson.center | actcenter | +| ActivityReason::class | | | actreasons | +| | ActivityReasonCategory::class | actreason.category | actreasoncat | +| Calendar::class | | | cal | +| | CancelReason::class | cal.cancelReason | calcancel | +| | Location::class | cal.location | calloc | +| | User::class | cal.user | caluser | +| VendeePerson::class | | | vp | +| | SituationProfessionelle::class | vp.situationProfessionelle | vpprof | +| | StatutLogement::class | vp.statutLogement | vplog | +| | TempsDeTravail::class | vp.tempsDeTravail | vptt | From 18a6a5a7ebeefc5999d16f7a3edf9bf89ba8e9e8 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Tue, 13 Sep 2022 13:27:24 +0200 Subject: [PATCH 023/120] exports: move acp ActivityType filter in ChillActivityBundle --- .../Export/Filter/ACPFilters}/ActivityTypeFilter.php | 2 +- src/Bundle/ChillActivityBundle/config/services/export.yaml | 5 +++++ .../config/services/exports_accompanying_course.yaml | 7 ------- 3 files changed, 6 insertions(+), 8 deletions(-) rename src/Bundle/{ChillPersonBundle/Export/Filter/AccompanyingCourseFilters => ChillActivityBundle/Export/Filter/ACPFilters}/ActivityTypeFilter.php (97%) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ActivityTypeFilter.php similarity index 97% rename from src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilter.php rename to src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ActivityTypeFilter.php index fab99df5f..7c0b202bd 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ActivityTypeFilter.php @@ -9,7 +9,7 @@ declare(strict_types=1); -namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; +namespace Chill\ActivityBundle\Export\Filter\ACPFilters; use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\ActivityType; diff --git a/src/Bundle/ChillActivityBundle/config/services/export.yaml b/src/Bundle/ChillActivityBundle/config/services/export.yaml index c88046c5f..9abf33487 100644 --- a/src/Bundle/ChillActivityBundle/config/services/export.yaml +++ b/src/Bundle/ChillActivityBundle/config/services/export.yaml @@ -67,6 +67,11 @@ services: name: chill.export_filter alias: 'activity_person_having_ac_bw_date_filter' + chill.person.export.filter_activitytype: + class: Chill\ActivityBundle\Export\Filter\ACPFilters\ActivityTypeFilter + tags: + - { name: chill.export_filter, alias: accompanyingcourse_activitytype_filter } + chill.activity.export.locationtype_filter: class: Chill\ActivityBundle\Export\Filter\ACPFilters\LocationTypeFilter tags: diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml index 73bd1cce3..f19db0649 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml @@ -65,13 +65,6 @@ services: tags: - { name: chill.export_filter, alias: accompanyingcourse_evaluation_filter } - chill.person.export.filter_activitytype: - class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ActivityTypeFilter - autowire: true - autoconfigure: true - tags: - - { name: chill.export_filter, alias: accompanyingcourse_activitytype_filter } - chill.person.export.filter_origin: class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\OriginFilter autowire: true From 6c29638fed8ba23f37e77e6b1eb0614eef68e18c Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Tue, 13 Sep 2022 14:54:36 +0200 Subject: [PATCH 024/120] exports: fix export_result cell if null (issue 628) https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/628 --- .../ChillMainBundle/Export/Formatter/SpreadSheetFormatter.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Bundle/ChillMainBundle/Export/Formatter/SpreadSheetFormatter.php b/src/Bundle/ChillMainBundle/Export/Formatter/SpreadSheetFormatter.php index 86c304508..6d6a685b7 100644 --- a/src/Bundle/ChillMainBundle/Export/Formatter/SpreadSheetFormatter.php +++ b/src/Bundle/ChillMainBundle/Export/Formatter/SpreadSheetFormatter.php @@ -230,7 +230,8 @@ class SpreadSheetFormatter implements FormatterInterface $worksheet->fromArray( $sortedResults, null, - 'A' . $line + 'A' . $line, + true ); return $line + count($sortedResults) + 1; From 71f49db2f4929565a5544b34290da2e87304cc18 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Tue, 13 Sep 2022 21:05:22 +0200 Subject: [PATCH 025/120] disable unstable filters/aggregators --- .../config/services/exports_accompanying_course.yaml | 12 ++++++------ .../config/services/exports_social_actions.yaml | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml index 2b578b4f9..b0452915b 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml @@ -44,12 +44,12 @@ services: tags: - { name: chill.export_filter, alias: accompanyingcourse_step_filter } - chill.person.export.filter_geographicalunitstat: - class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\GeographicalUnitStatFilter - autowire: true - autoconfigure: true - tags: - - { name: chill.export_filter, alias: accompanyingcourse_geographicalunitstat_filter } + #chill.person.export.filter_geographicalunitstat: + # class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\GeographicalUnitStatFilter + # autowire: true + # autoconfigure: true + # tags: + # - { name: chill.export_filter, alias: accompanyingcourse_geographicalunitstat_filter } chill.person.export.filter_socialaction: class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\SocialActionFilter diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml index d5f7376db..04dc1a2ac 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml @@ -9,12 +9,12 @@ services: ## FILTERS - chill.person.export.filter_social_work_type: - class: Chill\PersonBundle\Export\Filter\SocialWorkFilters\SocialWorkTypeFilter - autowire: true - autoconfigure: true - tags: - - { name: chill.export_filter, alias: social_work_type_filter } + #chill.person.export.filter_social_work_type: + # class: Chill\PersonBundle\Export\Filter\SocialWorkFilters\SocialWorkTypeFilter + # autowire: true + # autoconfigure: true + # tags: + # - { name: chill.export_filter, alias: social_work_type_filter } chill.person.export.filter_scope: class: Chill\PersonBundle\Export\Filter\SocialWorkFilters\ScopeFilter From 9b1e464011247b03958013d6ee16080df739e388 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 14 Sep 2022 09:38:12 +0200 Subject: [PATCH 026/120] fix form error in App.vue (does not POST from step=export to step=formatter) --- .../Resources/public/vuejs/FormActionGoalResult/App.vue | 3 --- .../ChillMainBundle/Resources/views/Export/new.html.twig | 6 ------ 2 files changed, 9 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue index 39a7c023a..62ce45c23 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue @@ -7,7 +7,6 @@
Date: Thu, 8 Sep 2022 15:00:37 +0200 Subject: [PATCH 027/120] exports: fix buildForm in SocialWorkType Filter --- .../SocialWorkTypeFilter.php | 82 ++++++------------- 1 file changed, 26 insertions(+), 56 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php index c2ffd74c6..7629a7b60 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php @@ -50,72 +50,42 @@ class SocialWorkTypeFilter implements FilterInterface $builder ->add('actionType', HiddenType::class) ->get('actionType') - ->addModelTransformer(new CallbackTransformer( - static function (?iterable $actionsAsIterable): string { - if (null === $actionsAsIterable) { return ''; } - $actionIds = []; - foreach ($actionsAsIterable as $value) { - $actionIds[] = $value->getId(); - } - return implode(',', $actionIds); - }, - function (?string $actionsAsString): array { - if (null === $actionsAsString) { return []; } - return array_map( - fn (string $id): ?SocialAction - => $this->socialActionRepository->findOneBy(['id' => (int) $id]), - explode(',', $actionsAsString) - ); - } - )) + ->addModelTransformer($this->iterableToIdTransformer(SocialAction::class)) ; - $builder ->add('goal', HiddenType::class) ->get('goal') - ->addModelTransformer(new CallbackTransformer( - static function (?iterable $goalsAsIterable): string { - if (null === $goalsAsIterable) { return ''; } - $goalIds = []; - foreach ($goalsAsIterable as $value) { - $goalIds[] = $value->getId(); - } - return implode(',', $goalIds); - }, - function (?string $goalsAsString): array { - if (null === $goalsAsString) { return []; } - return array_map( - fn (string $id): ?Goal - => $this->socialActionRepository->findOneBy(['id' => (int) $id]), - explode(',', $goalsAsString) - ); - } - )) + ->addModelTransformer($this->iterableToIdTransformer(Goal::class)) ; - $builder ->add('result', HiddenType::class) ->get('result') - ->addModelTransformer(new CallbackTransformer( - static function (?iterable $resultsAsIterable): string { - if (null === $resultsAsIterable) { return ''; } - $resultIds = []; - foreach ($resultsAsIterable as $value) { - $resultIds[] = $value->getId(); - } - return implode(',', $resultIds); - }, - function (?string $resultsAsString): array { - if (null === $resultsAsString) { return []; } - return array_map( - fn (string $id): ?Result - => $this->socialActionRepository->findOneBy(['id' => (int) $id]), - explode(',', $resultsAsString) - ); - } - )) + ->addModelTransformer($this->iterableToIdTransformer(Result::class)) ; + } + private function iterableToIdTransformer(string $entity): CallbackTransformer + { + return new CallbackTransformer( + static function (?iterable $asIterable): string { + if (null === $asIterable) { return ''; } + $ids = []; + foreach ($asIterable as $value) { + $ids[] = $value->getId(); + } + return implode(',', $ids); + }, + function (?string $asString) use ($entity): array { + if (null === $asString) { return []; } + return array_map( + fn (string $id) + => $this->em + ->getRepository($entity) + ->findOneBy(['id' => (int) $id]), + explode(',', $asString) + ); + } + ); } public function getTitle(): string From 478afc893bbc6bb9c18c3cbad595b10afa5d13ee Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 14 Sep 2022 12:08:57 +0200 Subject: [PATCH 028/120] exports: fix alterQuery and DescribeAction in SocialWorkTypeFilter --- .../SocialWorkTypeFilter.php | 90 ++++++++++++------- .../translations/messages.fr.yml | 4 +- 2 files changed, 63 insertions(+), 31 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php index 7629a7b60..0cf7f979d 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php @@ -50,17 +50,23 @@ class SocialWorkTypeFilter implements FilterInterface $builder ->add('actionType', HiddenType::class) ->get('actionType') - ->addModelTransformer($this->iterableToIdTransformer(SocialAction::class)) + ->addModelTransformer( + $this->iterableToIdTransformer(SocialAction::class) + ) ; $builder ->add('goal', HiddenType::class) ->get('goal') - ->addModelTransformer($this->iterableToIdTransformer(Goal::class)) + ->addModelTransformer( + $this->iterableToIdTransformer(Goal::class) + ) ; $builder ->add('result', HiddenType::class) ->get('result') - ->addModelTransformer($this->iterableToIdTransformer(Result::class)) + ->addModelTransformer( + $this->iterableToIdTransformer(Result::class) + ) ; } @@ -90,27 +96,35 @@ class SocialWorkTypeFilter implements FilterInterface public function getTitle(): string { - return 'Filter by type of action, objectives and results'; + return 'Filter by type of action, goals and results'; } public function describeAction($data, $format = 'string'): array { $actionTypes = []; - $objectives = []; + $goals = []; $results = []; foreach ($data['actionType'] as $at) { - $actionTypes[] = $at->getTitle(); - } - foreach ($data['objectives'] as $o) { - $objectives[] = $o->getTitle(); - } - foreach ($data['results'] as $r) { - $results[] = $r->getTitle(); + $actionTypes[] = $this->translatableStringHelper->localize( + $at->getTitle() + ); } - return ['Filtered by referrers: only %actionTypes%', [ - '%actionTypes%' => implode(', ou ', $actionTypes) + foreach ($data['goal'] as $g) { + $goals[] = $this->translatableStringHelper->localize( + $g->getTitle() + ); + } + + foreach ($data['result'] as $r) { + $results[] = $this->translatableStringHelper->localize( + $r->getTitle() + ); + } + + return ['Filtered actions by type, goals and results: %selected%', [ + '%selected%' => implode(', ', array_merge($actionTypes, $goals, $results)) ]]; } @@ -122,28 +136,44 @@ class SocialWorkTypeFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->eq('acpw.socialAction',':actionType'); - /* - if (!empty($data['goal'])) { - $clause - $qb->expr()->in('acpw.goals', ':goals'); + if (count($data['actionType']) > 0) { + $clause = $qb->expr()->in('acpw.socialAction', ':actionType'); + + if ($where instanceof Andx) { + $where->add($clause); + } else { + $where = $qb->expr()->andX($clause); + } + + $qb->setParameter('actionType', $data['actionType']); } - $qb->expr()->in('acpw.results', ':results'); - */ - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); + if (count($data['goal']) > 0) { + if (!in_array('goal', $qb->getAllAliases(), true)) { + $qb->join('acpw.goals', 'goal'); + } + + $where->add( + $qb->expr()->in('goal.id', ':goals') + ); + + $qb->setParameter('goals', $data['goal']); + } + + if (count($data['result']) > 0) { + if (!in_array('result', $qb->getAllAliases(), true)) { + $qb->join('acpw.results', 'result'); + } + + $where->add( + $qb->expr()->in('result.id', ':results') + ); + + $qb->setParameter('results', $data['result']); } $qb->add('where', $where); - $qb->setParameters([ - 'actionType' => $data['actionType'], - 'goal' => $data['goal'], - 'result' => $data['result'], - ]); } public function applyOn(): string diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 98facddf9..05e9a82a0 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -456,7 +456,9 @@ Filter by socialaction: Filtrer les parcours par action d'accompagnement Accepted socialactions: Actions d'accompagnement "Filtered by socialactions: only %socialactions%": "Filtré par action d'accompagnement: uniquement %socialactions%" Group by social action: Grouper les parcours par action d'accompagnement -Filter by type of action, objectives and results: "Filtrer les actions par type, objectif et résultat" + +Filter by type of action, goals and results: "Filtrer les actions par type, objectif et résultat" +'Filtered actions by type, goals and results: %selected%': "Actions filtrées par: %selected%" Filter by evaluation: Filtrer les parcours par évaluation Accepted evaluations: Évaluations From b2e83892a74922c310cc7256cdb762ed9ec776a5 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 14 Sep 2022 12:30:17 +0200 Subject: [PATCH 029/120] update export alias conventions --- exports_alias_conventions.csv | 4 ++-- exports_alias_conventions.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exports_alias_conventions.csv b/exports_alias_conventions.csv index 4b791463a..59b4c0fee 100644 --- a/exports_alias_conventions.csv +++ b/exports_alias_conventions.csv @@ -11,10 +11,10 @@ AccompanyingPeriod::class,,,acp ,User::class,acp.user,acpuser AccompanyingPeriodWork::class,,,acpw ,AccompanyingPeriodWorkEvaluation::class,acpw.accompanyingPeriodWorkEvaluations,workeval -,Goal::class,acpw.goals,goal ,User::class,acpw.referrers,acpwuser -,Result::class,acpw.results,acpwresult ,SocialAction::class,acpw.socialAction,acpwsocialaction +,Goal::class,acpw.goals,goal +,Result::class,acpw.results,result AccompanyingPeriodParticipation::class,,,acppart ,Person::class,acppart.person,partperson AccompanyingPeriodWorkEvaluation::class,,,workeval diff --git a/exports_alias_conventions.md b/exports_alias_conventions.md index f2dbb481e..f04c97ff2 100644 --- a/exports_alias_conventions.md +++ b/exports_alias_conventions.md @@ -19,10 +19,10 @@ These are alias conventions : | | User::class | acp.user | acpuser | | AccompanyingPeriodWork::class | | | acpw | | | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval | -| | Goal::class | acpw.goals | goal | | | User::class | acpw.referrers | acpwuser | -| | Result::class | acpw.results | acpwresult | | | SocialAction::class | acpw.socialAction | acpwsocialaction | +| | Goal::class | acpw.goals | goal | +| | Result::class | acpw.results | result | | AccompanyingPeriodParticipation::class | | | acppart | | | Person::class | acppart.person | partperson | | AccompanyingPeriodWorkEvaluation::class | | | workeval | From 6c191f584f6bb4859410b1bdde62a570ed4dec6b Mon Sep 17 00:00:00 2001 From: LenaertsJ Date: Wed, 14 Sep 2022 12:09:25 +0000 Subject: [PATCH 030/120] =?UTF-8?q?fix=20bug=20when=20member=20is=20non-po?= =?UTF-8?q?sitionn=C3=A9,=20case=20not=20taken=20into=20account=20in=20if-?= =?UTF-8?q?condition?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Resources/views/Household/summary.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Household/summary.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Household/summary.html.twig index a8ff245d7..bdf06c0bd 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/Household/summary.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/Household/summary.html.twig @@ -180,7 +180,7 @@ {% if members|length > 0 %}
{% for m in members %} - {% if m.position.shareHousehold %} + {% if m.position is null or m.position.shareHousehold %} {% include '@ChillPerson/Household/_render_member.html.twig' with { 'member': m, 'customButtons': { 'before': _self.customButtons(m, household) } From 424c9239b776d256a061f5ac612bffd39c5840c4 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 14 Sep 2022 10:00:02 +0200 Subject: [PATCH 031/120] App.vue: improve logs to understand algo --- .../public/vuejs/FormActionGoalResult/App.vue | 77 ++++++++++++++----- 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue index 62ce45c23..93ee6c958 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue @@ -54,6 +54,8 @@ new Promise((resolve, reject) => { - //console.log('fetch Goals', response); this.addElementInData('goals', response.results); resolve(); })).catch; getResultByAction(value.id).then(response => new Promise((resolve, reject) => { - //console.log('fetch Results', response); this.addElementInData('results', response.results); resolve(); })).catch; }, unselectAction(value) { - console.log('unselectAction', value); + console.log('----'); console.log('unselect action', value.id); getGoalByAction(value.id).then(response => new Promise((resolve, reject) => { - //console.log('fetch Goals', response); - this.goals.options = this.removeElementInData('goals', response.results); + [ this.goals.options, this.goals.value ] = this.removeElementInData('goals', response.results); resolve(); })).catch; getResultByAction(value.id).then(response => new Promise((resolve, reject) => { - //console.log('fetch Results', response); - this.results.options = this.removeElementInData('results', response.results); + [ this.results.options, this.results.value ] = this.removeElementInData('results', response.results); resolve(); })).catch; }, @@ -197,23 +198,33 @@ export default { * @param value */ selectGoal(value) { - console.log('selectGoal', value); + console.log('----'); console.log('select goal', value.id); getResultByGoal(value.id).then(response => new Promise((resolve, reject) => { - //console.log('fetch Results', response); this.addElementInData('results', response.results); resolve(); })).catch; }, unselectGoal(value) { - console.log('unselectGoal', value); + console.log('----'); console.log('unselect goal', value.id); getResultByGoal(value.id).then(response => new Promise((resolve, reject) => { - //console.log('fetch Results', response); - this.results.options = this.removeElementInData('results', response.results); + [ this.results.options, this.results.value ] = this.removeElementInData('results', response.results); resolve(); })).catch; }, + /** + * Select/unselect in Result Multiselect + * @param value + */ + selectResult(value) { + console.log('----'); console.log('select result', value.id); + }, + + unselectResult(value) { + console.log('----'); console.log('unselect result', value.id); + }, + /** * Add response elements in data target * @param target string -> 'actions', 'goals' or 'results' @@ -221,13 +232,17 @@ export default { */ addElementInData(target, response) { let data = this[target]; + let dump = []; response.forEach(elem => { let found = data.options.some(e => e.id === elem.id); if (!found) { - console.log('push elem in', target, elem.id); data.options.push(elem); + dump.push(elem.id); } }) + if (dump.length > 0) { + console.log('push ' + dump.length + ' elems in', target, dump); + } }, /** @@ -238,28 +253,48 @@ export default { */ removeElementInData(target, response) { let data = this[target]; + let dump = []; response.forEach(elem => { let found = data.options.some(e => e.id === elem.id); if (found) { - console.log('remove elem from', target, elem.id); data.options = data.options.filter(e => e.id !== elem.id); + dump.push(elem.id); /// remove too from selected and from hiddenField let selected = data.value.some(e => e.id === elem.id); if (selected) { + // remove from selected data.value = data.value.filter(e => e.id !== elem.id); + console.log('remove ' + elem.id + ' from selected ' + target); + // remove from hiddenField this.rebuildHiddenFieldValues(target); - // remove should be recursive; here it works but is not fine - if (target === 'goals') { // <==== TODO improve loop - this.unselectGoal(elem); - } + + // in any cases, remove should be recursive + this.unselectToNextField(target, elem); } /// } }) - return data.options; + if (dump.length > 0) { + console.log('remove ' + dump.length + ' elems from ' + target + ' options', dump); + } + return [ data.options, data.value ]; + }, + + /** + * When unselect Action, it could remove elements in goals multiselect. + * In that case, we have to unselect Goal to remove elements in results too. + * @param target + * @param elem + */ + unselectToNextField(target, elem) { + if (target === 'goals') { + console.log('!!!! target is goal: unselect goal', elem.id); + this.unselectGoal(elem); + console.log('!!!! done'); + } }, /** @@ -268,10 +303,12 @@ export default { */ rebuildHiddenFieldValues(target) { let data = this[target]; + console.log('rebuild hiddenFields ' + target + ' values :'); data.hiddenField.value = ''; // reset data.value.forEach(elem => { data.hiddenField.value = this.addIdToValue(data.hiddenField.value, elem.id); }) + console.log(data.hiddenField); }, addIdToValue(string, id) { From c7e88b39248358ac1b71107186d37859f7f83130 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 14 Sep 2022 14:10:17 +0200 Subject: [PATCH 032/120] select action childrens when selecting parent --- .../public/vuejs/FormActionGoalResult/App.vue | 100 +++++++++++++----- 1 file changed, 76 insertions(+), 24 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue index 93ee6c958..60d2c7d49 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue @@ -158,7 +158,6 @@ export default { this.results.hiddenField.value = ''; console.log(this.actions.hiddenField, this.goals.hiddenField, this.results.hiddenField); - // TODO choisir une action parente impliquera de retenir les actions "enfants". }, methods: { async getSocialActionsList() { @@ -171,14 +170,20 @@ export default { */ selectAction(value) { console.log('----'); console.log('select action', value.id); - getGoalByAction(value.id).then(response => new Promise((resolve, reject) => { - this.addElementInData('goals', response.results); - resolve(); - })).catch; - getResultByAction(value.id).then(response => new Promise((resolve, reject) => { - this.addElementInData('results', response.results); - resolve(); - })).catch; + let children = this.getChildrensFromParent(value); + this.addSelectedElement('actions', children); + + let parentAndChildren = [...[value], ...children]; + parentAndChildren.forEach(elem => { + getGoalByAction(elem.id).then(response => new Promise((resolve, reject) => { + this.addElementInData('goals', response.results); + resolve(); + })).catch; + getResultByAction(elem.id).then(response => new Promise((resolve, reject) => { + this.addElementInData('results', response.results); + resolve(); + })).catch; + }); }, unselectAction(value) { @@ -225,6 +230,21 @@ export default { console.log('----'); console.log('unselect result', value.id); }, + /** + * Choose parent action will involve retaining the "children" actions. + * @param value + * @return array + */ + getChildrensFromParent(value) { + if (null === value.parent) { + let excludeParent = this.actions.options.filter(o => o.parent !== null); + let children = excludeParent.filter(o => o.parent.id === value.id); + console.log("get childrens", children.map(e => e.id)); + return children; + } + return []; + }, + /** * Add response elements in data target * @param target string -> 'actions', 'goals' or 'results' @@ -260,21 +280,7 @@ export default { data.options = data.options.filter(e => e.id !== elem.id); dump.push(elem.id); - /// remove too from selected and from hiddenField - let selected = data.value.some(e => e.id === elem.id); - if (selected) { - - // remove from selected - data.value = data.value.filter(e => e.id !== elem.id); - console.log('remove ' + elem.id + ' from selected ' + target); - - // remove from hiddenField - this.rebuildHiddenFieldValues(target); - - // in any cases, remove should be recursive - this.unselectToNextField(target, elem); - } - /// + this.removeSelectedElement(target, elem); } }) if (dump.length > 0) { @@ -283,6 +289,52 @@ export default { return [ data.options, data.value ]; }, + /** + * + * @param target + * @param elements + */ + addSelectedElement(target, elements) { + let data = this[target]; + let dump = []; + elements.forEach(elem => { + let selected = data.value.some(e => e.id === elem.id); + if (!selected) { + + data.value.push(elem); + dump.push(elem.id); + + // add in hiddenField + this.rebuildHiddenFieldValues(target); + } + }); + if (dump.length > 0) { + console.log('add ' + dump.length + ' selected elems in', target, dump); + } + }, + + /** + * Remove element from selected and from hiddenField + * @param target + * @param elem + */ + removeSelectedElement(target, elem) { + let data = this[target]; + let selected = data.value.some(e => e.id === elem.id); + if (selected) { + + // remove from selected + data.value = data.value.filter(e => e.id !== elem.id); + console.log('remove ' + elem.id + ' from selected ' + target); + + // remove from hiddenField + this.rebuildHiddenFieldValues(target); + + // in any cases, remove should be recursive + this.unselectToNextField(target, elem); + } + }, + /** * When unselect Action, it could remove elements in goals multiselect. * In that case, we have to unselect Goal to remove elements in results too. From fceab958bb75fe815b87f5b87bb2834ddf757d5d Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 14 Sep 2022 13:31:05 +0200 Subject: [PATCH 033/120] comment logs --- .../public/vuejs/FormActionGoalResult/App.vue | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue index 60d2c7d49..d4d1be1b6 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue @@ -157,7 +157,7 @@ export default { this.goals.hiddenField.value = ''; this.results.hiddenField.value = ''; - console.log(this.actions.hiddenField, this.goals.hiddenField, this.results.hiddenField); + //console.log(this.actions.hiddenField, this.goals.hiddenField, this.results.hiddenField); }, methods: { async getSocialActionsList() { @@ -169,7 +169,7 @@ export default { * @param value */ selectAction(value) { - console.log('----'); console.log('select action', value.id); + //console.log('----'); console.log('select action', value.id); let children = this.getChildrensFromParent(value); this.addSelectedElement('actions', children); @@ -187,7 +187,7 @@ export default { }, unselectAction(value) { - console.log('----'); console.log('unselect action', value.id); + //console.log('----'); console.log('unselect action', value.id); getGoalByAction(value.id).then(response => new Promise((resolve, reject) => { [ this.goals.options, this.goals.value ] = this.removeElementInData('goals', response.results); resolve(); @@ -203,7 +203,7 @@ export default { * @param value */ selectGoal(value) { - console.log('----'); console.log('select goal', value.id); + //console.log('----'); console.log('select goal', value.id); getResultByGoal(value.id).then(response => new Promise((resolve, reject) => { this.addElementInData('results', response.results); resolve(); @@ -211,7 +211,7 @@ export default { }, unselectGoal(value) { - console.log('----'); console.log('unselect goal', value.id); + //console.log('----'); console.log('unselect goal', value.id); getResultByGoal(value.id).then(response => new Promise((resolve, reject) => { [ this.results.options, this.results.value ] = this.removeElementInData('results', response.results); resolve(); @@ -223,11 +223,11 @@ export default { * @param value */ selectResult(value) { - console.log('----'); console.log('select result', value.id); + //console.log('----'); console.log('select result', value.id); }, unselectResult(value) { - console.log('----'); console.log('unselect result', value.id); + //console.log('----'); console.log('unselect result', value.id); }, /** @@ -239,7 +239,7 @@ export default { if (null === value.parent) { let excludeParent = this.actions.options.filter(o => o.parent !== null); let children = excludeParent.filter(o => o.parent.id === value.id); - console.log("get childrens", children.map(e => e.id)); + //console.log("get childrens", children.map(e => e.id)); return children; } return []; @@ -261,7 +261,7 @@ export default { } }) if (dump.length > 0) { - console.log('push ' + dump.length + ' elems in', target, dump); + //console.log('push ' + dump.length + ' elems in', target, dump); } }, @@ -284,7 +284,7 @@ export default { } }) if (dump.length > 0) { - console.log('remove ' + dump.length + ' elems from ' + target + ' options', dump); + //console.log('remove ' + dump.length + ' elems from ' + target + ' options', dump); } return [ data.options, data.value ]; }, @@ -309,7 +309,7 @@ export default { } }); if (dump.length > 0) { - console.log('add ' + dump.length + ' selected elems in', target, dump); + //console.log('add ' + dump.length + ' selected elems in', target, dump); } }, @@ -325,7 +325,7 @@ export default { // remove from selected data.value = data.value.filter(e => e.id !== elem.id); - console.log('remove ' + elem.id + ' from selected ' + target); + //console.log('remove ' + elem.id + ' from selected ' + target); // remove from hiddenField this.rebuildHiddenFieldValues(target); @@ -343,9 +343,9 @@ export default { */ unselectToNextField(target, elem) { if (target === 'goals') { - console.log('!!!! target is goal: unselect goal', elem.id); + //console.log('!!!! target is goal: unselect goal', elem.id); this.unselectGoal(elem); - console.log('!!!! done'); + //console.log('!!!! done'); } }, @@ -355,12 +355,12 @@ export default { */ rebuildHiddenFieldValues(target) { let data = this[target]; - console.log('rebuild hiddenFields ' + target + ' values :'); + //console.log('rebuild hiddenFields ' + target + ' values :'); data.hiddenField.value = ''; // reset data.value.forEach(elem => { data.hiddenField.value = this.addIdToValue(data.hiddenField.value, elem.id); }) - console.log(data.hiddenField); + //console.log(data.hiddenField); }, addIdToValue(string, id) { From fb60808dcad322515384018a85e69912b8b69411 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 14 Sep 2022 17:28:03 +0200 Subject: [PATCH 034/120] export translations: improve title translations in filter/aggrs stack --- .../Export/LinkedToPerson/CountActivity.php | 2 +- .../PersonHavingActivityBetweenDateFilter.php | 2 +- .../PersonFilters/DeadOrAliveFilter.php | 2 +- .../ResidentialAddressAtThirdpartyFilter.php | 2 +- .../ResidentialAddressAtUserFilter.php | 4 +- .../translations/messages.fr.yml | 91 ++++++++++--------- 6 files changed, 55 insertions(+), 48 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php index 398fb14bb..9f4d15ec7 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php @@ -109,7 +109,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface return [ Declarations::ACTIVITY, Declarations::ACTIVITY_PERSON, - //PersonDeclarations::PERSON_TYPE, + PersonDeclarations::PERSON_TYPE, ]; } } diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilter.php index 8db312e35..871271aaa 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilter.php @@ -197,7 +197,7 @@ class PersonHavingActivityBetweenDateFilter implements ExportElementValidatedInt public function getTitle() { - return 'Filtered by person having an activity in a period'; + return 'Filter by person having an activity in a period'; } public function validateForm($data, ExecutionContextInterface $context) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/DeadOrAliveFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/DeadOrAliveFilter.php index 00d05117b..a161df44f 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/DeadOrAliveFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/DeadOrAliveFilter.php @@ -108,6 +108,6 @@ class DeadOrAliveFilter implements FilterInterface public function getTitle() { - return 'Filtered by person\'s that are alive or have deceased at a certain date'; + return "Filter by person's that are alive or have deceased at a certain date"; } } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php index c38ce6a5b..c96fd680f 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php @@ -106,6 +106,6 @@ class ResidentialAddressAtThirdpartyFilter implements FilterInterface public function getTitle() { - return 'Filtered by person\'s who have a residential address located at a thirdparty of type'; + return "Filter by person's who have a residential address located at a thirdparty of type"; } } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php index 48275eb12..482312143 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php @@ -59,11 +59,11 @@ class ResidentialAddressAtUserFilter implements FilterInterface public function describeAction($data, $format = 'string') { - return ['Filtered by person\'s who have a residential address located at another user']; + return ["Filtered by person's who have a residential address located at another user"]; } public function getTitle() { - return 'Filtered by person\'s who have a residential address located at another user'; + return "Filter by person's who have a residential address located at another user"; } } diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 05e9a82a0..a81fb7e86 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -86,8 +86,8 @@ Civility: Civilité choose civility: -- All genders: tous les genres Any person selected: Aucune personne sélectionnée -Create a household and add an address: Ajouter une adresse pour un usager non suivi et seul dans un ménage -A new household will be created. The person will be member of this household.: Un nouveau ménage va être créé. L'usager sera membre de ce ménage. +Create a household and add an address: Ajouter une adresse pour une personne non suivie et seule dans un ménage +A new household will be created. The person will be member of this household.: Un nouveau ménage va être créé. La personne sera membre de ce ménage. Comment on the gender: Commentaire sur le genre # dédoublonnage @@ -127,8 +127,8 @@ address_country_code: Code pays 'Alreay existing person': 'Dossiers déjà encodés' 'Add the person': 'Ajouter la personne' -'Add the person and create an accompanying period': "Créer l'usager ET créer une période d'accompagnement" -'Add the person and create a household': "Créer l'usager ET créer un ménage" +'Add the person and create an accompanying period': "Créer la personne ET créer une période d'accompagnement" +'Add the person and create a household': "Créer la personne ET créer un ménage" Show person: Voir le dossier de la personne 'Confirm the creation': 'Confirmer la création' 'You will create this person': 'Vous allez créer le dossier suivant' @@ -200,7 +200,7 @@ Participants: Personnes impliquées Create an accompanying course: Créer un parcours Accompanying courses of users: Parcours des utilisateurs This accompanying course is still a draft: Ce parcours est encore à l'état brouillon. -Associated peoples: Usagers concernés +Associated peoples: Personnes concernées Resources: Interlocuteurs privilégiés Any requestor to this accompanying course: Aucun demandeur pour ce parcours Social action: Action d'accompagnement @@ -217,7 +217,7 @@ See this period: Voir cette période Requestor: Demandeur No requestor: Pas de demandeur No resources: "Pas d'interlocuteurs privilégiés" -Persons associated: Usagers concernés +Persons associated: Personnes concernés Referrer: Référent Referrers: Agents traitants Some peoples does not belong to any household currently. Add them to an household soon: Certaines personnes n'appartiennent à aucun ménage actuellement. Renseignez leur ménage dès que possible. @@ -244,7 +244,7 @@ List of resources: "Liste des ressources" There are no available resources: "Aucun ressource" no comment found: "Aucun commentaire" Select a type: "Choisissez un type" -Select a person: "Choisissez un usager" +Select a person: "Choisissez une personne" Select a thirdparty: "Choisissez un tiers" Contact person: "Personne de contact" Kind: "Type" @@ -276,11 +276,11 @@ Which kind of residential address would you create ?: Quel type d'adresse de ré The address of another person: L'adresse d'une autre personne The address of a third party: L'adresse d'un tiers A new address: Une nouvelle adresse -residential_address_person_explanation: L'adresse sera positionnée auprès d'un usager. Lorsque l'usager déménage, l'adresse de résidence suivra également cet usager +residential_address_person_explanation: L'adresse sera positionnée auprès d'une personne. Lorsque la personne déménage, l'adresse de résidence suivra également cette personne residential_address_third_party_explanation: L'adresse sera associée à celle d'un tiers. residential_address_new_address_explanation: Créer une nouvelle adresse. L'adresse sera fixe. New residential address: Nouvelle adresse de résidence -Host person: Choisir l'adresse d'un usager +Host person: Choisir l'adresse d'une personne Host third party: Choisir l'adresse d'un tiers The new residential address was created successfully: La nouvelle adresse de résidence a été créée Edit a residential address: Modifier l'addresse de résidence @@ -330,16 +330,16 @@ Accompanyied people: Personnes accompagnées ## exports Exports of persons: Exports des personnes -Count people by various parameters.: Compte le nombre d'usagers en fonction de différents filtres. -Count people: Nombre d'usagers +Count people by various parameters.: Compte le nombre de personnes en fonction de différents filtres. +Count people: Nombre de personnes List peoples: Liste des personnes Create a list of people according to various filters.: Crée une liste des personnes selon différents filtres. Fields to include in export: Champs à inclure dans l'export Address valid at this date: Addresse valide à cette date List duplicates: Liste des doublons Create a list of duplicate people: Créer la liste des personnes détectées comme doublons. -Count people participating in an accompanying course by various parameters.: Nombre d'usagers concernées par un parcours -Count people participating in an accompanying course: Nombre d'usagers concernés par un parcours +Count people participating in an accompanying course by various parameters.: Nombre de personnes concernées par un parcours +Count people participating in an accompanying course: Nombre de personnes concernés par un parcours Exports of accompanying courses: Exports des parcours d'accompagnement Count accompanying courses: Nombre de parcours @@ -362,72 +362,77 @@ Count households: Nombre de ménages Count household by various parameters.: Compte le nombre de ménages impliqués dans un parcours selon différents filtres. ## persons filters -Filter by person gender: Filtrer par genre de la personne +Filter by person gender: Filtrer les personnes par genre Accepted genders: Genres acceptés 'Filtering by genders: only %genders%': 'Filtré par genre: seulement %genders%' -Filter by person's nationality: Filtrer par nationalité +Filter by person's nationality: Filtrer les personnes par nationalité Nationalities: Nationalités Choose countries: Choisir les nationalités 'Filtered by nationality : %nationalities%': 'Filtré par nationalité : seulement %nationalities%' -Filter by person's birthdate: Filtrer par date de naissance de la personne +Filter by person's birthdate: Filtrer les personnes par date de naissance Born after this date: Nés après cette date Born before this date: Nés avant cette date This field should not be empty: Ce champ ne peut pas être vide This date should be after the date given in "born after" field: Cette date doit être après la date donnée du le champ "nés après le" "Filtered by person's birthdate: between %date_from% and %date_to%": "Filtré par date de naissance de la personne: uniquement nés entre le %date_from% et %date_to%" -Filter by person's deathdate: Filtrer par date de décès de la personne +Filter by person's deathdate: Filtrer les personnes par date de décès "Filtered by person's deathdate: between %date_from% and %date_to%": "Filtré par date de naissance de la personne: uniquement nés entre le %date_from% et %date_to%" Death after this date: Décédé après cette date Deathdate before: Décédé avant cette date - Alive: Vivant Deceased: Décédé Filter in relation to this date: Filtrer par rapport à cette date "Filtered by a state of %deadOrAlive% at this date %date_calc%": Filtré par personnes qui sont %deadOrAlive% à cette date %date_calc% -Filter by person's age: Filtrer par âge de la personne +Filter by person's age: Filtrer les personnes par age "Filtered by person's age: between %min_age% and %max_age%": "Filtré par âge de la personne entre %min_age% et %max_age%" Minimum age: Âge minimum Maximum age: Âge maximum The minimum age should be less than the maximum age.: L'âge minimum doit être plus bas que l'âge maximum. Date during which residential address was valid: Date de validité -Filtered by person\'s who have a residential address located at a thirdparty of type %thirparty_type%: Uniquement les usagers qui ont une addresse de résidence chez un tiers de catégorie %thirdparty_type% +Filtered by person\'s who have a residential address located at a thirdparty of type %thirparty_type%: Uniquement les personnes qui ont une addresse de résidence chez un tiers de catégorie %thirdparty_type% Family composition: Composition familiale Family composition at this time: Composition familiale à cette date. +Filter by person's marital status: Filtrer les personnes par état matrimonial Filtered by person's marital status: Filtré par état matrimonial -Filter by person's marital status: Filtrer par état matrimonial Marital status at this time: État matrimonial par rapport à cette date -Filter by entrusted child status: Filtrer les usagers qui sont "enfant confié" -Filtered by entrusted child status: Uniquement les usagers qui sont "enfant confié" +Filter by entrusted child status: Filtrer les personnes "enfant confié" +Filtered by entrusted child status: Uniquement les personnes qui sont "enfant confié" -Filter by nomadic status: Filtrer les usagers qui sont "gens de voyage" -Filtered by nomadic status: Uniquement les usagers qui sont "gens de voyage" +Filter by nomadic status: Filtrer les personnes "gens du voyage" +Filtered by nomadic status: Uniquement les personnes qui sont "gens du voyage" -Filtered by person's who have a residential address located at another user: Uniquement les usagers qui ont une addresse de résidence chez un autre usager +"Filter by person's who have a residential address located at another user": Filtrer les personnes qui ont une addresse de résidence chez une autre personne +"Filtered by person's who have a residential address located at another user": Uniquement les personnes qui ont une addresse de résidence chez une autre personne -Filtered by person's that are alive or have deceased at a certain date: Filtrer par usagers qui sont décédé ou vivant à une certaine date +Filter by person's that are alive or have deceased at a certain date: Filtrer les personnes qui sont décédées ou vivantes à une certaine date +Filtered by person's that are alive or have deceased at a certain date: Uniquement les personnes qui sont décédées ou vivantes à une certaine date -"Filter by accompanying period: active period": "Filtrer par période d'accompagnement: en file active" +"Filter by accompanying period: active period": "Filtrer les personnes par période d'accompagnement: en file active" Having an accompanying period opened after this date: Ayant une période d'accompagnement ouverte après cette date Having an accompanying period ending before this date, or still opened at this date: Ayant une période d'accompagnement fermée après cette date, ou toujours ouverte à cette date "Filtered by accompanying period: persons having an accompanying period opened after the %date_from% and closed before the %date_to% (or still opened at the %date_to%)": "Filtré par période d'accompagnement: personnes ayant une période d'accompagnement ouverte après le %date_from%, et cloturée le %date_to% (ou toujours ouverte le %date_to%)" -"Filter by accompanying period: starting between two dates": "Filtrer par période d'accompagnement: début de la période entre deux dates" +"Filter by accompanying period: starting between two dates": "Filtrer les personnes par période d'accompagnement: début de la période entre deux dates" "Having an accompanying period opened before this date": "Ayant une période d'accompagnement ouverte avant cette date" "Filtered by accompanying period: persons having an accompanying period opened between the %date_from% and %date_to%": "Filtrer par période d'accompagnement: ayant une période ouverte entre le %date_from% et le %date_to%" -"Filter by accompanying period: closed between two dates": "Filtrer par période d'accompagnement: période fermée entre deux dates" +"Filter by accompanying period: closed between two dates": "Filtrer les personnes par période d'accompagnement: période fermée entre deux dates" Having an accompanying period closed after this date: Ayant une période d'accompagnement fermée après cette date "Having an accompanying period closed before this date": "Ayant une période d'accompagnement fermée avant cette date" "Filtered by accompanying period: persons having an accompanying period closed between the %date_from% and %date_to%": "Filtrer par période d'accompagnement: ayant une période fermée entre le %date_from% et le %date_to%" +Filter by person having an activity in a period: Filtrer les personnes ayant eu une échange pendant la période donnée +Filtered by person having an activity between %date_from% and %date_to% with reasons %reasons_name%: Uniquement les personnes associées à une échange entre %date_from% et %date_to% avec les sujets %reasons_name% + + ## accompanying course filters/aggr Filter by user scope: Filtrer les parcours par service du référent "Filtered by user main scope: only %scope%": "Filtré par service du référent: uniquement %scope%" @@ -486,13 +491,13 @@ Administrative location: Localisation administrative "Filtered by administratives locations: only %locations%": "Filtré par localisation administrative: uniquement %locations%" Group by administrative location: Grouper les parcours par localisation administrative -Filter by requestor: Filtrer les parcours selon la présence du demandeur au sein des usagers concernés +Filter by requestor: Filtrer les parcours selon la présence du demandeur au sein des personnes concernées Accepted choices: '' -is person concerned: Le demandeur est un usager concerné -is other person: Le demandeur est un usager, mais n'est pas concerné +is person concerned: Le demandeur est une personne concernée +is other person: Le demandeur est une personne, mais n'est pas concernée is thirdparty: Le demandeur est un tiers no requestor: Le parcours ne comporte pas de demandeur -"Filtered by requestor: only %choice%": "Filtré par présence du demandeur au sein des usagers concernés: uniquement si %choice%" +"Filtered by requestor: only %choice%": "Filtré par présence du demandeur au sein des personnes concernées: uniquement si %choice%" Group by requestor: Grouper les parcours selon la nature du demandeur Filter by confidential: Filtrer les parcours par confidentialité @@ -592,13 +597,15 @@ Group by country: Grouper par pays Group people by gender: Grouper les personnes par genre Group people by their professional situation: Grouper les personnes par situation professionelle Group people by marital status: Grouper les personnes par état matrimonial -Aggregate by household position: Grouper par position dans le ménage + +Aggregate by household position: Grouper les personnes par position dans le ménage Household position in relation to this date: Position dans le ménage par rapport à cette date Household position: Position dans le ménage -Filtered by person's who have a residential address located at a thirdparty of type: Uniquement les usagers qui ont une addresse de résidence chez un tiers de catégorie "xxx" -"Filtered by person's who have a residential address located at a thirdparty of type %thirdparty_type% and valid on %date_calc%": "Uniquement les usagers qui ont une addresse de résidence chez un tiers de catégorie %thirdparty_type% et valide sur la date %date_calc%" -Aggregate by age: Grouper par âge +Filter by person's who have a residential address located at a thirdparty of type: Filtrer les personnes qui ont une addresse de résidence chez un tiers de catégorie "xxx" +"Filtered by person's who have a residential address located at a thirdparty of type %thirdparty_type% and valid on %date_calc%": "Uniquement les personnes qui ont une addresse de résidence chez un tiers de catégorie %thirdparty_type% et valide sur la date %date_calc%" + +Aggregate by age: Grouper les personnes par âge Calculate age in relation to this date: Calculer l'âge par rapport à cette date Group people by country of birth: Grouper les personnes par pays de naissance @@ -765,7 +772,7 @@ This course is located at a temporarily address. You should locate this course t Accompanying course location: Localisation du parcours This course is located by: Localisé auprès de This course has a temporarily location: Localisation temporaire -Choose a person to locate by: Localiser auprès d'un usager concerné +Choose a person to locate by: Localiser auprès d'une personne concernée Associate at least one member with an household, and set an address to this household: Associez au moins un membre du parcours à un ménage, et indiquez une adresse à ce ménage. Locate by: Localiser auprès de fix it: Compléter @@ -850,7 +857,7 @@ Person addresses: Adresses de résidence Household addresses: Adresses de domicile Insert an address: Insérer une adresse see social issues: Voir les problématiques sociales -see persons associated: Voir les usagers concernés +see persons associated: Voir les personnes concernées docgen: Accompanying Period basic: "Parcours d'accompagnement (basique)" @@ -871,10 +878,10 @@ docgen: period_notification: period_designated_subject: Vous êtes référent d'un parcours d'accompagnement You are designated to a new period: Vous avez été désigné référent d'un parcours d'accompagnement. - Persons are: Les usagers concernés sont les suivants + Persons are: Les personnes concernées sont les suivantes Social issues are: Les problématiques sociales renseignées sont les suivantes See it online: Visualisez le parcours en ligne - Person locating period has moved: L'usager qui localise un parcours a déménagé + Person locating period has moved: La personne qui localise un parcours a déménagé You are getting a notification for a period which does not exists any more: Cette notification ne correspond pas à une période d'accompagnement valide. You are getting a notification for a period you are not allowed to see: La notification fait référence à une période d'accompagnement à laquelle vous n'avez pas accès. From 5dcd157bd061a14469a6fff14bbc64a8c10082d5 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 14 Sep 2022 18:12:29 +0200 Subject: [PATCH 035/120] export: move Vue component in ChillPersonBundle --- src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig | 2 +- src/Bundle/ChillMainBundle/chill.webpack.config.js | 1 - .../Resources/public/vuejs/ExportFormActionGoalResult}/App.vue | 0 .../Resources/public/vuejs/ExportFormActionGoalResult}/api.js | 0 .../Resources/public/vuejs/ExportFormActionGoalResult}/index.js | 0 src/Bundle/ChillPersonBundle/chill.webpack.config.js | 1 + 6 files changed, 2 insertions(+), 2 deletions(-) rename src/Bundle/{ChillMainBundle/Resources/public/vuejs/FormActionGoalResult => ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult}/App.vue (100%) rename src/Bundle/{ChillMainBundle/Resources/public/vuejs/FormActionGoalResult => ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult}/api.js (100%) rename src/Bundle/{ChillMainBundle/Resources/public/vuejs/FormActionGoalResult => ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult}/index.js (100%) diff --git a/src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig index c6adb241d..ad49d9aa6 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Export/new.html.twig @@ -23,7 +23,7 @@ {% block js %} {{ encore_entry_script_tags('page_export') }} {% if export_alias == 'count_social_work_actions' %} - {{ encore_entry_script_tags('vue_form_action_goal_result') }} + {{ encore_entry_script_tags('vue_export_action_goal_result') }} {% endif %} {% endblock js %} diff --git a/src/Bundle/ChillMainBundle/chill.webpack.config.js b/src/Bundle/ChillMainBundle/chill.webpack.config.js index c4c445d31..628f04ba5 100644 --- a/src/Bundle/ChillMainBundle/chill.webpack.config.js +++ b/src/Bundle/ChillMainBundle/chill.webpack.config.js @@ -74,5 +74,4 @@ module.exports = function(encore, entries) // Vue entrypoints encore.addEntry('vue_address', __dirname + '/Resources/public/vuejs/Address/index.js'); encore.addEntry('vue_onthefly', __dirname + '/Resources/public/vuejs/OnTheFly/index.js'); - encore.addEntry('vue_form_action_goal_result', __dirname + '/Resources/public/vuejs/FormActionGoalResult/index.js'); }; diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult/App.vue similarity index 100% rename from src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/App.vue rename to src/Bundle/ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult/App.vue diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/api.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult/api.js similarity index 100% rename from src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/api.js rename to src/Bundle/ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult/api.js diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/index.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult/index.js similarity index 100% rename from src/Bundle/ChillMainBundle/Resources/public/vuejs/FormActionGoalResult/index.js rename to src/Bundle/ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult/index.js diff --git a/src/Bundle/ChillPersonBundle/chill.webpack.config.js b/src/Bundle/ChillPersonBundle/chill.webpack.config.js index 85ee83c93..a445959e5 100644 --- a/src/Bundle/ChillPersonBundle/chill.webpack.config.js +++ b/src/Bundle/ChillPersonBundle/chill.webpack.config.js @@ -13,6 +13,7 @@ module.exports = function(encore, entries) encore.addEntry('vue_accourse_work_create', __dirname + '/Resources/public/vuejs/AccompanyingCourseWorkCreate/index.js'); encore.addEntry('vue_accourse_work_edit', __dirname + '/Resources/public/vuejs/AccompanyingCourseWorkEdit/index.js'); encore.addEntry('vue_visgraph', __dirname + '/Resources/public/vuejs/VisGraph/index.js'); + encore.addEntry('vue_export_action_goal_result', __dirname + '/Resources/public/vuejs/ExportFormActionGoalResult/index.js'); encore.addEntry('mod_set_referrer', __dirname + '/Resources/public/mod/AccompanyingPeriod/setReferrer.js'); From 91a5db4c14bea289d80edaea8656e612ee720e7f Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Thu, 15 Sep 2022 16:07:00 +0200 Subject: [PATCH 036/120] fix origin alias in qb --- .../AccompanyingCourseAggregators/OriginAggregator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregator.php index 4c784e08d..925160a2d 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregator.php @@ -45,7 +45,7 @@ final class OriginAggregator implements AggregatorInterface $qb->join('acp.origin', 'acporigin'); } - $qb->addSelect('o.id AS origin_aggregator'); + $qb->addSelect('acporigin.id AS origin_aggregator'); $groupby = $qb->getDQLPart('groupBy'); From 211a80e9beba3ee1f97a6c0b719edb696f230277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 8 Sep 2022 13:46:24 +0200 Subject: [PATCH 037/120] deprecate chill prophecy train in favor of prophecy-phpunit bridge --- src/Bundle/ChillMainBundle/Test/ProphecyTrait.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Bundle/ChillMainBundle/Test/ProphecyTrait.php b/src/Bundle/ChillMainBundle/Test/ProphecyTrait.php index a25624719..ae274ca1d 100644 --- a/src/Bundle/ChillMainBundle/Test/ProphecyTrait.php +++ b/src/Bundle/ChillMainBundle/Test/ProphecyTrait.php @@ -18,6 +18,7 @@ namespace Chill\MainBundle\Test; * and use tearDownTrait after usage. * * @codeCoverageIgnore + * @deprecated use @class{Prophecy\PhpUnit\ProphecyTrait} instead */ trait ProphecyTrait { From e379d8adb5c94eb617cff0cd3fa6a2c19ef558bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 8 Sep 2022 13:47:35 +0200 Subject: [PATCH 038/120] [feature] use internal services to check for acl on exports --- .../Controller/ExportController.php | 5 +- .../ChillMainBundle/Export/ExportManager.php | 56 ++++++------------- .../Repository/CenterRepository.php | 12 ++++ .../Authorization/ChillExportVoter.php | 17 +++--- .../ChillMainBundle/config/services.yaml | 8 +-- 5 files changed, 42 insertions(+), 56 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Controller/ExportController.php b/src/Bundle/ChillMainBundle/Controller/ExportController.php index 1893a64b3..ffd73b777 100644 --- a/src/Bundle/ChillMainBundle/Controller/ExportController.php +++ b/src/Bundle/ChillMainBundle/Controller/ExportController.php @@ -23,6 +23,7 @@ use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Contracts\Translation\TranslatorInterface; @@ -142,10 +143,8 @@ class ExportController extends AbstractController /** * Render the list of available exports. - * - * @return \Symfony\Component\HttpFoundation\Response */ - public function indexAction(Request $request) + public function indexAction(): Response { $exportManager = $this->exportManager; diff --git a/src/Bundle/ChillMainBundle/Export/ExportManager.php b/src/Bundle/ChillMainBundle/Export/ExportManager.php index e2d099ba8..c1384a3b8 100644 --- a/src/Bundle/ChillMainBundle/Export/ExportManager.php +++ b/src/Bundle/ChillMainBundle/Export/ExportManager.php @@ -14,6 +14,7 @@ namespace Chill\MainBundle\Export; use Chill\MainBundle\Form\Type\Export\ExportType; use Chill\MainBundle\Form\Type\Export\PickCenterType; use Chill\MainBundle\Security\Authorization\AuthorizationHelper; +use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\QueryBuilder; use Generator; @@ -42,52 +43,38 @@ class ExportManager /** * The collected aggregators, injected by DI. * - * @var AggregatorInterface[] + * @var array|AggregatorInterface[] */ - private $aggregators = []; + private array $aggregators = []; - /** - * @var AuthorizationChecker - */ - private $authorizationChecker; + private AuthorizationCheckerInterface $authorizationChecker; - /** - * @var AuthorizationHelper - */ - private $authorizationHelper; + private AuthorizationHelperInterface $authorizationHelper; - /** - * @var EntityManagerInterface - */ - private $em; + private EntityManagerInterface $em; /** * Collected Exports, injected by DI. * - * @var ExportInterface[] + * @var array|ExportInterface[] */ - private $exports = []; + private array $exports = []; /** * The collected filters, injected by DI. * - * @var FilterInterface[] + * @var array|FilterInterface[] */ - private $filters = []; + private array $filters = []; /** * Collected Formatters, injected by DI. * - * @var FormatterInterface[] + * @var array|FormatterInterface[] */ - private $formatters = []; + private array $formatters = []; - /** - * a logger. - * - * @var LoggerInterface - */ - private $logger; + private LoggerInterface $logger; /** * @var \Symfony\Component\Security\Core\User\UserInterface @@ -98,7 +85,7 @@ class ExportManager LoggerInterface $logger, EntityManagerInterface $em, AuthorizationCheckerInterface $authorizationChecker, - AuthorizationHelper $authorizationHelper, + AuthorizationHelperInterface $authorizationHelper, TokenStorageInterface $tokenStorage ) { $this->logger = $logger; @@ -547,19 +534,16 @@ class ExportManager . 'an ExportInterface.'); } - if (null === $centers) { - $centers = $this->authorizationHelper->getReachableCenters( + if (null === $centers || [] === $centers) { + // we want to try if at least one center is reachable + return [] !== $this->authorizationHelper->getReachableCenters( $this->user, $role ); } - if (count($centers) === 0) { - return false; - } - foreach ($centers as $center) { - if ($this->authorizationChecker->isGranted($role, $center) === false) { + if (false === $this->authorizationChecker->isGranted($role, $center)) { //debugging $this->logger->debug('user has no access to element', [ 'method' => __METHOD__, @@ -568,10 +552,6 @@ class ExportManager 'role' => $role, ]); - ///// Bypasse les autorisations qui empêche d'afficher les nouveaux exports - return true; - ///// TODO supprimer le return true - return false; } } diff --git a/src/Bundle/ChillMainBundle/Repository/CenterRepository.php b/src/Bundle/ChillMainBundle/Repository/CenterRepository.php index 554f39880..e8f6f4fa3 100644 --- a/src/Bundle/ChillMainBundle/Repository/CenterRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/CenterRepository.php @@ -30,6 +30,18 @@ final class CenterRepository implements ObjectRepository return $this->repository->find($id, $lockMode, $lockVersion); } + /** + * Return all active centers + * + * Note: this is a teaser: active will comes later on center entity + * + * @return Center[] + */ + public function findActive(): array + { + return $this->findAll(); + } + /** * @return Center[] */ diff --git a/src/Bundle/ChillMainBundle/Security/Authorization/ChillExportVoter.php b/src/Bundle/ChillMainBundle/Security/Authorization/ChillExportVoter.php index ec1a0479d..b98564adf 100644 --- a/src/Bundle/ChillMainBundle/Security/Authorization/ChillExportVoter.php +++ b/src/Bundle/ChillMainBundle/Security/Authorization/ChillExportVoter.php @@ -19,24 +19,23 @@ class ChillExportVoter extends Voter { public const EXPORT = 'chill_export'; - protected AuthorizationHelperInterface $authorizationHelper; + private VoterHelperInterface $helper; - public function __construct(AuthorizationHelperInterface $authorizationHelper) + public function __construct(VoterHelperFactoryInterface $voterHelperFactory) { - $this->authorizationHelper = $authorizationHelper; + $this->helper = $voterHelperFactory + ->generate(self::class) + ->addCheckFor(null, [self::EXPORT]) + ->build(); } protected function supports($attribute, $subject): bool { - return self::EXPORT === $attribute; + return $this->helper->supports($attribute, $subject); } protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool { - if (!$token->getUser() instanceof User) { - return false; - } - - return [] !== $this->authorizationHelper->getReachableCenters($token->getUser(), $attribute); + return $this->helper->voteOnAttribute($attribute, $subject, $token); } } diff --git a/src/Bundle/ChillMainBundle/config/services.yaml b/src/Bundle/ChillMainBundle/config/services.yaml index 6d55532a6..697fd62aa 100644 --- a/src/Bundle/ChillMainBundle/config/services.yaml +++ b/src/Bundle/ChillMainBundle/config/services.yaml @@ -88,12 +88,8 @@ services: - { name: validator.constraint_validator, alias: 'role_scope_scope_presence' } Chill\MainBundle\Export\ExportManager: - arguments: - - "@logger" - - "@doctrine.orm.entity_manager" - - "@security.authorization_checker" - - "@chill.main.security.authorization.helper" - - "@security.token_storage" + autoconfigure: true + autowire: true Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface: '@Chill\MainBundle\Security\Resolver\CenterResolverDispatcher' From 38cb1fe35711c75a9a749eb58e7b37cee010ef93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 9 Sep 2022 10:54:21 +0200 Subject: [PATCH 039/120] [dev-feature] use an interface for describing CenterRepository (allow mocking in tests --- .../Repository/CenterRepository.php | 9 +-------- .../Repository/CenterRepositoryInterface.php | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Repository/CenterRepositoryInterface.php diff --git a/src/Bundle/ChillMainBundle/Repository/CenterRepository.php b/src/Bundle/ChillMainBundle/Repository/CenterRepository.php index e8f6f4fa3..d8e54d1c4 100644 --- a/src/Bundle/ChillMainBundle/Repository/CenterRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/CenterRepository.php @@ -16,7 +16,7 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\Persistence\ObjectRepository; -final class CenterRepository implements ObjectRepository +final class CenterRepository implements CenterRepositoryInterface { private EntityRepository $repository; @@ -30,13 +30,6 @@ final class CenterRepository implements ObjectRepository return $this->repository->find($id, $lockMode, $lockVersion); } - /** - * Return all active centers - * - * Note: this is a teaser: active will comes later on center entity - * - * @return Center[] - */ public function findActive(): array { return $this->findAll(); diff --git a/src/Bundle/ChillMainBundle/Repository/CenterRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/CenterRepositoryInterface.php new file mode 100644 index 000000000..27ba64caf --- /dev/null +++ b/src/Bundle/ChillMainBundle/Repository/CenterRepositoryInterface.php @@ -0,0 +1,18 @@ + Date: Fri, 9 Sep 2022 18:36:13 +0200 Subject: [PATCH 040/120] [FIX] use AuthorizationHelperInterface instead of implementation in PickCenterType --- .../Form/Type/Export/PickCenterType.php | 36 ++++++------------- .../ChillMainBundle/config/services/form.yaml | 8 ++--- 2 files changed, 13 insertions(+), 31 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Form/Type/Export/PickCenterType.php b/src/Bundle/ChillMainBundle/Form/Type/Export/PickCenterType.php index c73402dd3..07776cb71 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/Export/PickCenterType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/Export/PickCenterType.php @@ -15,6 +15,7 @@ use Chill\MainBundle\Center\GroupingCenterInterface; use Chill\MainBundle\Entity\Center; use Chill\MainBundle\Export\ExportManager; use Chill\MainBundle\Security\Authorization\AuthorizationHelper; +use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; use Doctrine\ORM\EntityRepository; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; @@ -24,6 +25,7 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\User\UserInterface; use function array_intersect; use function array_key_exists; use function array_merge; @@ -38,30 +40,24 @@ class PickCenterType extends AbstractType { public const CENTERS_IDENTIFIERS = 'c'; - /** - * @var AuthorizationHelper - */ - protected $authorizationHelper; + protected AuthorizationHelperInterface $authorizationHelper; + + protected ExportManager $exportManager; /** - * @var ExportManager + * @var array|GroupingCenterInterface[] */ - protected $exportManager; - - /** - * @var GroupingCenterInterface[] - */ - protected $groupingCenters = []; + protected array $groupingCenters = []; /** * @var \Symfony\Component\Security\Core\User\UserInterface */ - protected $user; + protected UserInterface $user; public function __construct( TokenStorageInterface $tokenStorage, ExportManager $exportManager, - AuthorizationHelper $authorizationHelper + AuthorizationHelperInterface $authorizationHelper ) { $this->exportManager = $exportManager; $this->user = $tokenStorage->getToken()->getUser(); @@ -78,22 +74,12 @@ class PickCenterType extends AbstractType $export = $this->exportManager->getExport($options['export_alias']); $centers = $this->authorizationHelper->getReachableCenters( $this->user, - (string) $export->requiredRole() + $export->requiredRole() ); $builder->add(self::CENTERS_IDENTIFIERS, EntityType::class, [ 'class' => Center::class, - 'query_builder' => static function (EntityRepository $er) use ($centers) { - $qb = $er->createQueryBuilder('c'); - $ids = array_map( - static function (Center $el) { - return $el->getId(); - }, - $centers - ); - - return $qb->where($qb->expr()->in('c.id', $ids)); - }, + 'choices' => $centers, 'multiple' => true, 'expanded' => true, 'choice_label' => static function (Center $c) { diff --git a/src/Bundle/ChillMainBundle/config/services/form.yaml b/src/Bundle/ChillMainBundle/config/services/form.yaml index 407a1b6af..0a757a8db 100644 --- a/src/Bundle/ChillMainBundle/config/services/form.yaml +++ b/src/Bundle/ChillMainBundle/config/services/form.yaml @@ -81,12 +81,8 @@ services: chill.main.form.pick_centers_type: class: Chill\MainBundle\Form\Type\Export\PickCenterType - arguments: - - "@security.token_storage" - - '@Chill\MainBundle\Export\ExportManager' - - "@chill.main.security.authorization.helper" - tags: - - { name: form.type } + autowire: true + autoconfigure: true chill.main.form.formatter_type: class: Chill\MainBundle\Form\Type\Export\FormatterType From 78ea990189d8229150194c36dfbce55ee2708a1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 9 Sep 2022 18:36:46 +0200 Subject: [PATCH 041/120] allow voter to handle export about Accompanying periods on Center --- .../Security/Authorization/AccompanyingPeriodVoter.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php b/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php index 8da2036d7..b9c9fb219 100644 --- a/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php +++ b/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Security\Authorization; +use Chill\MainBundle\Entity\Center; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Security\Authorization\AbstractChillVoter; use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface; @@ -119,6 +120,7 @@ class AccompanyingPeriodVoter extends AbstractChillVoter implements ProvideRoleH ->addCheckFor(null, [self::CREATE, self::REASSIGN_BULK]) ->addCheckFor(AccompanyingPeriod::class, [self::TOGGLE_CONFIDENTIAL, ...self::ALL]) ->addCheckFor(Person::class, [self::SEE, self::CREATE]) + ->addCheckFor(Center::class, [self::STATS]) ->build(); } From d716e0c2c276463c7042a11cbc2bbf2c516d1615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sun, 11 Sep 2022 22:44:09 +0200 Subject: [PATCH 042/120] add missing roles and adapt role voter for exports houshold and activity --- .../Authorization/ActivityStatsVoter.php | 40 ++++++------------- .../Security/Authorization/HouseholdVoter.php | 38 ++++++++++++++++-- .../translations/messages.fr.yml | 1 + 3 files changed, 47 insertions(+), 32 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Security/Authorization/ActivityStatsVoter.php b/src/Bundle/ChillActivityBundle/Security/Authorization/ActivityStatsVoter.php index 789e634e5..48448d5e3 100644 --- a/src/Bundle/ChillActivityBundle/Security/Authorization/ActivityStatsVoter.php +++ b/src/Bundle/ChillActivityBundle/Security/Authorization/ActivityStatsVoter.php @@ -13,10 +13,10 @@ namespace Chill\ActivityBundle\Security\Authorization; use Chill\MainBundle\Entity\Center; use Chill\MainBundle\Security\Authorization\AbstractChillVoter; -use Chill\MainBundle\Security\Authorization\AuthorizationHelper; +use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface; +use Chill\MainBundle\Security\Authorization\VoterHelperInterface; use Chill\MainBundle\Security\ProvideRoleHierarchyInterface; - -use function in_array; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; class ActivityStatsVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface { @@ -24,14 +24,14 @@ class ActivityStatsVoter extends AbstractChillVoter implements ProvideRoleHierar public const STATS = 'CHILL_ACTIVITY_STATS'; - /** - * @var AuthorizationHelper - */ - protected $helper; + protected VoterHelperInterface $helper; - public function __construct(AuthorizationHelper $helper) + public function __construct(VoterHelperFactoryInterface $voterHelperFactory) { - $this->helper = $helper; + $this->helper = $voterHelperFactory + ->generate(self::class) + ->addCheckFor(Center::class, [self::STATS, self::LISTS]) + ->build(); } public function getRoles(): array @@ -49,30 +49,14 @@ class ActivityStatsVoter extends AbstractChillVoter implements ProvideRoleHierar return $this->getAttributes(); } - protected function getSupportedClasses() + protected function voteOnAttribute($attribute, $subject, TokenInterface $token) { - return [Center::class]; - } - - protected function isGranted($attribute, $object, $user = null) - { - if (!$user instanceof \Symfony\Component\Security\Core\User\UserInterface) { - return false; - } - - return $this->helper->userHasAccess($user, $object, $attribute); + return $this->helper->voteOnAttribute($attribute, $subject, $token); } protected function supports($attribute, $subject) { - if ( - $subject instanceof Center - && in_array($attribute, $this->getAttributes(), true) - ) { - return true; - } - - return false; + return $this->helper->supports($attribute, $subject); } private function getAttributes() diff --git a/src/Bundle/ChillPersonBundle/Security/Authorization/HouseholdVoter.php b/src/Bundle/ChillPersonBundle/Security/Authorization/HouseholdVoter.php index ca956db63..0288c9e61 100644 --- a/src/Bundle/ChillPersonBundle/Security/Authorization/HouseholdVoter.php +++ b/src/Bundle/ChillPersonBundle/Security/Authorization/HouseholdVoter.php @@ -11,6 +11,10 @@ declare(strict_types=1); namespace Chill\PersonBundle\Security\Authorization; +use Chill\MainBundle\Entity\Center; +use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface; +use Chill\MainBundle\Security\Authorization\VoterHelperInterface; +use Chill\MainBundle\Security\ProvideRoleHierarchyInterface; use Chill\PersonBundle\Entity\Household\Household; use Chill\PersonBundle\Entity\Household\HouseholdMember; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; @@ -19,7 +23,7 @@ use Symfony\Component\Security\Core\Security; use UnexpectedValueException; use function in_array; -class HouseholdVoter extends Voter +class HouseholdVoter extends Voter implements ProvideRoleHierarchyInterface { public const EDIT = 'CHILL_PERSON_HOUSEHOLD_EDIT'; @@ -36,17 +40,40 @@ class HouseholdVoter extends Voter self::EDIT, self::SEE, ]; + private VoterHelperInterface $helper; + private Security $security; - public function __construct(Security $security) + public function __construct(Security $security, VoterHelperFactoryInterface $voterHelperFactory) { $this->security = $security; + $this->helper = $voterHelperFactory + ->generate(self::class) + ->addCheckFor(Center::class, [self::STATS]) + ->build(); + } + + public function getRolesWithHierarchy(): array + { + return [ 'Person' => $this->getRoles() ]; + } + + public function getRoles(): array + { + return [self::STATS]; + } + + public function getRolesWithoutScope(): array + { + return $this->getRoles(); } protected function supports($attribute, $subject) { - return $subject instanceof Household - && in_array($attribute, self::ALL, true); + return ($subject instanceof Household + && in_array($attribute, self::ALL, true)) + || $this->helper->supports($attribute, $subject) + ; } protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool @@ -58,6 +85,9 @@ class HouseholdVoter extends Voter case self::EDIT: return $this->checkAssociatedMembersRole($subject, PersonVoter::UPDATE); + case self::STATS: + return $this->voteOnAttribute($attribute, $subject, $token); + default: throw new UnexpectedValueException('attribute not supported'); } diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index a81fb7e86..dc53f4839 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -318,6 +318,7 @@ CHILL_PERSON_ACCOMPANYING_PERIOD_FULL: Voir les détails, créer, supprimer et m CHILL_PERSON_ACCOMPANYING_COURSE_REASSIGN_BULK: Réassigner les parcours en lot CHILL_PERSON_ACCOMPANYING_PERIOD_SEE_DETAILS: Voir les détails d'une période d'accompagnement CHILL_PERSON_ACCOMPANYING_PERIOD_STATS: Statistiques sur les parcours d'accompagnement +CHILL_PERSON_HOUSEHOLD_STATS: Statistiques sur les ménages #period Period closed!: Période clôturée! From 62ff4998a0a4c777b9b54e80c0af49b27d93c551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 29 Jul 2022 22:53:40 +0200 Subject: [PATCH 043/120] Fixed: annotation schema for ManyToMany relationship between Evaluation and SocialAction Before this commit, the owning side of the relationship between Evaluation and SocialAction was declared twice. --- src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php index e9c7496ff..131cc4bac 100644 --- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php +++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php @@ -49,9 +49,8 @@ class Evaluation /** * @ORM\ManyToMany( * targetEntity=SocialAction::class, - * inversedBy="evaluations" + * mappedBy="evaluations" * ) - * @ORM\JoinTable(name="chill_person_social_work_evaluation_action") */ private Collection $socialActions; From a9b354a6f5aee2889bab2e3aa612823bce946e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sat, 30 Jul 2022 01:59:32 +0200 Subject: [PATCH 044/120] Feature: add constraint to ensure postal code uniqueness and track creation and update of postal code --- .../ChillMainBundle/Entity/PostalCode.php | 20 ++++++- .../migrations/Version20220729205416.php | 53 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/Bundle/ChillMainBundle/migrations/Version20220729205416.php diff --git a/src/Bundle/ChillMainBundle/Entity/PostalCode.php b/src/Bundle/ChillMainBundle/Entity/PostalCode.php index 4b79f58e8..769f6dfd7 100644 --- a/src/Bundle/ChillMainBundle/Entity/PostalCode.php +++ b/src/Bundle/ChillMainBundle/Entity/PostalCode.php @@ -12,6 +12,11 @@ declare(strict_types=1); namespace Chill\MainBundle\Entity; use Chill\MainBundle\Doctrine\Model\Point; +use Chill\MainBundle\Doctrine\Model\TrackCreationInterface; +use Chill\MainBundle\Doctrine\Model\TrackCreationTrait; +use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface; +use Chill\MainBundle\Doctrine\Model\TrackUpdateTrait; +use DateTimeImmutable; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Serializer\Annotation\Groups; @@ -21,6 +26,10 @@ use Symfony\Component\Serializer\Annotation\Groups; * @ORM\Entity * @ORM\Table( * name="chill_main_postal_code", + * uniqueConstraints={ + * @ORM\UniqueConstraint(name="postal_code_import_unicity", columns={"code", "refpostalcodeid", "postalcodesource"}, + * options={"where": "refpostalcodeid is not null"}) + * }, * indexes={ * @ORM\Index(name="search_name_code", columns={"code", "label"}), * @ORM\Index(name="search_by_reference_code", columns={"code", "refpostalcodeid"}) @@ -28,8 +37,12 @@ use Symfony\Component\Serializer\Annotation\Groups; * * @ORM\HasLifecycleCallbacks */ -class PostalCode +class PostalCode implements TrackUpdateInterface, TrackCreationInterface { + use TrackCreationTrait; + + use TrackUpdateTrait; + /** * This is an internal column which is populated by database. * @@ -63,6 +76,11 @@ class PostalCode */ private $country; + /** + * @ORM\Column(type="datetime_immutable", nullable=true, options={"default": null}) + */ + private ?DateTimeImmutable $deletedAt = null; + /** * @var int * diff --git a/src/Bundle/ChillMainBundle/migrations/Version20220729205416.php b/src/Bundle/ChillMainBundle/migrations/Version20220729205416.php new file mode 100644 index 000000000..69ffd856f --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20220729205416.php @@ -0,0 +1,53 @@ +addSql('DROP INDEX postal_code_import_unicity'); + $this->addSql('ALTER TABLE chill_main_postal_code DROP deletedAt'); + $this->addSql('ALTER TABLE chill_main_postal_code DROP updatedAt'); + $this->addSql('ALTER TABLE chill_main_postal_code DROP createdAt'); + $this->addSql('ALTER TABLE chill_main_postal_code DROP updatedBy_id'); + $this->addSql('ALTER TABLE chill_main_postal_code DROP createdBy_id'); + $this->addSql('ALTER TABLE chill_main_postal_code DROP CONSTRAINT chill_internal_postal_code_import_unicity'); + } + + public function getDescription(): string + { + return 'postal code: add columns to track creation, update and deletion'; + } + + public function up(Schema $schema): void + { + $this->addSql('ALTER TABLE chill_main_postal_code ADD deletedAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL'); + $this->addSql('ALTER TABLE chill_main_postal_code ADD updatedAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL'); + $this->addSql('ALTER TABLE chill_main_postal_code ADD createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL'); + $this->addSql('ALTER TABLE chill_main_postal_code ADD updatedBy_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE chill_main_postal_code ADD createdBy_id INT DEFAULT NULL'); + $this->addSql('COMMENT ON COLUMN chill_main_postal_code.deletedAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_main_postal_code.updatedAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_main_postal_code.createdAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER TABLE chill_main_postal_code ADD CONSTRAINT FK_6CA145FA65FF1AEC FOREIGN KEY (updatedBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_main_postal_code ADD CONSTRAINT FK_6CA145FA3174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX IDX_6CA145FA65FF1AEC ON chill_main_postal_code (updatedBy_id)'); + $this->addSql('CREATE INDEX IDX_6CA145FA3174800F ON chill_main_postal_code (createdBy_id)'); + $this->addSql('CREATE UNIQUE INDEX postal_code_import_unicity ON chill_main_postal_code (code, refpostalcodeid, postalcodesource) WHERE refpostalcodeid is not null'); + //$this->addSql('ALTER TABLE chill_main_postal_code ADD CONSTRAINT chill_internal_postal_code_import_unicity '. + // 'EXCLUDE (code WITH =, refpostalcodeid WITH =, postalcodesource WITH =) WHERE (refpostalcodeid IS NOT NULL)'); + } +} From 58ddf9038d625866da3c18f5e5089260cdf5f27c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sat, 30 Jul 2022 02:01:42 +0200 Subject: [PATCH 045/120] Feature: load french postal code from laposte hexasmal open data --- .../Command/LoadPostalCodeFR.php | 42 ++++++ .../Service/Import/PostalCodeBaseImporter.php | 123 ++++++++++++++++++ .../Import/PostalCodeFRFromOpenData.php | 105 +++++++++++++++ .../ChillMainBundle/config/services.yaml | 4 + .../config/services/command.yaml | 6 + 5 files changed, 280 insertions(+) create mode 100644 src/Bundle/ChillMainBundle/Command/LoadPostalCodeFR.php create mode 100644 src/Bundle/ChillMainBundle/Service/Import/PostalCodeBaseImporter.php create mode 100644 src/Bundle/ChillMainBundle/Service/Import/PostalCodeFRFromOpenData.php diff --git a/src/Bundle/ChillMainBundle/Command/LoadPostalCodeFR.php b/src/Bundle/ChillMainBundle/Command/LoadPostalCodeFR.php new file mode 100644 index 000000000..1c7f9cbc5 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Command/LoadPostalCodeFR.php @@ -0,0 +1,42 @@ +loader = $loader; + + parent::__construct(); + } + + public function configure(): void + { + $this->setName('chill:main:postal-code:load:FR') + ->setDescription('Load France\'s postal code from online open data'); + } + + public function execute(InputInterface $input, OutputInterface $output): int + { + $this->loader->import(); + + return 0; + } +} diff --git a/src/Bundle/ChillMainBundle/Service/Import/PostalCodeBaseImporter.php b/src/Bundle/ChillMainBundle/Service/Import/PostalCodeBaseImporter.php new file mode 100644 index 000000000..a7e8c8e70 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Service/Import/PostalCodeBaseImporter.php @@ -0,0 +1,123 @@ + + */ + private array $cachingStatements = []; + + private Connection $defaultConnection; + + private array $waitingForInsert = []; + + public function __construct( + Connection $defaultConnection + ) { + $this->defaultConnection = $defaultConnection; + } + + public function finalize(): void + { + $this->doInsertPending(); + } + + public function importCode( + string $countryCode, + string $label, + string $code, + string $refPostalCodeId, + string $refPostalCodeSource, + float $centerLat, + float $centerLon, + int $centerSRID + ): void { + $this->waitingForInsert[] = [ + $countryCode, + $label, + $code, + $refPostalCodeId, + $refPostalCodeSource, + $centerLon, + $centerLat, + $centerSRID, + ]; + + if (100 <= count($this->waitingForInsert)) { + $this->doInsertPending(); + } + } + + private function doInsertPending(): void + { + if (!array_key_exists($forNumber = count($this->waitingForInsert), $this->cachingStatements)) { + $sql = strtr(self::QUERY, [ + '{{ values }}' => implode( + ', ', + array_fill(0, $forNumber, self::VALUE) + ), + ]); + + $this->cachingStatements[$forNumber] = $this->defaultConnection->prepare($sql); + } + + $statement = $this->cachingStatements[$forNumber]; + + try { + $statement->executeStatement(array_merge(...$this->waitingForInsert)); + } catch (\Exception $e) { + // in some case, we can add debug code here + //dump($this->waitingForInsert); + throw $e; + } finally { + $this->waitingForInsert = []; + } + } +} diff --git a/src/Bundle/ChillMainBundle/Service/Import/PostalCodeFRFromOpenData.php b/src/Bundle/ChillMainBundle/Service/Import/PostalCodeFRFromOpenData.php new file mode 100644 index 000000000..a49bb10a4 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Service/Import/PostalCodeFRFromOpenData.php @@ -0,0 +1,105 @@ +baseImporter = $baseImporter; + $this->client = $client; + $this->logger = $logger; + } + + public function import(): void + { + $response = $this->client->request('GET', self::CSV); + + if (200 !== $response->getStatusCode()) { + throw new RuntimeException('could not download CSV'); + } + + $tmpfile = tmpfile(); + + if (false === $tmpfile) { + throw new RuntimeException('could not create temporary file'); + } + + foreach ($this->client->stream($response) as $chunk) { + fwrite($tmpfile, $chunk->getContent()); + } + + fseek($tmpfile, 0); + + $csv = Reader::createFromStream($tmpfile); + $csv->setDelimiter(';'); + $csv->setHeaderOffset(0); + + foreach ($csv as $offset => $record) { + $this->handleRecord($record); + } + + $this->baseImporter->finalize(); + fclose($tmpfile); + + $this->logger->info(__CLASS__ . ' postal code fetched', ['offset' => $offset]); + } + + private function handleRecord(array $record): void + { + if ('' !== trim($record['coordonnees_gps'])) { + [$lat, $lon] = array_map(static fn ($el) => (float) trim($el), explode(',', $record['coordonnees_gps'])); + } else { + $lat = $lon = 0.0; + } + + $ref = trim($record['Code_commune_INSEE']); + + if ('987' === substr($ref, 0, 3)) { + // some differences in French Polynesia + $ref .= '.' . trim($record['Libellé_d_acheminement']); + } + + $this->baseImporter->importCode( + 'FR', + trim($record['Libellé_d_acheminement']), + trim($record['Code_postal']), + $ref, + 'INSEE', + $lat, + $lon, + 4326 + ); + } +} diff --git a/src/Bundle/ChillMainBundle/config/services.yaml b/src/Bundle/ChillMainBundle/config/services.yaml index 6d55532a6..d3475752b 100644 --- a/src/Bundle/ChillMainBundle/config/services.yaml +++ b/src/Bundle/ChillMainBundle/config/services.yaml @@ -97,3 +97,7 @@ services: Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface: '@Chill\MainBundle\Security\Resolver\CenterResolverDispatcher' + Chill\MainBundle\Service\Import\: + resource: '../Service/Import/' + autowire: true + autoconfigure: true diff --git a/src/Bundle/ChillMainBundle/config/services/command.yaml b/src/Bundle/ChillMainBundle/config/services/command.yaml index 87220bc1f..07b3c1721 100644 --- a/src/Bundle/ChillMainBundle/config/services/command.yaml +++ b/src/Bundle/ChillMainBundle/config/services/command.yaml @@ -43,3 +43,9 @@ services: $entityManager: '@doctrine.orm.entity_manager' tags: - { name: console.command } + + Chill\MainBundle\Command\LoadPostalCodeFR: + autoconfigure: true + autowire: true + tags: + - { name: console.command } From 9b3b9f2552a67f170591c353020b2449b7409113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sat, 30 Jul 2022 23:07:51 +0200 Subject: [PATCH 046/120] Fixed: possible unexisting variable --- .../ChillMainBundle/Service/Import/PostalCodeFRFromOpenData.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bundle/ChillMainBundle/Service/Import/PostalCodeFRFromOpenData.php b/src/Bundle/ChillMainBundle/Service/Import/PostalCodeFRFromOpenData.php index a49bb10a4..26a52c840 100644 --- a/src/Bundle/ChillMainBundle/Service/Import/PostalCodeFRFromOpenData.php +++ b/src/Bundle/ChillMainBundle/Service/Import/PostalCodeFRFromOpenData.php @@ -73,7 +73,7 @@ class PostalCodeFRFromOpenData $this->baseImporter->finalize(); fclose($tmpfile); - $this->logger->info(__CLASS__ . ' postal code fetched', ['offset' => $offset]); + $this->logger->info(__CLASS__ . ' postal code fetched', ['offset' => $offset ?? 0]); } private function handleRecord(array $record): void From 84cda8845dd207e42f513327f4bb9c7a98b6e966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sat, 30 Jul 2022 23:10:19 +0200 Subject: [PATCH 047/120] Feature: command to load addresses from France from bano.openstreetmap.fr --- .../LoadAddressesFRFromBANOCommand.php | 47 ++++ .../Entity/AddressReference.php | 3 + .../Import/AddressReferenceBaseImporter.php | 207 ++++++++++++++++++ .../Import/AddressReferenceFromBano.php | 87 ++++++++ .../config/services/command.yaml | 6 + .../migrations/Version20220730204216.php | 26 +++ 6 files changed, 376 insertions(+) create mode 100644 src/Bundle/ChillMainBundle/Command/LoadAddressesFRFromBANOCommand.php create mode 100644 src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBaseImporter.php create mode 100644 src/Bundle/ChillMainBundle/Service/Import/AddressReferenceFromBano.php create mode 100644 src/Bundle/ChillMainBundle/migrations/Version20220730204216.php diff --git a/src/Bundle/ChillMainBundle/Command/LoadAddressesFRFromBANOCommand.php b/src/Bundle/ChillMainBundle/Command/LoadAddressesFRFromBANOCommand.php new file mode 100644 index 000000000..96e7023fb --- /dev/null +++ b/src/Bundle/ChillMainBundle/Command/LoadAddressesFRFromBANOCommand.php @@ -0,0 +1,47 @@ +addressReferenceFromBano = $addressReferenceFromBano; + } + + protected function configure() + { + $this->setName('chill:main:address-ref-from-bano') + ->addArgument('departementNo', InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'a list of departement numbers') + ->setDescription('Import addresses from bano (see https://bano.openstreetmap.fr'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + foreach ($input->getArgument('departementNo') as $departementNo) { + $output->writeln('Import addresses for ' . $departementNo); + + $this->addressReferenceFromBano->import($departementNo); + } + + return 0; + } +} diff --git a/src/Bundle/ChillMainBundle/Entity/AddressReference.php b/src/Bundle/ChillMainBundle/Entity/AddressReference.php index fc4339fe0..5a6b176c2 100644 --- a/src/Bundle/ChillMainBundle/Entity/AddressReference.php +++ b/src/Bundle/ChillMainBundle/Entity/AddressReference.php @@ -20,6 +20,9 @@ use Symfony\Component\Serializer\Annotation\Groups; * @ORM\Entity * @ORM\Table(name="chill_main_address_reference", indexes={ * @ORM\Index(name="address_refid", columns={"refId"}) + * }, + * uniqueConstraints={ + * @ORM\UniqueConstraint(name="chill_main_address_reference_unicity", columns={"refId", "source"}) * }) * @ORM\HasLifecycleCallbacks */ diff --git a/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBaseImporter.php b/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBaseImporter.php new file mode 100644 index 000000000..87659e65d --- /dev/null +++ b/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBaseImporter.php @@ -0,0 +1,207 @@ + + */ + private array $cachingStatements = []; + + private ?string $currentSource = null; + + private Connection $defaultConnection; + + private bool $isInitialized = false; + + private LoggerInterface $logger; + + private array $waitingForInsert = []; + + public function __construct(Connection $defaultConnection, LoggerInterface $logger) + { + $this->defaultConnection = $defaultConnection; + $this->logger = $logger; + } + + public function finalize(): void + { + $this->doInsertPending(); + + $this->updateAddressReferenceTable(); + + $this->deleteTemporaryTable(); + + $this->currentSource = null; + $this->isInitialized = false; + } + + public function importAddress( + string $refAddress, + string $refPostalCode, + string $postalCode, + string $street, + string $streetNumber, + string $source, + ?float $lat = null, + ?float $lon = null, + ?int $srid = null + ): void { + if (!$this->isInitialized) { + $this->initialize($source); + } + + if ($this->currentSource !== $source) { + throw new LogicException('Cannot store addresses from different sources during same import. Execute finalize to commit inserts before changing the source'); + } + + $this->waitingForInsert[] = [ + $refAddress, + $refPostalCode, + $postalCode, + $street, + $streetNumber, + $source, + $lat, + $lon, + $srid, + ]; + + if (100 <= count($this->waitingForInsert)) { + $this->doInsertPending(); + } + } + + private function createTemporaryTable(): void + { + $this->defaultConnection->executeStatement('CREATE TEMPORARY TABLE reference_address_temp ( + postcode_id INT, + refid VARCHAR(255), + street VARCHAR(255), + streetnumber VARCHAR(255), + municipalitycode VARCHAR(255), + source VARCHAR(255), + point GEOMETRY + ); + '); + $this->defaultConnection->executeStatement('SET work_mem TO \'50MB\''); + } + + private function deleteTemporaryTable(): void + { + $this->defaultConnection->executeStatement('DROP TABLE IF EXISTS reference_address_temp'); + } + + private function doInsertPending(): void + { + if (!array_key_exists($forNumber = count($this->waitingForInsert), $this->cachingStatements)) { + $sql = strtr(self::INSERT, [ + '{{ values }}' => implode( + ', ', + array_fill(0, $forNumber, self::VALUE) + ), + ]); + + $this->cachingStatements[$forNumber] = $this->defaultConnection->prepare($sql); + } + + $this->logger->debug(self::LOG_PREFIX . ' inserting pending addresses', [ + 'number' => $forNumber, + 'first' => $this->waitingForInsert[0] ?? null, + ]); + + $statement = $this->cachingStatements[$forNumber]; + + try { + $statement->executeStatement(array_merge(...$this->waitingForInsert)); + } catch (Exception $e) { + // in some case, we can add debug code here + //dump($this->waitingForInsert); + throw $e; + } finally { + $this->waitingForInsert = []; + } + } + + private function initialize(string $source): void + { + $this->currentSource = $source; + $this->deleteTemporaryTable(); + $this->createTemporaryTable(); + $this->isInitialized = true; + } + + private function updateAddressReferenceTable(): void + { + $this->defaultConnection->executeStatement( + 'CREATE INDEX idx_ref_add_temp ON reference_address_temp (refid)' + ); + + //1) Add new addresses + $this->logger->info(self::LOG_PREFIX . 'upsert new addresses'); + $affected = $this->defaultConnection->executeStatement("INSERT INTO chill_main_address_reference + (id, postcode_id, refid, street, streetnumber, municipalitycode, source, point, createdat, deletedat, updatedat) + SELECT + nextval('chill_main_address_reference_id_seq'), + postcode_id, + refid, + street, + streetnumber, + municipalitycode, + source, + point, + NOW(), + null, + NOW() + FROM reference_address_temp + ON CONFLICT (refid, source) DO UPDATE + SET postcode_id = excluded.postcode_id, refid = excluded.refid, street = excluded.street, streetnumber = excluded.streetnumber, municipalitycode = excluded.municipalitycode, source = excluded.source, point = excluded.point, updatedat = NOW(), deletedAt = NULL + "); + $this->logger->info(self::LOG_PREFIX . 'addresses upserted', ['upserted' => $affected]); + + //3) Delete addresses + $this->logger->info(self::LOG_PREFIX . 'soft delete adresses'); + $affected = $this->defaultConnection->executeStatement('UPDATE chill_main_address_reference + SET deletedat = NOW() + WHERE + chill_main_address_reference.refid NOT IN (SELECT refid FROM reference_address_temp WHERE source LIKE ?) + AND chill_main_address_reference.source LIKE ? + ', [$this->currentSource, $this->currentSource]); + $this->logger->info(self::LOG_PREFIX . 'addresses deleted', ['deleted' => $affected]); + } +} diff --git a/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceFromBano.php b/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceFromBano.php new file mode 100644 index 000000000..c30b7c4f2 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceFromBano.php @@ -0,0 +1,87 @@ +client = $client; + $this->baseImporter = $baseImporter; + } + + public function import(string $departementNo): void + { + if (!is_numeric($departementNo) || !is_int((int) $departementNo)) { + throw new UnexpectedValueException('Could not parse this department number'); + } + + $url = "https://bano.openstreetmap.fr/data/bano-{$departementNo}.csv"; + + $response = $this->client->request('GET', $url); + + if (200 !== $response->getStatusCode()) { + throw new Exception('Could not download CSV: ' . $response->getStatusCode()); + } + + $file = tmpfile(); + + foreach ($this->client->stream($response) as $chunk) { + fwrite($file, $chunk->getContent()); + } + + fseek($file, 0); + + $csv = Reader::createFromStream($file); + $csv->setDelimiter(','); + $stmt = Statement::create() + ->process($csv, [ + 'refId', + 'streetNumber', + 'street', + 'postcode', + 'city', + '_o', + 'lat', + 'lon', + ]); + + foreach ($stmt as $record) { + $this->baseImporter->importAddress( + $record['refId'], + substr($record['refId'], 0, 5), // extract insee from reference + $record['postcode'], + $record['street'], + $record['streetNumber'], + 'BANO.' . $departementNo, + (float) $record['lat'], + (float) $record['lon'], + 4326 + ); + } + + $this->baseImporter->finalize(); + + fclose($file); + } +} diff --git a/src/Bundle/ChillMainBundle/config/services/command.yaml b/src/Bundle/ChillMainBundle/config/services/command.yaml index 07b3c1721..8c1d9b94e 100644 --- a/src/Bundle/ChillMainBundle/config/services/command.yaml +++ b/src/Bundle/ChillMainBundle/config/services/command.yaml @@ -44,6 +44,12 @@ services: tags: - { name: console.command } + Chill\MainBundle\Command\LoadAddressesFRFromBANOCommand: + autoconfigure: true + autowire: true + tags: + - { name: console.command } + Chill\MainBundle\Command\LoadPostalCodeFR: autoconfigure: true autowire: true diff --git a/src/Bundle/ChillMainBundle/migrations/Version20220730204216.php b/src/Bundle/ChillMainBundle/migrations/Version20220730204216.php new file mode 100644 index 000000000..4ed1b2918 --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20220730204216.php @@ -0,0 +1,26 @@ +addSql('CREATE UNIQUE INDEX chill_main_address_reference_unicity ON chill_main_address_reference (refId, source)'); + } + + public function down(Schema $schema): void + { + $this->addSql('DROP INDEX chill_main_address_reference_unicity'); + } +} From 0f63548d5a78611301eeb1e1f7eb765d6fd9608a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sat, 3 Sep 2022 00:40:21 +0200 Subject: [PATCH 048/120] import addresses and postal codes from bestaddress --- .../LoadAddressesBEFromBestAddressCommand.php | 52 +++++++++ .../AddressReferenceBEFromBestAddress.php | 103 +++++++++++++++++ .../Import/AddressReferenceBaseImporter.php | 13 ++- .../Import/PostalCodeBEFromBestAddress.php | 105 ++++++++++++++++++ .../Service/Import/PostalCodeBaseImporter.php | 5 +- .../config/services/command.yaml | 6 + 6 files changed, 280 insertions(+), 4 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Command/LoadAddressesBEFromBestAddressCommand.php create mode 100644 src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBEFromBestAddress.php create mode 100644 src/Bundle/ChillMainBundle/Service/Import/PostalCodeBEFromBestAddress.php diff --git a/src/Bundle/ChillMainBundle/Command/LoadAddressesBEFromBestAddressCommand.php b/src/Bundle/ChillMainBundle/Command/LoadAddressesBEFromBestAddressCommand.php new file mode 100644 index 000000000..abd4e294a --- /dev/null +++ b/src/Bundle/ChillMainBundle/Command/LoadAddressesBEFromBestAddressCommand.php @@ -0,0 +1,52 @@ +addressImporter = $addressImporter; + $this->postalCodeBEFromBestAddressImporter = $postalCodeBEFromBestAddressImporter; + } + + protected function configure() + { + $this + ->setName('chill:main:address-ref-from-best-addresses') + ->addArgument('lang', InputArgument::REQUIRED) + ->addArgument('list', InputArgument::IS_ARRAY, 'The list to add'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->postalCodeBEFromBestAddressImporter->import(); + + $this->addressImporter->import($input->getArgument('lang'), $input->getArgument('list')); + + return 0; + } +} diff --git a/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBEFromBestAddress.php b/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBEFromBestAddress.php new file mode 100644 index 000000000..b034dbca7 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBEFromBestAddress.php @@ -0,0 +1,103 @@ +client = $client; + $this->baseImporter = $baseImporter; + } + + public function import(string $lang, array $lists): void + { + foreach ($lists as $list) { + $this->importList($lang, $list); + } + } + + private function getDownloadUrl(string $lang, string $list): string + { + try { + $release = $this->client->request('GET', self::RELEASE) + ->toArray(); + } catch (TransportExceptionInterface $e) { + throw new RuntimeException('could not get the release definition', 0, $e); + } + + $asset = array_filter($release['assets'], static function (array $item) use ($lang, $list) { + return 'addresses-' . $list . '.' . $lang . '.csv.gz' === $item['name']; + }); + + return array_values($asset)[0]['browser_download_url']; + } + + private function importList(string $lang, string $list): void + { + $downloadUrl = $this->getDownloadUrl($lang, $list); + + $response = $this->client->request('GET', $downloadUrl); + + if (200 !== $response->getStatusCode()) { + throw new Exception('Could not download CSV: ' . $response->getStatusCode()); + } + + $tmpname = tempnam(sys_get_temp_dir(), 'php-add-' . $list . $lang); + $file = fopen($tmpname, 'r+b'); + + foreach ($this->client->stream($response) as $chunk) { + fwrite($file, $chunk->getContent()); + } + + fclose($file); + + $uncompressedStream = gzopen($tmpname, 'r'); + + $csv = Reader::createFromStream($uncompressedStream); + $csv->setDelimiter(','); + $csv->setHeaderOffset(0); + + $stmt = Statement::create() + ->process($csv); + + foreach ($stmt as $record) { + $this->baseImporter->importAddress( + $record['best_id'], + $record['municipality_objectid'], + $record['postal_info_objectid'], + $record['streetname'], + $record['housenumber'] . $record['boxnumber'], + 'bestaddress.' . $list, + (float) $record['X'], + (float) $record['Y'], + 3812 + ); + } + + $this->baseImporter->finalize(); + + gzclose($uncompressedStream); + } +} diff --git a/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBaseImporter.php b/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBaseImporter.php index 87659e65d..0ca17b5de 100644 --- a/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBaseImporter.php +++ b/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBaseImporter.php @@ -26,7 +26,7 @@ final class AddressReferenceBaseImporter (postcode_id, refid, street, streetnumber, municipalitycode, source, point) SELECT cmpc.id, i.refid, i.street, i.streetnumber, i.refpostalcode, i.source, - CASE WHEN (i.lon::float != 0.0 AND i.lat::float != 0.0) THEN ST_setSrid(ST_point(i.lon::float, i.lat::float), i.srid::int) ELSE NULL END + CASE WHEN (i.lon::float != 0.0 AND i.lat::float != 0.0) THEN ST_Transform(ST_setSrid(ST_point(i.lon::float, i.lat::float), i.srid::int), 4326) ELSE NULL END FROM (VALUES {{ values }} @@ -137,9 +137,18 @@ final class AddressReferenceBaseImporter ), ]); + $this->logger->debug(self::LOG_PREFIX . ' generated sql for insert', [ + 'sql' => $sql, + 'forNumber' => $forNumber, + ]); + $this->cachingStatements[$forNumber] = $this->defaultConnection->prepare($sql); } + if (0 === $forNumber) { + return; + } + $this->logger->debug(self::LOG_PREFIX . ' inserting pending addresses', [ 'number' => $forNumber, 'first' => $this->waitingForInsert[0] ?? null, @@ -188,7 +197,7 @@ final class AddressReferenceBaseImporter NOW(), null, NOW() - FROM reference_address_temp + FROM reference_address_temp ON CONFLICT (refid, source) DO UPDATE SET postcode_id = excluded.postcode_id, refid = excluded.refid, street = excluded.street, streetnumber = excluded.streetnumber, municipalitycode = excluded.municipalitycode, source = excluded.source, point = excluded.point, updatedat = NOW(), deletedAt = NULL "); diff --git a/src/Bundle/ChillMainBundle/Service/Import/PostalCodeBEFromBestAddress.php b/src/Bundle/ChillMainBundle/Service/Import/PostalCodeBEFromBestAddress.php new file mode 100644 index 000000000..a5b8b216b --- /dev/null +++ b/src/Bundle/ChillMainBundle/Service/Import/PostalCodeBEFromBestAddress.php @@ -0,0 +1,105 @@ +baseImporter = $baseImporter; + $this->client = $client; + $this->logger = $logger; + } + + public function import(string $lang = 'fr'): void + { + $fileDownloadUrl = $this->getFileDownloadUrl($lang); + + $response = $this->client->request('GET', $fileDownloadUrl); + + $tmpname = tempnam(sys_get_temp_dir(), 'postalcodes'); + $tmpfile = fopen($tmpname, 'r+b'); + + if (false === $tmpfile) { + throw new RuntimeException('could not create temporary file'); + } + + foreach ($this->client->stream($response) as $chunk) { + fwrite($tmpfile, $chunk->getContent()); + } + + fclose($tmpfile); + + $uncompressedStream = gzopen($tmpname, 'r'); + + $csv = Reader::createFromStream($uncompressedStream); + $csv->setDelimiter(','); + $csv->setHeaderOffset(0); + + foreach ($csv as $offset => $record) { + $this->handleRecord($record); + } + + gzclose($uncompressedStream); + unlink($tmpname); + + $this->logger->info(__CLASS__ . ' list of postal code downloaded'); + + $this->baseImporter->finalize(); + + $this->logger->info(__CLASS__ . ' postal code fetched', ['offset' => $offset ?? 0]); + } + + private function getFileDownloadUrl(string $lang): string + { + try { + $release = $this->client->request('GET', self::RELEASE) + ->toArray(); + } catch (TransportExceptionInterface $e) { + throw new RuntimeException('could not get the release definition', 0, $e); + } + + $postals = array_filter($release['assets'], static function (array $item) use ($lang) { + return 'postals.' . $lang . '.csv.gz' === $item['name']; + }); + + return array_values($postals)[0]['browser_download_url']; + } + + private function handleRecord(array $record): void + { + $this->baseImporter->importCode( + 'BE', + trim($record['municipality_name']), + trim($record['postal_info_objectid']), + $record['municipality_objectid'], + 'bestaddress', + $record['Y'], + $record['X'], + 3812 + ); + } +} diff --git a/src/Bundle/ChillMainBundle/Service/Import/PostalCodeBaseImporter.php b/src/Bundle/ChillMainBundle/Service/Import/PostalCodeBaseImporter.php index a7e8c8e70..20b41420f 100644 --- a/src/Bundle/ChillMainBundle/Service/Import/PostalCodeBaseImporter.php +++ b/src/Bundle/ChillMainBundle/Service/Import/PostalCodeBaseImporter.php @@ -13,6 +13,7 @@ namespace Chill\MainBundle\Service\Import; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Statement; +use Exception; use function array_key_exists; use function count; @@ -40,7 +41,7 @@ class PostalCodeBaseImporter 0, g.refpostalcodeid, g.postalcodeSource, - CASE WHEN (g.lon::float != 0.0 AND g.lat::float != 0.0) THEN ST_setSrid(ST_point(g.lon::float, g.lat::float), g.srid::int) ELSE NULL END, + CASE WHEN (g.lon::float != 0.0 AND g.lat::float != 0.0) THEN ST_Transform(ST_setSrid(ST_point(g.lon::float, g.lat::float), g.srid::int), 4326) ELSE NULL END, NOW(), NOW() FROM g @@ -112,7 +113,7 @@ class PostalCodeBaseImporter try { $statement->executeStatement(array_merge(...$this->waitingForInsert)); - } catch (\Exception $e) { + } catch (Exception $e) { // in some case, we can add debug code here //dump($this->waitingForInsert); throw $e; diff --git a/src/Bundle/ChillMainBundle/config/services/command.yaml b/src/Bundle/ChillMainBundle/config/services/command.yaml index 8c1d9b94e..e5f285545 100644 --- a/src/Bundle/ChillMainBundle/config/services/command.yaml +++ b/src/Bundle/ChillMainBundle/config/services/command.yaml @@ -50,6 +50,12 @@ services: tags: - { name: console.command } + Chill\MainBundle\Command\LoadAddressesBEFromBestAddressCommand: + autoconfigure: true + autowire: true + tags: + - { name: console.command } + Chill\MainBundle\Command\LoadPostalCodeFR: autoconfigure: true autowire: true From 658e846120cd9a5b75214688050defdafcbd7f3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sat, 17 Sep 2022 10:44:26 +0200 Subject: [PATCH 049/120] add test for AddressReferenceBaseImporter --- .../Import/AddressReferenceBaseImporter.php | 8 +- .../AddressReferenceBaseImporterTest.php | 87 +++++++++++++++++++ 2 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Tests/Services/Import/AddressReferenceBaseImporterTest.php diff --git a/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBaseImporter.php b/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBaseImporter.php index 0ca17b5de..6f1d218a9 100644 --- a/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBaseImporter.php +++ b/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBaseImporter.php @@ -73,7 +73,7 @@ final class AddressReferenceBaseImporter public function importAddress( string $refAddress, - string $refPostalCode, + ?string $refPostalCode, string $postalCode, string $street, string $streetNumber, @@ -157,7 +157,11 @@ final class AddressReferenceBaseImporter $statement = $this->cachingStatements[$forNumber]; try { - $statement->executeStatement(array_merge(...$this->waitingForInsert)); + $affected = $statement->executeStatement(array_merge(...$this->waitingForInsert)); + + if ($affected === 0) { + throw new \RuntimeException('no row affected'); + } } catch (Exception $e) { // in some case, we can add debug code here //dump($this->waitingForInsert); diff --git a/src/Bundle/ChillMainBundle/Tests/Services/Import/AddressReferenceBaseImporterTest.php b/src/Bundle/ChillMainBundle/Tests/Services/Import/AddressReferenceBaseImporterTest.php new file mode 100644 index 000000000..d4302413e --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Services/Import/AddressReferenceBaseImporterTest.php @@ -0,0 +1,87 @@ +importer = self::$container->get(AddressReferenceBaseImporter::class); + $this->addressReferenceRepository = self::$container->get(AddressReferenceRepository::class); + $this->entityManager = self::$container->get(EntityManagerInterface::class); + $this->postalCodeRepository = self::$container->get(PostalCodeRepository::class); + } + + public function testImportAddress(): void + { + $postalCode = (new PostalCode()) + ->setRefPostalCodeId($postalCodeId = '1234'.uniqid()) + ->setPostalCodeSource('testing') + ->setCode('TEST456') + ->setName('testing'); + + $this->entityManager->persist($postalCode); + $this->entityManager->flush(); + + $this->importer->importAddress( + '0000', + $postalCodeId, + 'TEST456', + 'Rue test abccc-guessed', + '-1', + 'unit-test', + 50.0, + 5.0, + 4326 + ); + + $this->importer->finalize(); + + $addresses = $this->addressReferenceRepository->findByPostalCodePattern( + $postalCode, + 'Rue test abcc guessed'); + + $this->assertCount(1, $addresses); + $this->assertEquals('Rue test abccc-guessed', $addresses[0]->getStreet()); + + $this->entityManager->clear(); + + $this->importer->importAddress( + '0000', + $postalCodeId, + 'TEST456', + 'Rue test abccc guessed fixed', + '-1', + 'unit-test', + 50.0, + 5.0, + 4326 + ); + + $this->importer->finalize(); + + $addresses = $this->addressReferenceRepository->findByPostalCodePattern( + $postalCode, + 'abcc guessed fixed'); + + $this->assertCount('1', $addresses); + $this->assertEquals( 'Rue test abccc guessed fixed', $addresses[0]->getStreet()); + } + + +} \ No newline at end of file From 8129739d27b76497e87d9c6e4e7932ed9d56b771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sat, 17 Sep 2022 10:59:04 +0200 Subject: [PATCH 050/120] test for postal code base importer --- .../AddressReferenceBaseImporterTest.php | 3 + .../Import/PostalCodeBaseImporterTest.php | 85 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 src/Bundle/ChillMainBundle/Tests/Services/Import/PostalCodeBaseImporterTest.php diff --git a/src/Bundle/ChillMainBundle/Tests/Services/Import/AddressReferenceBaseImporterTest.php b/src/Bundle/ChillMainBundle/Tests/Services/Import/AddressReferenceBaseImporterTest.php index d4302413e..cb23634f1 100644 --- a/src/Bundle/ChillMainBundle/Tests/Services/Import/AddressReferenceBaseImporterTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Services/Import/AddressReferenceBaseImporterTest.php @@ -59,6 +59,8 @@ class AddressReferenceBaseImporterTest extends KernelTestCase $this->assertCount(1, $addresses); $this->assertEquals('Rue test abccc-guessed', $addresses[0]->getStreet()); + $previousAddressId = $addresses[0]->getId(); + $this->entityManager->clear(); $this->importer->importAddress( @@ -81,6 +83,7 @@ class AddressReferenceBaseImporterTest extends KernelTestCase $this->assertCount('1', $addresses); $this->assertEquals( 'Rue test abccc guessed fixed', $addresses[0]->getStreet()); + $this->assertEquals($previousAddressId, $addresses[0]->getId()); } diff --git a/src/Bundle/ChillMainBundle/Tests/Services/Import/PostalCodeBaseImporterTest.php b/src/Bundle/ChillMainBundle/Tests/Services/Import/PostalCodeBaseImporterTest.php new file mode 100644 index 000000000..826e581ac --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Services/Import/PostalCodeBaseImporterTest.php @@ -0,0 +1,85 @@ +entityManager = self::$container->get(EntityManagerInterface::class); + $this->importer = self::$container->get(PostalCodeBaseImporter::class); + $this->postalCodeRepository = self::$container->get(PostalCodeRepository::class); + $this->countryRepository = self::$container->get(CountryRepository::class); + } + + public function testImportPostalCode(): void + { + $this->importer->importCode( + 'BE', + 'tested with pattern '. ($uniqid = uniqid()), + '12345', + $refPostalCodeId = 'test'.uniqid(), + 'test', + 50.0, + 5.0, + 4326 + ); + + $this->importer->finalize(); + + $postalCodes = $this->postalCodeRepository->findByPattern( + 'with pattern '.$uniqid, + $this->countryRepository->findOneBy(['countryCode' => 'BE']) + ); + + $this->assertCount(1, $postalCodes); + $this->assertStringStartsWith('tested with pattern', $postalCodes[0]->getName()); + + $previousId = $postalCodes[0]->getId(); + + $this->entityManager->clear(); + + $this->importer->importCode( + 'BE', + 'tested with adapted pattern '. ($uniqid = uniqid()), + '12345', + $refPostalCodeId, + 'test', + 50.0, + 5.0, + 4326 + ); + + $this->importer->finalize(); + + $postalCodes = $this->postalCodeRepository->findByPattern( + 'with pattern '.$uniqid, + $this->countryRepository->findOneBy(['countryCode' => 'BE']) + ); + + $this->assertCount(1, $postalCodes); + $this->assertStringStartsWith('tested with adapted pattern', $postalCodes[0]->getName()); + $this->assertEquals($previousId, $postalCodes[0]->getId()); + } + + + +} \ No newline at end of file From 841eb57144a88426c20c1f5b4c14c5584b3deb33 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Tue, 13 Sep 2022 15:10:12 +0200 Subject: [PATCH 051/120] GeographicalUnit Filter: add join clauses --- .../GeographicalUnitStatFilter.php | 18 +++++++++++++- .../services/exports_accompanying_course.yaml | 24 +++++++++---------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilter.php index 8f6748b70..e29dfd658 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilter.php @@ -17,6 +17,7 @@ use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Export\Declarations; use DateTime; use Doctrine\DBAL\Types\Types; +use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; @@ -41,8 +42,23 @@ class GeographicalUnitStatFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { + if (!in_array('location', $qb->getAllAliases(), true)) { + $qb->join('acp.administrativeLocation', 'location'); + } + + if (!in_array('address', $qb->getAllAliases(), true)) { + $qb->join('location.address', 'address'); + } + + 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()->eq(1, 1); + $clause = $qb->expr()->andX( + $qb->expr()->eq($x, ':date'), + $qb->expr()->in($x, ':loctype') + ); if ($where instanceof Andx) { $where->add($clause); diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml index f19db0649..8a8b20cbe 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml @@ -44,12 +44,12 @@ services: tags: - { name: chill.export_filter, alias: accompanyingcourse_step_filter } - #chill.person.export.filter_geographicalunitstat: - # class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\GeographicalUnitStatFilter - # autowire: true - # autoconfigure: true - # tags: - # - { name: chill.export_filter, alias: accompanyingcourse_geographicalunitstat_filter } + chill.person.export.filter_geographicalunitstat: + class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\GeographicalUnitStatFilter + autowire: true + autoconfigure: true + tags: + - { name: chill.export_filter, alias: accompanyingcourse_geographicalunitstat_filter } chill.person.export.filter_socialaction: class: Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\SocialActionFilter @@ -171,12 +171,12 @@ services: tags: - { name: chill.export_aggregator, alias: accompanyingcourse_step_aggregator } - #chill.person.export.aggregator_geographicalunitstat: - # class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\GeographicalUnitStatAggregator - # autowire: true - # autoconfigure: true - # tags: - # - { name: chill.export_aggregator, alias: accompanyingcourse_geographicalunitstat_aggregator } + chill.person.export.aggregator_geographicalunitstat: + class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\GeographicalUnitStatAggregator + autowire: true + autoconfigure: true + tags: + - { name: chill.export_aggregator, alias: accompanyingcourse_geographicalunitstat_aggregator } chill.person.export.aggregator_socialaction: class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\SocialActionAggregator From b13686bbf196b74843a636feff1ea026c72972ea Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Tue, 13 Sep 2022 20:49:24 +0200 Subject: [PATCH 052/120] fix geom type in db: multipolygon --- .../migrations/Version20220913174922.php | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/Bundle/ChillMainBundle/migrations/Version20220913174922.php diff --git a/src/Bundle/ChillMainBundle/migrations/Version20220913174922.php b/src/Bundle/ChillMainBundle/migrations/Version20220913174922.php new file mode 100644 index 000000000..cabc7f1f8 --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20220913174922.php @@ -0,0 +1,29 @@ +addSql('ALTER TABLE chill_main_geographical_unit ALTER COLUMN geom SET DATA TYPE GEOMETRY(MULTIPOLYGON, 4326)'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER COLUMN geom SET DATA TYPE TEXT'); + } +} From d30ac75995e0191b7bbba5b05be96775283a92b7 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Mon, 19 Sep 2022 11:22:41 +0200 Subject: [PATCH 053/120] exports: create workAction goalResult aggregator --- .../SocialWorkAggregators/GoalAggregator.php | 10 +- .../GoalResultAggregator.php | 94 +++++++++++++++++++ .../ResultAggregator.php | 12 ++- .../services/exports_social_actions.yaml | 7 ++ .../translations/messages.fr.yml | 5 +- 5 files changed, 118 insertions(+), 10 deletions(-) create mode 100644 src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalResultAggregator.php diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php index 4310dcac2..21c13cef9 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php @@ -52,7 +52,7 @@ final class GoalAggregator implements AggregatorInterface } } - public function applyOn() + public function applyOn(): string { return Declarations::SOCIAL_WORK_ACTION_TYPE; } @@ -71,16 +71,18 @@ final class GoalAggregator implements AggregatorInterface $g = $this->goalRepository->find($value); - return $this->translatableStringHelper->localize($g->getTitle()); + return $this->translatableStringHelper->localize( + $g->getTitle() + ); }; } - public function getQueryKeys($data) + public function getQueryKeys($data): array { return ['goal_aggregator']; } - public function getTitle() + public function getTitle(): string { return 'Group social work actions by goal'; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalResultAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalResultAggregator.php new file mode 100644 index 000000000..86bfcd71a --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalResultAggregator.php @@ -0,0 +1,94 @@ +resultRepository = $resultRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + + + /** + * @inheritDoc + */ + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Goal and result Type'; + } + + //$g = $this->resultRepository->find($value); + + return + $value + //$this->translatableStringHelper->localize( + // $g->getTitle() + //) + ; + }; + } + + /** + * @inheritDoc + */ + public function getQueryKeys($data): array + { + return ['goal_result_aggregator']; + } + + /** + * @inheritDoc + */ + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + /** + * @inheritDoc + */ + public function getTitle(): string + { + return 'Group social work actions by goal and result'; + } + + /** + * @inheritDoc + */ + public function addRole(): ?string + { + return null; + } + + /** + * @inheritDoc + */ + public function alterQuery(QueryBuilder $qb, $data) + { + // TODO: Implement alterQuery() method. + } + + /** + * @inheritDoc + */ + public function applyOn(): string + { + return Declarations::SOCIAL_WORK_ACTION_TYPE; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php index 47391ed69..2bbd8445c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php @@ -60,7 +60,7 @@ final class ResultAggregator implements AggregatorInterface } } - public function applyOn() + public function applyOn(): string { return Declarations::SOCIAL_WORK_ACTION_TYPE; } @@ -77,18 +77,20 @@ final class ResultAggregator implements AggregatorInterface return 'Result Type'; } - $g = $this->resultRepository->find($value); + $r = $this->resultRepository->find($value); - return $this->translatableStringHelper->localize($g->getTitle()); + return $this->translatableStringHelper->localize( + $r->getTitle() + ); }; } - public function getQueryKeys($data) + public function getQueryKeys($data): array { return ['result_aggregator']; } - public function getTitle() + public function getTitle(): string { return 'Group social work actions by result'; } diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml index d5f7376db..450899659 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml @@ -79,3 +79,10 @@ services: autoconfigure: true tags: - { name: chill.export_aggregator, alias: social_work_actions_result_aggregator } + + chill.person.export.aggregator_goalresult: + class: Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\GoalResultAggregator + autowire: true + autoconfigure: true + tags: + - { name: chill.export_aggregator, alias: social_work_actions_goal_result_aggregator } diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index dc53f4839..ca48fbb7a 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -560,8 +560,11 @@ Group by treating agent: Grouper les actions par agent traitant Group social work actions by action type: Grouper les actions par type Group social work actions by goal: Grouper les actions par objectif -Goal Type: Objectif Group social work actions by result: Grouper les actions par résultat +Group social work actions by goal and result: Grouper les actions par objectif et résultat +Goal Type: Objectif +Result Type: Résultat +Goal and result Type: Objectif et résultat ## evaluations filters/aggr Filter by evaluation type: Filtrer les évaluations par type From 37d49e1123595ec9cabe155e7a7f9125c7c6d455 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Mon, 19 Sep 2022 11:46:53 +0200 Subject: [PATCH 054/120] fix goal aggregator error --- .../Aggregator/SocialWorkAggregators/GoalAggregator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php index 21c13cef9..bc534c678 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php @@ -41,7 +41,7 @@ final class GoalAggregator implements AggregatorInterface $qb->join('acpw.goals', 'goal'); } - $qb->addSelect('goal.id as goal_aggregator'); + $qb->addSelect('IDENTITY(goal.goal) as goal_aggregator'); $groupBy = $qb->getDQLPart('groupBy'); @@ -70,7 +70,7 @@ final class GoalAggregator implements AggregatorInterface } $g = $this->goalRepository->find($value); - + return $this->translatableStringHelper->localize( $g->getTitle() ); From 91af01336a1b21acdcf06e972cda9c4f6a3c2aa2 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Mon, 19 Sep 2022 13:46:24 +0200 Subject: [PATCH 055/120] fix goalResult aggregator --- .../GoalResultAggregator.php | 78 +++++++++++++++---- .../ResultAggregator.php | 14 +--- 2 files changed, 64 insertions(+), 28 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalResultAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalResultAggregator.php index 86bfcd71a..aeced53d7 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalResultAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalResultAggregator.php @@ -5,20 +5,26 @@ namespace Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators; use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Export\Declarations; +use Chill\PersonBundle\Repository\SocialWork\GoalRepository; +use Chill\PersonBundle\Repository\SocialWork\ResultRepository; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; class GoalResultAggregator implements AggregatorInterface { - //private ResultRepository $resultRepository; + private ResultRepository $resultRepository; + + private GoalRepository $goalRepository; private TranslatableStringHelper $translatableStringHelper; public function __construct( - //ResultRepository $resultRepository, + ResultRepository $resultRepository, + GoalRepository $goalRepository, TranslatableStringHelper $translatableStringHelper ) { - //$this->resultRepository = $resultRepository; + $this->resultRepository = $resultRepository; + $this->goalRepository = $goalRepository; $this->translatableStringHelper = $translatableStringHelper; } @@ -28,19 +34,35 @@ class GoalResultAggregator implements AggregatorInterface */ public function getLabels($key, array $values, $data) { - return function ($value): string { - if ('_header' === $value) { - return 'Goal and result Type'; + return function ($value) use ($key): string { + switch ($key) { + case 'goal_aggregator': + + if ('_header' === $value) { + return 'Goal Type'; + } + + $g = $this->goalRepository->find($value); + + return $this->translatableStringHelper->localize( + $g->getTitle() + ); + + case 'result_aggregator': + + if ('_header' === $value) { + return 'Result Type'; + } + + $r = $this->resultRepository->find($value); + + return $this->translatableStringHelper->localize( + $r->getTitle() + ); + + default: + throw new \LogicException(); } - - //$g = $this->resultRepository->find($value); - - return - $value - //$this->translatableStringHelper->localize( - // $g->getTitle() - //) - ; }; } @@ -49,7 +71,10 @@ class GoalResultAggregator implements AggregatorInterface */ public function getQueryKeys($data): array { - return ['goal_result_aggregator']; + return [ + 'goal_aggregator', + 'result_aggregator' + ]; } /** @@ -81,7 +106,26 @@ class GoalResultAggregator implements AggregatorInterface */ public function alterQuery(QueryBuilder $qb, $data) { - // TODO: Implement alterQuery() method. + if (!in_array('goal', $qb->getAllAliases(), true)) { + $qb->join('acpw.goals', 'goal'); + } + + if (!in_array('goalresult', $qb->getAllAliases(), true)) { + $qb->join('goal.results', 'goalresult'); + } + + $qb->addSelect('IDENTITY(goal.goal) as goal_aggregator'); + $qb->addSelect('goalresult.id as result_aggregator'); + + $groupBy = $qb->getDQLPart('groupBy'); + + if (!empty($groupBy)) { + $qb->addGroupBy('goal_aggregator'); + } else { + $qb->groupBy('goal_aggregator'); + } + + $qb->addGroupBy('result_aggregator'); } /** diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php index 2bbd8445c..8f7ac0624 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php @@ -37,19 +37,11 @@ final class ResultAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('acpwresult', $qb->getAllAliases(), true)) { - $qb->join('acpw.results', 'acpwresult'); + if (!in_array('result', $qb->getAllAliases(), true)) { + $qb->join('acpw.results', 'result'); } - if (!in_array('goal', $qb->getAllAliases(), true)) { - $qb->join('acpw.goals', 'goal'); - } - - if (!in_array('goalresult', $qb->getAllAliases(), true)) { - $qb->join('goal.results', 'goalresult'); - } - - $qb->addSelect('acpwresult.id, IDENTITY(goal.results) as result_aggregator'); + $qb->addSelect('result.id as result_aggregator'); $groupBy = $qb->getDQLPart('groupBy'); From 6bd0bcff6ee12b6d4123ee456c929c28f3451a7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 19 Sep 2022 21:27:07 +0200 Subject: [PATCH 056/120] Fix: Workflow: the notification is send when the user is added on first step. See https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/813 --- CHANGELOG.md | 8 +- .../Controller/WorkflowController.php | 9 +- .../NotificationOnTransitionTest.php | 102 ++++++++++++++++++ ...ntityWorkflowTransitionEventSubscriber.php | 24 ++++- .../NotificationOnTransition.php | 30 ++++-- 5 files changed, 152 insertions(+), 21 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationOnTransitionTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 994d4bd61..d635f2e06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,12 @@ and this project adheres to ## Unreleased +* [workflow]: Fixed: the notification is sent when the user is added to the first step. + +## Test releases + +### 2022-06 + * [workflow]: added pagination to workflow list page * [homepage_widget]: null error on tasks widget fixed * [person-thirdparty]: fix quick-add of names that consist of multiple parts (eg. De Vlieger) within onthefly modal person/thirdparty @@ -19,8 +25,6 @@ and this project adheres to * [household]: Reposition and cut button for enfant hors menage have been deleted (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/620) * [admin]: Add crud for composition type in admin (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/611) -## Test releases - ### 2022-05-30 * fix creating a new AccompanyingPeriodWorkEvaluationDocument when replacing the document (the workflow was lost) diff --git a/src/Bundle/ChillMainBundle/Controller/WorkflowController.php b/src/Bundle/ChillMainBundle/Controller/WorkflowController.php index 2803ff254..a4e2ff8bf 100644 --- a/src/Bundle/ChillMainBundle/Controller/WorkflowController.php +++ b/src/Bundle/ChillMainBundle/Controller/WorkflowController.php @@ -318,19 +318,12 @@ class WorkflowController extends AbstractController ); } + // TODO symfony 5: add those "future" on context ($workflow->apply($entityWorkflow, $transition, $context) $entityWorkflow->futureDestUsers = $transitionForm['future_dest_users']->getData(); $entityWorkflow->futureDestEmails = $transitionForm['future_dest_emails']->getData(); $workflow->apply($entityWorkflow, $transition); - foreach ($transitionForm['future_dest_users']->getData() as $user) { - $entityWorkflow->getCurrentStep()->addDestUser($user); - } - - foreach ($transitionForm['future_dest_emails']->getData() as $email) { - $entityWorkflow->getCurrentStep()->addDestEmail($email); - } - $this->entityManager->flush(); return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]); diff --git a/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationOnTransitionTest.php b/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationOnTransitionTest.php new file mode 100644 index 000000000..3aa2c71b1 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationOnTransitionTest.php @@ -0,0 +1,102 @@ +prophesize(WorkflowInterface::class); + $workflow = $workflowProphecy->reveal(); + $entityWorkflow = new EntityWorkflow(); + $entityWorkflow + ->setWorkflowName('workflow_name') + ->setRelatedEntityClass(\stdClass::class) + ->setRelatedEntityId(1) + ; + // force an id to entityWorkflow: + $reflection = new \ReflectionClass($entityWorkflow); + $id = $reflection->getProperty('id'); + $id->setAccessible(true); + $id->setValue($entityWorkflow, 1); + + $step = new EntityWorkflowStep(); + $entityWorkflow->addStep($step); + $step->addDestUser($dest) + ->setCurrentStep('to_state') + ; + + $em = $this->prophesize(EntityManagerInterface::class); + $em->persist(Argument::type(Notification::class))->should( + function($args) use ($dest) { + /** @var Call[] $args */ + if (1 !== count($args)) { + throw new FailedPredictionException('no notification sent'); + } + + $notification = $args[0]->getArguments()[0]; + + if (!$notification instanceof Notification) { + throw new FailedPredictionException('persist is not a notification'); + } + + if (!$notification->getAddressees()->contains($dest)) { + throw new FailedPredictionException('the dest is not notified'); + } + }); + + $engine = $this->prophesize(EngineInterface::class); + $engine->render(Argument::type('string'), Argument::type('array')) + ->willReturn('dummy text'); + + $extractor = $this->prophesize(MetadataExtractor::class); + $extractor->buildArrayPresentationForPlace(Argument::type(EntityWorkflow::class), Argument::any()) + ->willReturn([]); + $extractor->buildArrayPresentationForWorkflow(Argument::any()) + ->willReturn([]); + + $registry = $this->prophesize(Registry::class); + $registry->get(Argument::type(EntityWorkflow::class), Argument::type('string')) + ->willReturn($workflow); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($currentUser); + + $notificationOnTransition = new NotificationOnTransition( + $em->reveal(), + $engine->reveal(), + $extractor->reveal(), + $security->reveal(), + $registry->reveal() + ); + + $event = new Event($entityWorkflow, new Marking(), new Transition('dummy_transition', ['from_state'], ['to_state']), $workflow); + + $notificationOnTransition->onCompletedSendNotification($event); + } +} diff --git a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php index 7074be14d..2510f9460 100644 --- a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php +++ b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php @@ -45,13 +45,33 @@ class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterfac { return [ 'workflow.transition' => 'onTransition', - 'workflow.completed' => 'onCompleted', + 'workflow.completed' => [ + ['markAsFinal', 2048], + ['addDests', 2048], + ], 'workflow.guard' => [ ['guardEntityWorkflow', 0], ], ]; } + public function addDests(Event $event): void + { + if (!$event->getSubject() instanceof EntityWorkflow) { + return; + } + + /** @var EntityWorkflow $entityWorkflow */ + $entityWorkflow = $event->getSubject(); + foreach ($entityWorkflow->futureDestUsers as $user) { + $entityWorkflow->getCurrentStep()->addDestUser($user); + } + + foreach ($entityWorkflow->futureDestEmails as $email) { + $entityWorkflow->getCurrentStep()->addDestEmail($email); + } + } + public function guardEntityWorkflow(GuardEvent $event) { if (!$event->getSubject() instanceof EntityWorkflow) { @@ -90,7 +110,7 @@ class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterfac } } - public function onCompleted(Event $event): void + public function markAsFinal(Event $event): void { if (!$event->getSubject() instanceof EntityWorkflow) { return; diff --git a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php index f7854c93f..982a7024e 100644 --- a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php +++ b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php @@ -52,10 +52,20 @@ class NotificationOnTransition implements EventSubscriberInterface public static function getSubscribedEvents(): array { return [ - 'workflow.completed' => 'onCompletedSendNotification', + 'workflow.completed' => ['onCompletedSendNotification', 2048], ]; } + /** + * Send a notification to: + * + * * the dests of the new step; + * * the users which subscribed to workflow, on each step, or on final + * + * **Warning** take care that this method must be executed **after** the dest users are added to + * the step (@link{EntityWorkflowStep::addDestUser}). Currently, this is done during + * @link{EntityWorkflowTransitionEventSubscriber::addDests}. + */ public function onCompletedSendNotification(Event $event): void { if (!$event->getSubject() instanceof EntityWorkflow) { @@ -65,23 +75,27 @@ class NotificationOnTransition implements EventSubscriberInterface /** @var EntityWorkflow $entityWorkflow */ $entityWorkflow = $event->getSubject(); - $dests = array_merge( + /** @var array $dests array of unique values, where keys is the object's hash */ + $dests = []; + foreach (array_merge( + // the subscriber to each step $entityWorkflow->getSubscriberToStep()->toArray(), + // the subscriber to final, only if final $entityWorkflow->isFinal() ? $entityWorkflow->getSubscriberToFinal()->toArray() : [], - $entityWorkflow->getCurrentStepChained()->getPrevious()->getDestUser()->toArray() - ); + // the dests for the current step + $entityWorkflow->getCurrentStep()->getDestUser()->toArray() + ) as $dest) { + $dests[spl_object_hash($dest)] = $dest; + } $place = $this->metadataExtractor->buildArrayPresentationForPlace($entityWorkflow); $workflow = $this->metadataExtractor->buildArrayPresentationForWorkflow( $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName()) ); - $visited = []; - foreach ($dests as $subscriber) { if ( $this->security->getUser() === $subscriber - || in_array($subscriber->getId(), $visited, true) ) { continue; } @@ -102,8 +116,6 @@ class NotificationOnTransition implements EventSubscriberInterface ->setMessage($this->engine->render('@ChillMain/Workflow/workflow_notification_on_transition_completed_content.fr.txt.twig', $context)) ->addAddressee($subscriber); $this->entityManager->persist($notification); - - $visited[] = $subscriber->getId(); } } } From 4d192d748f97a815be5f4bb03bd8a1b0a808a383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 20 Sep 2022 10:02:22 +0200 Subject: [PATCH 057/120] [skeleton] we do not commit composer into repository --- .gitignore | 1 + composer | Bin 2268732 -> 0 bytes 2 files changed, 1 insertion(+) delete mode 100755 composer diff --git a/.gitignore b/.gitignore index d0a16a46a..7e0372d3c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .composer/* +composer composer.phar composer.lock docs/build/ diff --git a/composer b/composer deleted file mode 100755 index 0e7ab8212c170ea286a13f6113154981bc8b7a85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2268732 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

^aU%HVHRhf*uGx1=GmGvvlq! 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%0DgR~@9M+Yn7_@#vB11TSuY@U;S%|2GrU*9E{ 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

  • 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%0Ad#TRFA;mvM*4-=aJY;$d_-@m1U>gQw*SzWcpcG z&M2Z)ewt(_t2CpJYNW{|@)Gvd*COqV2+2qkgjWmim^C~BB2Fs4WN{2LRBM+ew4`ah z>mnYZOv5FrYZSs$sjxU>2!g7?3EvBLF>tBK4JEy8EgX_5bp>i!6FA&B-P)J%JMX_8 zUZCK;<-C^GhQeL%r(XF^SEM}JxE7d*c$YE7<35BRc=)4Bg|zcsuz(=srSvW!M4X8r zWh6Wz=DLUtX328pvg90;YABWlA9?ze^zOsQI_0%y`|MxBG@{;o5vfTqT`5gE?F=<6s*I&9E@XBPLjZRRtA;}v;lYr$1H`1^ z&e7ozw{@KK-}Z#RgW>Y2Rt7)l%eSG7AL0kQCkzMzTypOAVf#&Y;n#Z$|M9byk>hPH z3Cx2lmhMi&BZqcfD1pZ?P+my|dWJ6A(0H_Hg+@cS2?oY3tJ#PE&W4Vc7#QR8J_Bp% z2?}js)@E?Em)P7b95VSZK2a{rycuCZ^&C4xdOLsK6Pvho1gxhHyYQg*3(6C~gxl8D z`W;V%tY#4%yt+6)yEvbU8n>(i*0H3bddM`XyDgcCN_OlxG9P1vzu?9|MLcgufXIKh zxHH(nH3~tu2=fFir0j~$#)1%4O^Zw>=w=rsSOJ6Ehx80mQ;hU0pgklQpizu^;4O-{ ztG~dgha-g&bh7aPx@y=UZmLQ`lfZWxbLyH;1zY>EWkiTn=7mpziJ;&nD1@032n)54 zMu0E4;(4V&PYQfirpfX%feHzA?ugIVV^>j@f>+)#{&nObqp~=x?GS9(*W=_=!_-p9 zkmc#E8`E%%YaoJwn**}~4X2&CW;7epo>7yZ-l5H_Qpcy%aus`uy|`Pq1)8@)@Fxmy z>$H2xG+r!^#C^cBt0hQEbO!u`2CM3rO9A3719(qy>t4W5-wC1nGa<6JhSQCI_m+!beptnBZ|iyN5* z7C%|46$hNKUl7Pjg`9(C+2W`YxRcKePi`6G@(Buarm}D~03z&r&{|j+pvDRiM*uk; ziarhAQ8Yq{?0z_nVyty&|RdKMvKGbalOX1H*?#a7Y#JDFW}ixf41ju|^6ZTn~g`a=YY|S0U>1jGNwV{gLbT z1?J9Pey&&H1rPAwVdm(xUvE9OE8iRNLzP!kKTGoDxlxG{e_(dOM==#9_!38nF^k(z z;&y2Qe*xw38EW{#P6I94`L!5k&~um)N)vXE#9M`bWp8b-@KZy~@UJ{~JkUY-dIBm$ z^}PWuY6L-o49|-G<4W++k3JKKwDV+gn3~{HepFj}v-7nWo&u`bheDygtZiGMfLR}= zkoIO6B0!=B;@+%)dYPDH(Tw96E}8>}CEfITXBawoASh(AB!JEvTxly7w}&6c22QjUs4$Bg$q%YCwl&+ze(awI*HK|HE1rYi>&0k;K4a`A zx7jCfsC_TKrm{oXpxIO;w(@L$s#;KcpNPSi1E(*m(@He#GW~yrL?oVv)`2EA#WJU2 zd~c!aWH=nA&M!s-lm$ReLC@kwayIK7D$FM@+aTou@eB=YBPmA=>&dJu)-IPsKtpJ- z2SbF6iwo}T3}xEQdez0H7_2|*$Ai0a|7?5Fzs>CR)PoOU451l>Rx|i8SR1l?(hAR%lbwy<~CoN;>;B!i9A&Cf!(qjk#8sZa~OlIh*rqS>- zcaw+5CO0HxQPDadf8jxD*bg$ky0X#@i3J(UAhG{BIei8%HqM^y<@5!ED;Hr5P7knp z>|exnIA9qZ|0bl&Cwp(CgV*#IRJ}sjE2^{jhpmbhr6|ij7oS1#0Zw@0eIi1se+V5b zE=g9V z^~3hg&eG4qGnO9S{tcukH`as?N~I{iV8@drN;|sJc{@n6Nikp`<_D>vWeCvx{5Z-X z$$e4bXcT9liV^}}Ln4S*2DpOf7}c)VjgtmWyRlJN;Ae3hMV$FOUL##j1pvx}L_Kgn zS=BvL?!I}M7VF(iWE$eaG5W$?uP43kV0G}0-h}J884CY_ErKVdslJBLb}HuGfS;jZ zp4WCZ5Zh8={^1QyQ8+|t^sy z)Wh@!=esQo%_d{N3(YPXV*(sp)u($1H|ZWSB<0rfEeHeHd-2a}h;yCu5kySymoDo{ z^RTyk|NfFtD-?i#T%f{#`CeWE0Ic6}FjW6c7tR=Xe8BryJx(qN7?0l%M+YnpvX5R+ zBa8EKXU%PNN)8`IG=I5meU<>WL%;j%3-1fXb1nz_FZy!DL4@`yE0tZAD#?(>uYI1bo7SQbWJ5n)6i{={m|_s125P4 zHQa!5Xu=@gm5@aY3-iZfd(ecy!u-ZZ8%-ldQqb zx()FfJZwz~49m52|KN>yDP4h#sJ`G>8qMc&{a-^)Amc-LC|+P6)Yo9~@~5(F86S`~j=LNBjdQ z4B<%Z%|HC}$E77a*jdznap^h_0ZUUHJ^u|oHCSW|#FZg1#PdWi4N>b0@urcSY(t~n z(oY`PdUDhs^-M#L5irVZh>boabxcQ-j|2u<7sbI`6Nr2?gj8J&9wUz#(FvVk9n~bF zI7^X3Cu8u{Q`n&_ZF`v!Fy=CSS~Wa5y%fg8iPlH4X=3x9UF;Q~$s1-pq_y)v|G^%KPi8bp zN)|NK9kA8Z!l+i>{JBbzv}NQ!>C;kU&fb{^E$Km08JR7pig~r zyFXsR9l^+*s=d4jsC)qb!5YFwFr@sKtQ6#HU;?pe{5_gXF{);akS=fhuX(-EpLo5k z=pC8jpOJV1!wnuoyn8V``e5*tFwc)mkQ-l;gDC4460rkJa9_zTQ5dM8|04uOqOn>e zUU>a&d>w832st_*o#UMMzBlSb?-!MbZ*b2K%Br{$!FwWHy8vaXHX|xn(i@$I@87>C z`#1J3{zIEp(olSh-h?vDn%vC6z8o+0RtV722h{NtR#;X#jq9E1Q^;h7Rj`i*h06j; zn*c7nO;a^>l$q~Xj!isqDBFSWaVYJv8%zS6@IE=%iUaFM$745+D91J7B8r1`Cz*vC zMZ};qD6t+KdLzK=b$+&u`BUy03LBond%_0DkTtZ%QH$A`PHGDPT$N-K;aR?Fc7NyU zQ^}|-e!e-JF(HMS=OLhz<=Ws8crcyY)C2g`njWA!lLL-iEi$zryaCFUv%c>fr%v(h zl&C4Uieq7P!7HFFJ_568vo25a2^Qgwk=vnB?E>(2>D?bQ|9HM?{d@uv)Eh>w1OJ+y zIBwEa5HDOlRPYN~y(3kxOlyrmC>p8-DBbEx)GD9}P9H5e=;0>gRLiLc1Q4_m_7|PG z6Q~nJjM^r92vTA5XexjRH6*UA>@(Ivfm#Z_-5XJePcOSTGht5aRyLAo;swlv<(PEE3OH2>dXi*hj6>Ue;MV4&AdOmH+P zQs94E;$ocISik|w?Q0yfM=oVcNXX8+s@O;$k;a61g0>2G& z6O4`UFD9L|$7KEFm(pm2i)sGev;D+pfw7-IjfVp*pO9>F_?%zZ4{k!V(bO{n zHup)SqQFM2|4178i3WqA76k4^1_Ea@OpS94+OsZga^#K#SFYkDFJ+K~MTH%dM95S( znAi;#7>yOdIH&R;f_B9;kE;tFXwpnE7%!RXvGnxy%O`6ue_Xso_1}PgiE=Y(2=60w znszoX=0eGn0Nl(T0^Tge=p&-!fm-Tiv>K1P>NzBu;Ohm}2w6Bp?i393;-C>YvA%a% zH!2uA8jG@U!d?M~duF9_e0C`DtY9K~>(x~{ZCo5bK!Ir-dBKz|3DY&l#r)-bWM-zAKr9!-rt$u8GpTu|LZK? znU`>;+iVWhl0$EU`FCf#0}JoY&pft|zy2~>d;O&B-`|%G!WvXeop#SUNUQ3f0)w+v zZSS;i{rnI;-o^jv4_Tx1dY>nA#Zs76PlXvO?BpURg!PNv5Va(nyS%Ab;*Er8q{o3!v;B1h z`R=(v|a2Emqyc&6iV0W1)_H@clSp90~D8@P7+21T!DV7U&_TLFsSevh_MVi z1(9n!uGTe`G6*GO8lr*K8@=eVd8{IRdI9N~kGNBMmHBXPQk9nT)FbJP%2kLSjOP^? z4oopoz=@tj1??eR-9NM!!Cgc@L4Z}XDp3B{PQn0!tVDlZ(WDYZ#M`7AjO#|Sz-hXX z%^Yy3>PFUqH3172DWAk#Rzvz?kvgh}ecYC&Dc@8$2@<(uEi#}f%!PZ&8wjQFuR}1WuL$l5`yNd~*WCNqLDT-b6Ey*fKl+?_03m{_ln{R; zKKxI|2%YonIFYm`R_ITG0aZqk+MNjuG6JYFC+Yi&Ri zVh+P?xl=nysori6_bA#k#G+Zp(Ig|ZPe3i7L4jVMB|0aIZTj*}=catp;-JuD6msyn zJ$A;AHlW+lkd&rX;@Hv~BR(y4{xCT z?<{JeGn#?Ipi<=pvNGO{rl*jFgCP?*ffM!9mVvLG-uR=0WqA57INiT9udy1k0KUCp zkrrBDckM!Mm9I#f&|wmzzZC)q8d1uw6JGFKh0oAyPXgjHIcD zes7Lg_kyN5(Pu?tU+#0ng9(X_H|1rG5>lz9tlkTLB4FW72x?`trGHx4W-RX6#a^E9 zf(IS~FiXCR?oDps1b3YaR)@4$Gl^g90VvqK?0=lj6RkOM3$67;Jd3K!i^qR^E zx0wVfpMWlgnJ-02W%eTiLtE6>A5I3N?zr(O)syFLkN1%#tuajuONz>zzIcfniX~nt zypZ`(68A>p8&tqQ(+BFDkS6*)y7UPyFQX>Tfj0b!R}bKx1N{n=_SXfe5WKJ{)3KE8 zVs2R(9Gkc(O4k~11?Bn9P^+tX;IQ__fe|94bF}PG_w^n04-Y%`N_&AENLNGDRe|C# z5Uck-N-yF{5^bp;iqSlKu49R)U`(V@R*9rZsqT0&XUg0D#sbYhs&GikF#`j7i1wCS zr=@bVPzy}K8pIY4(5QiM-o9oE9+}0dc-qy@0Vj#TEH3>TQT7pDndjyDln|W zkYig?d}C_W@<|2DYv=)ao&5oCf42e26-s}N(Hm|Mp6*H( zmYFJY68iV;fBZ+nN$PbXK)?h3BKQetc3GOSe5z#K@-7Kc04A3~n+jcBvS{@Y0^So2 z>WqKnIUNaZ{Xue>FPp@u=Ny!7NCK#xDw6ndk9;n_F;Og5KrRo?2i|^IHZ$CVkAXM7 z(+(WjaUM=ux1REA2k?XeHEZ^o;}swKa1*8+vxh^Vc8DOtikhH@HZ>PW$2gt|pLQ*J z-|eq8k*xLDiPKcg{%TN=5gFNmwW*JjE{o()gS5k$MHkI@l}NyFtW={IeBcHl9R;x_ z{Abd)oEA!+M2x>-Q5<+O)}B*&T~KieMrfrx)*6sAC}{GM(*5VJk`#unDUeiyQsA<| zvgb#sXvvl(=`o6A3{hkTZhWRkt&Uu_PBq-rJZ)V{4}=e((EksRs#a_CE^SxVHm%61 z4QUAFU+jiFA&brU9^nZO=O296`g+tm`QaAtu(~C&BK*RmPf`s$jed~Gwa=C!b>-A1 z2D9#XL~D`Ubd`LokO&n&DthxIs^{Ikx;M|NNmSq&A&&}QS%Q#Q3J=DupZ3T41X$1i z{0N8R<#F#6w#ws6Klb=@?DZ@6F*{JXJSU<`7CopcjT8gh@n`vvi(NFttfr zO5jk^CK3lLQJxPGm!NY8RlTFL4LaIf6O6qk~ssxIvY686he^6<_4!nmp*r>)haikWhwav9*Rn8tdhtem*6{v#qUlsoXO>#L?tORPF8V z@k9DEVi_Fh+T-5A6S02-+Ag#;5~-~qgSd@3U*TD>&ci1vpWhdyOU>&DYqU-*fo4vwx<0omYm_8xJ=Dvh+ARCX9^g-y!?JYo!SI3EDoz~q;8H$C zIH6L3RLsUC63}7kN9hLPx5Frb3NJgQLt4Z@AWRHnBiDi5$ye=sDwl$1CG_D}zA@kz z`yh+*f8AM<;B7Uo!8X}P3;2mR?iSDzukW@9DZ@vbaA=Mt#J`zE$aw%2=?4f_D z;EWCh8v15wio4JgjlF?!d0@G3b716ve;*~Scy}Rco+Uenow9XC{*K1E59`a0Bz(?G zwx^WPGm7Ll83}y;+{mz`G+!^yJ+8;nQ=s>4zzFba5Y3($3TiXj2^NNE11JM`4J2~< zURNd+uj!!xDYz~Mp*)VVyp#Wg=9IloG|&76=_G$5$IY4{<+T03=|%sO(~B&#MI}Ra z><6EsVBK!-ih52%L})Br1lpOTXEmkMMBdlb5Al+0uA0}AaOcRBml!$7m^qsn)P~go zl0AAWC#Z7Bm-B4jeQ&{*$t=K(&Fl!S&GedVeNdDnGw!HKnaPW4drp(Xu|Sp7Bw?(kZ+}L$E{)} z?|XZkh(#3E!}kd&-8b@A4M)bq+aTig~Nk1S&n^l>;)BcPZ)HONq z*Cd;oo>EX(7DeSgNr^F}0Bwl~Pb2Z|0SkmVNaP2qwu-^*ELwtie$2W?o% z8u+tu{(MM={ZZaZ^2SN6TrU2)kBBTy3RbfS%MZwsjHqOd)--BD>*S+sxKEgQ) z;V#k`(g$V)uzK-dl=g3D^+#&Ny*Q34!l8v%|9Aocoe1U))Y~v)GQI(s*PY@~|8DS7xof za%8;(|6uXa>7XgPw3bUnbvoB^uP`=Tn$l;0KQ25y)K|T&Pu&|T0npOUbr3nuAoCt` z4@dQ&=+w5X>m$&!MN@H3^Q{M1fPqHPt`^S00Tb3r6mXAox;JJ?hQeGI(;sL$?tsjy z1}}0`1lYk86bNP=;{c=@C17A!Rc&tY(O2|I>utOJu0O_Q+bEiN3HwjN1*5VR2CV%? zQ@quVLYtWb&rL6BKM{zvB-7|*O%%(dJE-^4p-j3^j5IQMU93B=sh%b?D@)YmQN*%u zdQyo+?jnGypGRWo z6DZY9S3XxcRtZme-yIF0Wj^>(or~wgx442CRS1q-vfdWW`+f^MyFM#grId%e>!uN&c& zcJQ_`!W4G0{ywZU2i!D7I3Dy5yze|JuYG4MumiSb+qh+5=j zd7AC5D+n@0MNO1JQG)V~UcRPw)E8hYOlh6xi=4n1%h3Sw+E8dc<$`_dWWsLX&?fl` ziT?395*1t_n6TkDlcvxBlh22R!m8_pn6UIo?2nAg=-|`SjiP~lT5PntTt9mbYBfSe z6$tTor(7ViYKx$PD0N0}?r{(DNCQ^<84S&;#^*s5)lRMv^@16%<MG{g#zTIx`G+*}h!J)#N5*UxyJzSN9z7ynEPYX=V#eYx@(1B0M)XOo(H0k79FhZ7rL- zys7Cn@2>?@Wid(l(5g?6J;@OQ3kEjJSy5(x{@54_V4{Nayx`(l@DrBNH8s8c_~fC7iZ+2<4$tL zHXKK|b_RxS(7rQraA4Kt4tl*aurr}=$_zozG~VXR>9>*FjTpEAqi^w)DO-`Q1A$y$ zeF?i-qM0X0vtk^J22T;7FY}R(*0P%QUv_V%l+}|ad+eXxTujtLk<+|w+H}FO znz`%lfM0BFVMA`=9N;s%_8r^qi1FP3X&l1?%d0ycT`KYW%2dcHuJwn6#qDFI^uTw8 z?!Em~<1D+-qtaNeJ#!(gBp^{1_w>lFgJN#vVT%M7#!nl%CW(aS= zE-8-lYGnfquE;}Wgvr<50^xAdTF*ts@^Wvy-#r7p`G`QH-x6fZZtnAG13Pp^XqBg_tSf1lkf$9wl+k^vzu5Ks5dI(u9dA+|K)FxJ?kU5M|$R-P^4X1B5sK zc8ifkP3ZprvG?wMZCy#a@c;f43PW0=ZGr71-P26RHpV!n8ygPbq;tU0PauKS28oft zmr3$|_V;<}vTl1z2;1rInKM3TI%w~`Znf%OwQ6CnZgNj+WVM-LM**bW9NewTek~>B zRPcb2{KmIH!#JwVsFaaEHjlC{-_`xoaxvc21yihmaWa8?O66ikay`o`-E2g|8tw7* zbdgjFoPh+fDpGmJH_CSjP2c` zgz{a7#t^T2R)8phfEKAvgqNT9s%K6A6c?FfyG;WUK_ls)vp7RcmD6C25svnT;X^)!F7(f9RcpLRo)4)xI`I>ukpVzydk>x%-epihR`4(m9SzI z_8;(UD%rZuluyZ%_}>iS;_DeW7a6&kRGH@wdo8bw<1V9p)UUc;v7&+GTaPx{bl_5Bq2+)d0IGi@I)*b4Lu7wfVC!$ERhrN;ajQq5!}?2D>5b&7vc5W|RDM&N!bGp8D*HZ=3`FnCh2 zOGn1HJg*>1mR)I8ZK|K<*7HjwNoY*(NPp++^|Jxg%Ds#Jabj3GeA^G8P!8c~(XXVVg3LmsH`tu{ zIwF9BzdeJZ50$8_0+ip*jpi^ml(Z73pV^sa>Ul&o01^R-de>j@m)R~F6%>1lzI^Jd ztE@xGQ>78S;3-lcc z)LSoQv=aJcK2IgS88dhsoC=k;j;^xC_}z@o_Mr~fnAcfH`pt|^KHk=?IsF(HrCRc> zcpqESrv=L_T*zkI8&S=!>cS`C_=;Q}m>ht_yNj~ye!2L1>F}sFWV&NA30zHtR)74qKX%7Hz@f%_oqDDaHR_Iv zU0iG6E5~R3?(pg&y;;C#1|g>}XNp2_-{489$+>t!-Jyoejg^DP)-+Q7QPV1NsL!GW zGB{L3>Qt#H9{y!UiMvmZ4N3{N=qi1s-f!DNT28?^%;*%)Ya1Kn!SW=GJLbs#lZw9mGT3cW!l>N^l$uk@fj!M zpYZSEH46B>K_0e$QM{Fa)H-YraE(Ftj6NH+nFuS-+grSnjgUu0hEcs<4LU_fCMpbq zcrX?>oMxB8;5qM}p%^oO>k2NK!5Tueki66OAAEoWxiNez3MVzeth7Zchy;kZN$0XC zY@0&5Mt-HuD0XL#;hRu1jK4Ba*No&}SmSF4<95~zljW$)Wb^1S!~Uk@bqxhOaftE-+bw%Ierv z$(^D?-isp@I4{Q*;ov!qBjP-=L&wks5=4|H(8X_^3c2k_+Xer$QPBJgG_#nXLJc4sKE*DB{Z|7<)ARawGz%;=qrh00T|?_+Cbw3$Oe zPV{4-I7p^cjSb3pF;yW}oFIoS*eRekuXZ1tB0;U0ydGVhK`#`vN+TvE3}n+wAN0?U z5D?Joh?BN6U{$9Zr}{5IPh#`RU`iRh{zGP%l@$^u!;cMlrmci?W*jUE7sFo|z^KI# z=Z)jMJY}cF5_1Hop8d)cOsj+tjp3@2 z?^lb2!-q<|jubkoZ+{K*o$hW*p!Yvz*Jp1l>8*BO1)aj4-Z(^!Qj~I5SlB&(R0Ppa zTU}I))~)C@a6+Ju>c|H_wGg)Udhz{0CMgD`QT%4t42wFeUHd$Huh5tRgtw(Eq;or;iWU+jqVFw6| zFQ~-pnG_zo*c#!6ID$h20xFj+Pd1}H5(?B?FpEp6nSqudIq!(59#6&mvqz=`OfL5oNjQ(xRFHrl&Sbo zYizQ03IJW~iSXx7!^J%fgt%`cxR*>IR6K_mM^z(30+5R`osg1c>TR^G1Qai zXo^Lvb?7#f_N`q_St44wuVb1KHw^^s2Sn(;30l|1jqQ0=YeF5J0x`(HRFBGYZ42<4g9Xu_E+ z8>0`*!Dod-$n{Z0zl>l429|2?B5?<&rGO!D7oc?GXqY%iO^FWQ5b2WzCt)N?A3gBs z-4IWHtLWr4QN1iXtu~T5GiFFP-?W>&UV>f@ck=TrMLbeId6wPkQTIg@NOf=79_Kps zk~S!2@~?XYJ_HGZvp8K8XbVRqW0XI6C}opQa8(yrVZu6sKI}EdNg!i$3ZKOe^6$b2gArd46>Mb10zPznuowBR^Mm|J=)rJ? zUj+1_IwSugCIn74Eg*oM$mNotPSvC2>GZrfWAWF)6Z%l1w^qT5u}ciYj$G&0H(dCq zL^%Yl^7RMW%Hk`4^X%wG@S$@@RKm|4*GTQiY5SCDHnKzQ2bEyJKP3oIgKBfG8~YxB zMj~FBQ}iPv>#|n$}s32G*FyCcR6xRRWpNw)kG60q>nTp zXX7O@J@8IhWsy+0IFhtM`lzR7)7U56`fy`Plohz#TDy?TQlc35T~j8KvwzKLP{Yb+ zFqQYFNqXApXHzmI`?^rXN+pp16(gQnRn)#RMXM?>(sZbxz?29(7utmlt!4-$L|u1c zb>_H%e$zNC_CWvD%(TqWnDOc3;N+h1(LVh3lF7vpYsRajKUiNipVlO6%ozA3(?R@w zb?8}KL|c_J2nZTG4c(SLi%+XfM8v<@`5#Ovvx}lCUYi-2eY+d1Qx=%v|7ogZK>dPp zcGNOFG?U%`i>h!)>YnoL7V69P$A@dF4^F3ZG{&^21o<~#5$-FPq);=2eRA%#PIlg#Rh>_z}UuO3P$^ux*F2LK>w?& z>53sy%wDn$!uP&c%vV%6-{K{~udj~e{7ZAazLeMDcZ17kS9V`JkKCZmS@-%jC~T_? z+S;_k+2Cu33w7nP4z6t$zkYo>?`V}umYgk0fi%8-(3lDlFLK3i{e;Hs7JhsrPVr{zO&`X>RU&deIgMxlCdI}%q43j$eTO?uZohcnBWoE+Y*JMYhn^^-tgpO20Y88qS;_xCX$tG>}`hZK*zhqT4 z^LZjr3C3^+yTtdAJ(s=+)XaEQ=u8gmQ__#+OR8#vsbm|IeHhS9d%?VkS&JDuMCgP) zo*sS6u{ks-usPDEj$78GEZm#=ku1N=<+9aF85sSW&?Arxw}2{MA=Th8eey&iLDCdp z4ioL&Y2;rCC_vfY8Z7A*f&blrQ$x|OyFD|MqV*0M&b>G_D(W<9-Z;Ow{17$G8WvON zqSHDu)vQJvt?APQPR+;KeF$^~r&#K!NmRS^F{OGCqU2+`-Qf{9hWzcKv-rdxvm2$D z_(399a^XOlOQPMjHEZM3D<10&Ml6r}#yY^r`)3tTxORGHa8<4EiNL4{FoW3u1#BG# zGqr?_v<>KP9jVyTWgCCT{H%T8@8pwf(U5n(ko9jP!^x8SlUx;MbOLiQtH;vOulw4- z+I~>+-6*`p-I$Lja$I=g%+7`N!Qas3mL;9}bT^aZ;OYCC!3R5X zXbnw0Ql4?%q&Icr!uE9}i{0qN>C#3CoMjszM2V-<#6{z(y>`e~9mc;ad*D z0(}h@3(l{v`ZZg-rAKjisQ7d8jJW8}KKJ1$&`{$9fyr|22E%LjBHdq&44Sg7Jj&R+ zS2@Zac0!6C?zoFMn@NQBgYc1F1dPjM)f6{u zH{~(f<@T$Gz5ZL|2ax5F2#YqDSN^CRwCJ(*6S`~va^M{=P3bx7hO~9da0yk-rNp4rkj;CxgEu;keB|&n9n@H48Kc@W5Vf^hOwwS%Q*M-7tJpvpdDW zY_r%9MOHGQRZJBsS4K+YF~-j6hlu1+h&8uu6$i+L%o&tEpc{w+SoT)V_CtnNgay*4 zoO27mOolI!JX_so=S@hr1T0R@2FI5Zy^|AL-v!VSO%Z0xT`86!THFo{l1uV+O$sI0 zk<)pEhR}79){87_XuxQkQEavtcuWu{>7)4j+1ZD3kFp)ACluqGCCYE&tm>G7DVe{E zkn7P#)h1V_Q%RfZ+wBKbvxDL@?(b+eqTBSgeH85BbdwzKzeZ#10~Ux;4v+iLu&QQL zLgGdT)o)nz71WDc*oo1?<`F(oM3g;SN9k+a{>Za2U*G*Y>GE39V7f!udSPKQ5u>WG_VhhcbGEPh` z3S3B96)<|dKGET$X8@u}Hh8A-#6%%e6}-cPSq6sr==@tSMBU+8ZN!l97T5INxE(9J z$~vQq$a@g`M1TAfO%$7e*f9TmD@CzMMdV}2?1-XDv}1%N^*M^^%KOhCE(`= zj9;sW3vMDFE)^2ei9?&CA_k#BW^}iK%48-=b|gt*b@j={)3ujd`<=(TYcJNHb=I~v z*Y@;yZ`zNr4p$6dvr4C=Xq{}LwIV`69R?8d`RFH@$=fgu@QSyjbRNR@7ztr)ZI`ut zTCfm*ZXGWF>E}QGx}>+O>;djCuN_T-m zO;_uRNj9T||E5we|E4enh3RBp$S}&gqLp{8V97Nxbu%fRQ*R)wl9jC|eN$Ig^GAlC{bSs_ebqD*RWa{7lntJAq5cH$iz(**NCFe+N9N(M6I(PDH6*4um zm2PYwb$`k{CZ%Efq@r)D2ibjaReHVN^E{iplE7gFK zb~F=m8ZGXI#L5763FN4+G4mlL+YR?Zclg0>r=UxO-V0c5G&QBT1svZL9P#KNX&bk} z87($W#-no_bOfKHegHimA!s}I6~bW&ldI-?x>PjNOWk7B3Zlu05E}joD>opRZzJ;= zPIqGH$p#Yz`dMV7wh-x&{(cIKWE3+>`~;4P6O=j3eUm+TQporxcvxgYtIe4Arq}=E z{rR(^$J(T10No?1oSx`uo_81aV2V-=&{{k!I81wwOMP4&BdX0No>l!W=;wfxGi~F# z-a^#K^^i@9LWt#HK%TIM@?FC1D;y;*-GY09fSSQrS^7R5`ZhJDl4NH$4Bd;Fhq=Ut zpFEwd_t24Wt4A`PtD^{0s4_nnbqdgSl@x;T_5K!7gRDtgu3#8y{fJaWL z=AxOQOn;g%16#6*)oHVhmJX~;2{sbhSmYz%QKi&!q>I=i^ULNhCWXZjpRx=dO&3)BB+@iGAkHEnf`%@dlc;%NSPqP2dMDq&n;ep+xT0i<%>RbVM_nw52B#D z_Zs9B{mlN2_?hho`7TM;HBJ9U`#JEjMY6N!))Ee4!gJ{_CNn-veF-U=3$LGJAg?=nR+3CV zAYx?=y0{y&xaLYSy zGoJ#QR^(NBq-v3R;vJkTw4&2t9yKLhTuy*Th=CAX6y0%JfHgcmIb*^};9O+#Fnueh zx@-!I>;<~1v;w;^8Q`88GA92!ysRAXG3fB)04NnyRWJ~-ZP_(h&T*_6?Kih;T5`*# zwV{D3vbLt(JQLm?4{+^V+~=BZs;Ja%6fgtESYEI+>D zCES(ntP`MXs-Q#w1;h^SiRpdSO-=N0nP@8Al;l{=R$hrDt4vLN`01>09nm%GnAVZ3 z$Gegt`Q(P2iKOa0L)n!qW1@^g-qB~&*xB9aY`)mrU)$R1tUrH(U1-9ih$4uhMz@d8 zdkAs*Klo+YWP5MF(q=TdoZSfHyxfseA;~a$Mg82~MqjYHg@Zaw= zst@n|q3-GZuk|UXRP^%I{kjHU<4T)q=U>+j`Hc;!mpFXH-eg+Q^=0f zRlc!Oe`tv75wLw~!!)tws9(VS!LGd>^xyH%$*_BYidMYdpm~q~bDuGkMDxkd_~BL1 ziLkReo%jUjb1-K5kvX|0Pb4Pb*cZ?)Rf=?ZeiZjk<5^Z;uRJ@ONXUGOSn#xCwmM^e z;B$wNF12YYD7p?2^CId@-s)pom@aj)7b1@w@T_3Nk?aeBl?XCi#6CizE3Y|n7Onk) z7jZBqwEG=aX=frh{Q-KGLecVZPG&4Tg-zi2L`-%$2x*4)(y8qloJeyHYga6w)bUjG z)&mBg`A7nz66DZ`9OS-0ZZD_)Ma|r`-m-rQ?X^gZpdA0M^vjwLoDBB(=EcKG5d zWu(bScX&RyGsMxk@!%LIShN+-SvkO&X6cWYcb@HZHnz9atx)a)&}a)XgV923fPJHv zR7lMUnOm+njqlB!Dby{MhoV2ozds0_#dAz4iMhZEnBlY(7cp(Apak#uSmmm&5*h-1Us>LXmKCm6WJ$?js=^sjf_j55985 zoZUESbl;+EG&v?}>W-~k?z5fnDbl!;>BI~hXNG~rRlww!ZD4djkwb>{*KiF4cR8E( z<=Mp2>jdq=jF3$?x*Q#kSeHRcsDYCtSK|~fSx^CNuV(bPwK+#swiQQVVZD_Vtjn#n z<z|GYVh9%yK~2l!=P^5=(qP z>z_gCp(&8~sb>_7^{O=}-*VRd(C?}BlNSq*5}AUwB*Ii6Cf*SEgGcliP~y7uN+e*# zBCnNnTw6KbjrQqXP=2us5BlB?_1niv3Dv zW58Y?AnVyNSj2CbkTit)0tdn+hybYH9ZufiU`1d`%)jYQ-mDEt@2scQ$4w|tMx%va5}7pu zq1J~bJ#N$*E{!Bl=FTlLca)v^(2T|S9iB)c#&cQCwEe;Oad+aEfPb%N`;QYXuq&_w zWGL;U?qqO`YwH}-oCk5wz)f;8aeQ2=Q~|Fdu_6T9Z=Ss9(P&vVA9zEcJ z1U!War-!ix08kfkFc+FMcFf%U5%Jk6o*VKP+EnPhhFxnlq;_v~j@o75*ol_1h@sjX;PBu`wdsaYXn7!J62dmgH_FMxk6j)CG^gYy9-96C$7W_g_5__F1zbZSf* z%I1aNWjENG&`+foT{(9U2HTV!lrtf9Ppzp=Kn+2G6@XN19WCi#*fQShH1b?XMXzDIyL_B9bB`2&yU~w?(s41kA&MnCGD8$hP8Xn!PHeVbQ3eK-Z=40ZtX@KvwyT%hHGs&0&kGp_E zayi?@gX`I@%DK45(^(x8*jtO-26EBZV!C*fqSJ8~*z^E5o=zt3Mq?Z;4|)J54{M}- zJZKARq^riLAU+teH5@pJteJ^93&9jlZbzrJ)0AsKz@M-=3mB?pPChKX9z1-dE?5#iBc)r0@|A5i|C}Oe^RZ9&e3TYiR zC9S~_UanH>UJRD=Pee>YcYQh<3y;|s5$I`Yq3=dBZ>BDu1lOixr;cy~m`Nn74hhof32Egm{>w%j|Z1WSI2Mq@G3YtU~BYG7>x4&^(?Y-cD3TBQ7FI6Dra+h?KBOOYr}>oR%AhqY#JDH#YjyE&LCuG5@NzMxq>PS2OWm#1SW3zaOLoYx;2F>vqvRx(6@>9ATR^`Gi?kUmVCQa z%bl6FQ(@X)i6?3*%bu>t3BSfH9t-N+6rEIhXU#v6Db4IF=SyLqKC29`M}UA`DxoUy zmm}%sd!Y#-o*!e_5%mj|h4^EJoilou!6j<#Lmka!MFT7tI_ZC24XDy5$P9{FJ{(fL zbr0*6RAvIUBZeGrk^@gbu>;Zblh%FMI#Fq%7j) znmNel5%nqr8TEeA#V4yBpa-9i$O}lM0UYh8E zn#b>*zgND8+vgcl?bpQR;f;4pnX<+a?_oIQGmb1jObHv}qjBWK6O{`w^^wBq4tJVJ zM40?SZAN_-gTt@!2PwNt_ZM`y(4jnx{cB@-?5ge#_*e3IKNJ6jr@xvv12Yc_5EwmO z8bZ}^{c|V^Q5@nJ5q+A^VSXV)7?4+fawA(-F$d_Q$G7*b*eovB|Yk zo0J6Q)y8A_j#f<xE_ z5oaEoh<87-N?h>KQQV8zpcpWZfp-hgiXD_73bu*a9d#y2X|}j+B4(}NW7(@>gPSsB z!?R#H3mfJKCJc^D)2V_qRAb5}v$MPTJrYIQ``drrcme)Whm$v(g?l#U+1kPoNYR`d zC+Zz*WIc8je06mjsWs2;ch=U|H}>{AGHn`7{5;okwKLtDNMT{ug2!XBBbT>e2vyZQ zyb|xBoooB3-js*{cAM`*g#{Tkg!Nsshe=S39q(N-ZUCor{|{ex`a?#n)O1gI3MSTE z6>mj3J&J=28P*CuMQ^vG1jd?sIZ`S|eC37yq~)Z1H(;1YsQE)MQ%rb4n1HhTnIOcr zhvaI{EmBcvq?fw%z_w2 zdStGVHM*oVF<&+A!5jHd1%^l^=Y$j&d3bc1e0Ow13c}?$W?ivgx`Xh4wmo!yP!gpI zh&PsJs#jEkfrFXUygv$5F3BZOC>Axm0W!+HF~z62y~3O`&yeSaX_4_`4N}EMyTJU9 zoK1n{g@@i!&{haONm0W`wjZz- zJRMHHUurx+p)V;?!7t_6!B^B@P$&o10n-{aygpwt80}h&2I38A2ZvALIUbEa+$5yE zZi!e~KhB78z^iI+2plXgFmO)|bEg^bE{sCDR80Ot$c>(ObQC_ylLpc^D$HDEj4gQW z9=@Z=cM0BIE1IxJdMzAut_(v~Q2n{TqI$xLnsOZ(2+S}3E92cah_BCa=gPC0DQcLy z1sJ(d`*pLZy>TytHo9OIhJ-+7-V0&P@*QF8;t4u2d|O>r=TE+(%x9+xV`Qnes72jH zO?NNB4Zrt3wcU_P=SRVmE6aWzG_&2)T4_dstQ&=%bN*P{D&cG8B*@TdK;)sDmx)cJ z>>E$ivzg(USR^)q;MP;q;I3*(x)$4*_5;5$oF)i71(LE6d(^cmqLN`>T_RVYlzdYy zQ%n(@vhYW0Qi1mb?6=tlR#}~WB-@a{U{2Px=-ZS{v%(Ar9$tV z?B>EPL;=yOSjp`ZBv~Q)E7q?-msjS1s^o!6(_78+J~pr?kD5%X=Uq7fEc1%>;VFtm zTy}UH#<$0313c~|0VRBm6k_ICKp8NfI#K|1J!P&EB~!pretgh`h=4O|(isd{os{uB zzmh}W*I;jS!yImnP6x+HF_aBUM?+XrC!-i55%6~~Cn=F^IJv4l?Vah?6RhqILg0++ z{T*2hL35SfJQ*<|FOt2y1BG}|oT@~yf<Hw=6+(I1e&) z8P!S!oZUvCy@qSaD{khqdcu*M>=_})CMb*L3h&1u)M<$TeZI^)jU#0I^aj1*Vbh5o zTZ&C?;h4kuhb74iIKttB1{5<6YaoGls9gQ1D9{^JphF$PfKxcUE_UYN^2sfIyh0fz9sOu#UeKkKTi2%Gr9$t9VoE{>bgz7G4Muu@sHwgQLz>x3La%1xn^mTT|RB7>H0zHO}g)b)y zlBaTVbxHK6MhYBRw*!APQ*1EhdLi#?JxR4|A%PTngC^TepI>tWD1$MOPY{xX7S@5B zLS=AjEHqk_B+wiC@4Gl@(kGyqm4mY`$kRiZ8wf3HU`cGO1LnwNz=Ypq#lwaQ#Kyiz zjutmOqvfmzthdJnJ2gnzJzk=%*o`ANzyuN3G=D%iOAZgR95}oS2R6W>T$C+auLnJp zqgtXLn+!xmRoeF~9_`h_U$At(bqZQ*oZ>{%FbVMIwQ1@xqxrAT83I`Lgn@h{)UEMs z!0Sghg}pMi|FPhYd~voa8}Y={dIsn%XrNaUg@R_76vJI3R!8C3>_)W8(W=RiAH{c@a+dE zQF>JjdzmKiN_kh8JuwFW@_3Qzs`Mt)q?K$I-4wU*o+2q*VUPe>6cwANEm}G%O+FQr zp9^V}G0ufEW?z0De35b8Hjui7wTl&?%&-ui!X$zife?yUb(yVzJgm1kI%c-+ynt0r z$o(|I;QJKnpzlusoDheR!02j>+kPO#YY8JNI^w#C>EJ*~muiAy613AX6!}uiD}`<} zn5=r?4A|bp`kHBM#R&wHEBbg+w}5y_9ya(NWP{=`mMuwKUxDF4K$)=%Ommg^%&6&j;i2i07+JW-<3AJ(X@gH2@k{jLEl(5_XE8CISF%$I0*)sx9JBAFlks z-}DCIKcM1xtqR~k-V$;l!uO)Sg8iAu zh%>`103}4W7TFbd5=YLk)w7G^ZPJXEK2IB+z&C}Oy za65T^a1V9bSHiFRuK<>C5nnzznN}JPIg}HaQ)aMo%^Z%7uAc*a9Xe&13C>;Um3}`T z{M2p#7bv5H3&8a1FJAAb9%+Fwky^K`W`}QCyfjDO;RtsToWWn;dpSPKu=b8MDDYJV z5`u@LH)4vO9J>Zpo}FTlS==S*9E{2rA!opiJ$-i*Y$LMYh;7n`;{5V($11UHA<~G* z-denqvLSnZgZu^Ab>||qo?5vsf0FIc>Z+~*MTgMdD|=~_C0m_LPWn`EN!^67cE_-2 z0B0K}KdosGiIAGf3g2kW%t^y>4?`pF9tzy(diC(Tovrr4z58Fi!W~NockjJgdU$wS?*R=%2%2X|;jx5U zO`Ds#_<<2!++&GRc+#-f*xPyXH{aDptaW}zx4*fZYIV)q)KRGv`O21??a{a`5v2wA zJaAsN+v9;&doNH@TQ$?vr`AB5j(vQO{L)5W_w|;H7_tI1$h9S{%EU zyWpHL!oPThl!gZ8#3LeRph#n8oK9H#9ui5_6I^j2%*28s6~3p(*5s&VMi~cd?f?5q zjJUWO`&MXSa&>wNDMqshL@Q0B>Pr&=cHw`$g6hwHmVUAS>VNdrk`zQcJiK>!_*MJx z@c!Z9*F^owa1eF*U?3UR$z$z^^bOcRA=QO>M>>_cN*u1Z?UhhGqDa0N^KkMpN)-2( zPnR1qM*A3Pu)P&V%-cOwUh7YmmK#4Hw5I^@C@;f%Mxe`$wFzG1Bs&9eX&6mv=0HfG zNGAz;TZn?bLXvg@ zi|2)-_BJ88EWAk(kBu#g(97IiEENYAd z0f>9cv#fm9c+j|~CkUA#gt+H`*_An`%xiOrlEfL~dY4LfK3}djrQF2ufo8;&{n`w*Upa3l`>K80%bDFL-xMRn-HvmRDcf3tSNd{{5(Af0 z7m$vCYVQgNy9+fVM;7A>EMC&ueIynKz}7&fCY`ZD4~UEwd8J6XR+qe+lMgl#P#7%BZ*$k%_V!de{+o zTy7xtTlR(Q3eLP55&}*umVm8$M^A7)`1HPvc5D07cUhmt;v!iiCF>N1o6QW|!T!Ri znI0P&5>uT~ehcrk$#UQGRGhB-n8^{&qG~V#Mlq!baPKma0y3lx zAEr?uzBmF61{v0sn{aSFIqIw+-(*NCf$}rtX5h@Y6h^V>t5_k4E>5H_IjTbIACeVQ z19STfaE;$pHnaYtY!~d4x^1;r(|W~ujs!&l3#eTHcS^fceFdAPKa*wpwtW#8WhgZn zn*u9rTFR(1QjMBM(ya(7T%Lfi1nCiFwzLCJ2;zN3Ob@bdGk~JLgl`Dv9e0Tmq(V{% zR!H?=+{xX%n2g(Z`G*U$Y@!Re0tf+AV3N04mIlyOkY$MsKG6~zLyS2AhJgP7%W$79 zIg8*Jn08mCZ+{HPgM5$W7_x}AYbW;gO(*vCr%w!8z4!%zX!OC`sfwfJT&`VDz0 zCOs>-vq;@If*G}5DliCj28nF1K)~qUHzTsU@B;H{-dn!Ad{?r%>wDpTRJ+ipY%l4Q zEAqe6i!Ir2AoWo(*WhrO;>k3Jka<+lx6*`hNcH1@6mmBa`vn7WOejanE^U8LGT|JPicWta|CN6`gb z6)3_^PoTNQ@RxZ$A($o3qFP}~V9PL%R+WMC4UP1(I@%HxC#@Y+zVm7G;Dnp?p#^KO5 zWmU3=CE+;h4>^T2WZ(g#;bi;DLO4(w=-4{yFD&0eM_nj|hyx7}!|5Y0mf4+G52o!a&X-Y;>M%pyd2IPM1CrTSvfSuU_t9mGM29 zdXn?7!Cehbc&P4rWLwG&hAm1rMo^mLTV-3gaM7cx@Tq+N`KR4J(mIC?F_=&*b??LZ z$!PfD5dW5cukWaF{(!vDb>PHl|5qsN5g+O{SBBM-V6=E)?CQ*#hlkW`5j?{GB1N$` z8m~-rI7UesdldgPXIkP*SVobjZkzbvi8-zy_8TuoG==5puEB}wpD3K1$TALp(w#HJ zUwLX|1~1my`@TOuf-6)nDZXwBdzVZFhx&}M^M31FDr-_AQ158xynE3KxjZZ0BF za+luTAy_HkkH5JXW2*+PX1JtR4ypPly5i6}TecvIHK(+|Z_7>ZOz2!{2|;$4fGbO* zY`KqYbzz!do8?9cQ<=h3P*|R-#JE*dK?TR<&bUnA=Bz~*0Ib(M@uaZ?_Nk%Ha;o{=ua0|CKCr z-WaE$t=<0d)p&x`+b1~cA?N)zvD7XO{uS^2vHw?JJKe3*)iH#+b@&w9JNW>3oyX_` zZqo@|V0t^a^v7t<|3EhJpUQ^VV=5W^_}RvLtU#taGlT{x@UWVH4Pk2Sl3ld85e&ZU zQ*3~LENEwF5-?tq%amPs2p^@edjn%3DYLO5v`GNDwGG^lBoee?{;28VYDgP1ZD}|i zj&UdqD^S4|G=uR5%GSb{f;mdO4(chO4p1og{Rke8U4fsRA`0IBetoZ{Y%V-{GP;zl ziO=DoLiygi(h60p*n8A64$ZfsV`+NH-CBT(I-CFp{eX2K-ijpq@OXpzJ*`O!=LGBu z6%I{dl|hmZ$(w8w9D;ltKwm#h(yDeEzE*G+a%129FyL{Z9xu(xo50TkBEqDMtb z&*$Zw4Ure~Pr?@MAU;DYXmxck`2mPKdS`clFXHMOOu_BK=J*_=Hdgi;EB{B2nG{T5 zbrsfh_oCl|)^A~0GCxoAo`%ar14RWvXivW>D^_!7hTdH0GS}i$pvI&TaCoW;f+Daw%=AodzaE zq{uBg1TiI0xw`c5V1?8|DU`86vKORrk@5=!U=ee%8jS)YL_UP=xl zD)h%wfD%!82ayR3K7yV7K`dj&oS0&QtdkRZ=EW69F(I_U5T#eDRNsL( z;dE;xLH0X{m`pQTR#*4_y1CPNvc12@-3Ws&ieg}*1fy@ht95N zV~*U5zu9^ani-U!a)ZqO+rsb(i2Sktmx+9vTTePqH+Q7~PK*+=Z>o-BaDdRbVEI9& z4op;T?)XXF%3o6$L4BfY6dw`pxbCZuF6?7xCG1(;koni-@3$=bX*>Y48={qOp0vUxq08jn4 z@hIEfO`dBGDFJqKpHWR{CA7x%PF2K=>c0BMs4g^}cR&|Wp&`dq&pNtL&kOLigCp;k zRHEG}iI++3W04q0#aNa?Oz=cWz2QgN5AK3eM`jb5@iSKwI#*ZkzDMtI#JhK;m1QBP z5M_Kj7+>Kyyxt^XMh$W_cu`|x6N2bHtn;(c@f!(8!V667WMr8~#9t%XP-;#LyJsJe zs=goseipj;w1jw~a^?KXMM;SaVoxP^GAfV+71CF}>Gt{~_Izr`VRMtd|95Y3CN8+w zf|LE4y{#$<$D@l62!juIB^?2&UXn)(MvP=x5R91n%7gi3^}TJo+o0OK&fj8h0&5p^ zb+ICYKXDc*no9VdVnY9wm{~uglF27iGTo`Zola;jA+B8};xU`5f|lZ)59e z5Z?y3DFV;74xn~7ffw?H3M?KiNbOXn%Y6$Ijly&f4x8{^W7;@jxW{ z-qleQ>WT_eb%&XgWiQ0tph85iiTwpnhwfXAIuX#-yGd+E1A**ri9k52N9Cms>B->9 z8O0OxXxfM5a*9;azsU5NMu3Q=NKdmUFQ=550*4ho&JlUe{9x}1E;y_B|5r8|E1PtP zt^5e%XD4@c2Zxx!0YQ_gUl?jpg2o-v_0;+%;MdZbVT;VSg>}{F-6Bz~o|*QXfXe)s zKEDhy0cC*gHBdNzt04Gqwd{yeV17pH+|AkO5*cNc&C^G`haz;mUXw%u9UlH5(hbix zy86jPHIHjL8@1sFleHtb*^qM@o7?3=y8Yz`m!ndDwKRz8ji|wOE0e-4R&YiUnR!A~ zet5B- zdP?fvw++ON2j>n_IYo=KLCq2AkX=cli1cs>P%F=2Qw@j2X9hD9Q_ba-<}%9zYXiJg z5k)K?4Y?5E)Hp__Dq*N-%C32o6X1#8@`1*!@6r>2UlPPlzTo38a_@|~W^jO0j#eh$ zs)UcdVs$G<5h;@khoxxw4EnPcAS8)tp?58kbS_$rY!}YA# zKI}j-98t;kH|kmj@9f_ZRC-TQ855TvA7?8E z;B7S)`TJQgH&)#KUq+yiRLlff1|=Zpq_{7v(}Dif=}7K)2in`#27c7wtYGp#fr^C_ zz+)jt=_57^Z;L7`lV9*-aKfHemPLM+ z>}h$SNl!x1!y^`0kAPZWaW2g2_}sF?t(gtGRQD|@0{Z0HzSx`rL^JqcRi3xE2 zWbTe!acgKpd>GvLgRcrK_>@kpKUehX4F^@Se1zJ>xJ( zDCH5>s1H%cW7>S;Y6L8%GN-&ydk1ACd@uvs^u_AUp70ejP%5fJaxNJx`;00GdbTy_ zIicg%SHm|1DJKDy$yq-iQ#c{7&~<*wsHWaycy%HArzRy{jF7t^r~zo}=wJBBbA7B* z#rj5o&p+qx2&2=%T@3oz7)<4&&rwIwqxCXzA5SJ8zvZSwJ9GNb{aJ3sG#p>k(sDRG zW^e(58_uZ9%bAbz`G&_AAzudPD55+-B=>X=PHh-;+&a&QD%JQ9Sq;z^G-eZQmdQ%r zdomrt+pqw9rid()GIA}aG?;9SaQW~KK8};IE?P&U(b*DDIGiGH6qw7aj3n5CDl)By zU#zSw{UZOxe;z8rACUX4y!z#E8OO!#Z@#fwFSqU4l3FIRc$wpr+0i>;8ASOEx%d92 znp?zv0U?>Vot~4aKU^h0!{k@&2=3~`6`hJ?y5hy+z14{8DLBi%tRpR66ZR*8cN9TY zkRubO$29Q>ajplf8d2-?1-IMwBKZ65OO9{Nldpt;1^@T(9w$%6NDo9dL`&Ak{C6lY zQUP5utO+NJ4f{ckyOeIS3>yd%;wIxTYK|raL~IepUqEGuuwNp)huBmgjzScLpW@7( z%fTC({9}E*MT~Zl@)FpGM6j6cH!EEGlv}B$xiq~EPcFAteYKR!vLOj?;%AO(HIPhw zdWAy>U#I!9!cWu_RJ_3gc;sg%|6XU}IB7aW(b6ZTtgw=~qe02q2&B!mmaZvQGF`?V zXi|(Yl7tTO5Lbj;79ve2S}HS8FL4O;ZQt^Jpwo(y3n|C}1H*RY4Vfbo%_>6XcqHA3 z^^3>v=?m9z}VUa=fs(~*ibj_EZAI^JLM?n+ZD)i3Q#a2GXV)1TfRN1CR>H=h<(PXV?4zM z?@-8QCK^^ph~B~v!g#!|Po12t%n{JLO{PeusMqK?R^e#5K&Cp&00$yI9gP7a9q8dj zf{3DQ@N3y!Vv55((IxoaU1jP*3i_uvNcS=CGeIKhIzgk7!u}pI;{s{6nJqb~jFcTnGM`SGUJ_|rPF&fk%0TY0wDYuPTyOG**xp$Ai-$^X70fzB zh|Kuc+Iv)QYQlpU-)Fmoa(L6aVp<(h%NgR*67ymRB}4F3_Ds!_rqV5UgT#HT1!Jsf zY*z{R6eB<{1+d~2sp8QTP!`$QRISqGCrGQz!~eHL;lboD0f~KP*W+$Q7b@dw)%hqL zI%$WYXdwlr7oH{rpGY1mq2OU>{BJ96HMnQs{PM}e#@+Y0xJBLkh_T{xq|m?!#F_Hn z;oVav*umKnyEXz!PQBUfxsltviOxYJnhBQnB#Qkdk^AX!;Hi0SnO3+^{7A=5n2!7u0+cCB7318(=7JM_mj18f8#%K zIZZX4fXgvIFo1PJ!zO;z#FfKPwv^?>*Uo~P?{4$`Enq4R83PIzA8^ZDM}Nj%qB?gX z1@Zh1n)-u80zi%BzlxSntfPy4KjMxH*N~Uu+rv>$^4<90D3#+0Juh~tiQ0nETXTFQ zi##;G={1w_6$>5op}2zo7}P(qy0y5*BQjeo!QC%EK*JofC!p9#>RXXpVX zo752+z9GT@0uC4S8-aAhAAyf)bN&8?r!d#83e+KJ3dKS7{9|}ncPL(uJ))K?0q$Ev zu8D%wUiE~~MA;drU8&(~r9^`vOgfS=6XVzqPNF5OEVG!!IMidCd|oi#8MR37pdu`X z5R%1zD;XoKdoA`M4#kr=hL3LH>e3+%#p!QXb{!TK3ks{>moAe&28R4C}8JkIzMGd({-YHIfchlU#qs|ylYK`nv>hM8U>2VCcivILKk ztB+Ri-^aygFiw&1`gfEp!{syidwGe)v!EL)d{W-MLz*mAG{BohtdK|@u1-TQR``r_ zS~j00VfRJ0jRd)4i(yYxC*_VrN>tQRYrmy1)hs3afla{{R`&v)dIZIgw9p^_qPMCn z9Zqg*rEYpNmv1kv@_!y#IqY8t=UKn%xu?@1XbiY}>2-IqhARRfRtG3)iBl^qc?L%* zYibJ8I9jzI)ZA#}pWbPVyYGO{J(Npjz+a!E)@J>S`{>)aAqnepbUZp^22g?Q(8T>d z`*!$@@D^Z0Z`6QH>_TRoU}N>fjhKSxPx%3!f;}Km9+9BJCgN|128l|E0&VDu6fdU~AJI|eP)d~r ztGf?Gr1u1Y5vK$ehJFp-)JEQN`)tLYSkaAInAGqGJo zEJ@TFL=F%&8*c|)0bB?q0X6`PfRWTJSWNb*#GTx<#%`n#4bj7!6Xs~{xXqo!NX{f~ zEgpR!|J|FsemWW}0RdHWsw3XsoY2TJA1=wA1(s~aaRq4MQc9tkba5fiB2E3g9#3^e zdM8VeyNhrPNY!KLS9lm=%p}*kWy3B|)3!vyc%nOGzD^?sJ;d;Qa*6@VZor{3TsCdu zH-j~IU1G|-3??5X$hMnrEBR-38-<4Jumf>CXNq%)Lr{`%N?;kdH_M)I2N7RTY;+br zU|Oqk2j%ABr!fnlv|~9sXj0+hDH~n~4F7SsWtCbG%X)*cXSchKaAzT8HPjy{D`Oj+ z*Hi`+g3_XQAOV=_jOCe8)(LY4Z&Jt@hm zCz+hC1;?X;F;y3F;B&eWWqEZZu(e)&K@&il4PNSvBVHAnxTMMoqKl%e@S}q!B@8{s zC>BHphs2or?_3YG?}X)~qSCT^6q5HT_L|E86>~M9zFD&T^}NDnjzthivn8Z=3|yN= z@lpI?8VR;I!?^|`uEymcOF%(j%W1cHnF>tdkOEOd^;BrXjcIbr3on?tj-)}M4akbY z<dda@A=gFaDf@nnc+nxq zP+n0NP1wd%i-F9byv;ow0^K-*$d-~CZbJBG0hg35?xTEM*|yn>W2IOeU(f{xQU|e> z-G@gBVVJQZMTX!xikHl%nio0QC&G&EhySHqT6(bx?Rkc(x`g8xBxFqsFr^C(a% zi>-@PGZfBcQiUz1@~`juS)wrAa=_VK_vQ~4Uy@Q8TSu@$xJbkErBY;C;I+EZ;@`J3k2t2X}C ztN&;OMwuE1^UkUm?PF|&+F6mtZp0=Dd+_+*f=~X-SqH997Jh1aryzJd`k`zT*wzp= z2ysd|-sff*)Xhhl(#DGupojs0J51|$4tzk8{}zW2EMNQ)d1q{G8TH5}Ky}JB)kZBM zHm}z8v2+{lB!>jF#X5t$*FWhZ>iE(OS*%7QJDi{w$Mt0^dkat?mGU!Ll4!{e!0-tm zxSB$+80}6Y1K%pam$Vi5g~L5XTg)_mYd+3ot))Uj@W*(C-(J#dwDcZ%O3ScwG{5cc z_$5pq)(Izt0oBiTE1Lasw@1{1x|268ID7fe0gCfESX420WEwRCv#R#7Htan^{Ag`B z;e|%l;SOAx??&SuIz)3$Dn%<>xvEy;;egwoXxHY{CrP|X)-r>^)-l%4#lj52V5$FH z)|KGNR0~R^ErFMMc?>^sn}Y2I96E*`&-!N<@UW?WkJzRUrfM&FJD76LBs>r%69`Z7 zc@mEJ*YU@g0hxx4sRSA8u;6iOw$2^15Z+=1c@z1vgmX=jM3=}u+s|{*-+J^@g{Tj9 zE$sjsR@Lh(ZT*u%x1|AHHoh*SDYVY)gsz&Aqk9IJ5g=WB-Tk-M>ON$3D#Q6|-Yd`VSR)RGp9P zEajo0{&^^02&Ju|*NitfQ7c@C?!}PcUjM)plymq@Mu9!rGHY4lu zbU!YYE12!Yw2=7QHV#iAl;3V+R&6vT(+$CK?@9|F=wm%H)`YPMDP}ePY^T;O+XA#4 zQj%o@glkT4x7Q^yi+J5Kz7RVv9=4$iQZgT4gGrF+kz8p`jzPLI(RjBSJ&?d0biLh7tw<&4)U z5G0}vno_)(CwqnaLFWT<2T(h#6^pQ2sl$XlXDBEn`CyO8W{9T|5Qs#IZ#_BkT%!fJ zCPSpSw!u<0y)>zhzqZ~f=)gIjUZ1ErxUdF zAag7U&825j6kR4z0pp^uw@C9l3ktPe8=nE{>k>3TU23O zz<(U%boOPFz-)?1a6eFce0&#}(R}c2cK)Ub{27kg$cTE^p7?ClHDfU4F=MRV zLw;eAvp2=$&p(4tFKRLL9uNkgEPx)F7tn0E`P0v2=Yz{%MS(}6EK-1_?1>@8h38>- z2)tm&QKaBny8b1AiM<${ut*hvoy?>zfkat>plTf1v;PZ>uEoCcNDPkgL#2!cOm4{j zP8DmXtuG4aV+e`k?$w`GUfo_|o=}ZHF`=AD2x2eTa52~5R`aBb`#V<~`Lrw~jZ4aK zouA~X3IAP1fk?^JV`*Xum@=)-b!ePoxYe}pzP@{}@p1_1gz`@mvV3JDUC9+g%&*gc zNb)L{4rQ)wu)XjFxw~px>h%WNl=}}_+~6fZ(JdYmqd1YUb{3vZ9uQjGVK;hZ1=-xLkyyS z`?XI@B=5*U2GOR}R%gWkn|8P#Dty?1`h%NvdmX5kyg~}bBbdu~oBN%$y}$0@su<-m zGc)EwXH@^}ZYZge6<$yhY+@c;)B{ka(# zXaFhwQaEd+Q=C9j=*3>-R8HteK49Ba#piUW(s%ZEH#TOsPd=;8U~BEgcQ4nzLl?TH z^(hMq-(fZQMmwsT@sl4>rFm=f@$MQ*F7B-DKeLag3(xPWd5r5>ayC)h#S>f~G1;s6 z3)rqWVB|6w=Cd5j-(+rd zbx)aF$@U8q)59BBFwI8h-WP|u$nS_cv$MU#Y7&7)t%UH)Qb5F9pGDs=eO6c9258O9 z`9m%&^jQ|KC-t77Y8leCMZwU^-tbZqxxwqsn9Rg8M4)|1R3b)^*ga!gQ!KCJ-yg8Dw9Dln|4r!Q)=f{R^(b@49CbR{c zz~tWRpW-V8H8d?^g}(sHEHWh@%j-6Z9S)9#xzx?v3a-sr3iW zy$S;9$V)NBg~}PvuY|EJvl2}#$CrN7{)*iDnt{>F#}Dk?Pw%4nx4->OSZ~yH005Td zl9Rc~25DTSKmjl((X2!a-S~dFb(8XkPm#Mk8h^OvXcGH z;e^LILi#h9FaCwIQONluiv}(G5vBoZK&HP{Y~|04^z(;Q;p5PF=l@D0{Ii^cGLCeo z@hls*v6xT8KBF8mrabtBQ&KDzSyy`lhpw>qHb7+p<&@BgZMEMJm#-o7$1C^+vkaak zID;j!y#`Z2lj;XU$Uj%XzlfYheRg;UlvNjt_HW@Bu#!?M393#sw@i9dLoWrL8>%7- zv}+1ibh@k-(Ipe@D9ueY2wg{aZcV5onR4bFt+F|wTw%BR?_}=NkTQp2k>`-i!6k8m zSQMrHxL8l(8w_8FG+KnJ!2@x)EWvbq^Q}zZ6;^4)eN(STlS|3xRrfG$v^jjxp;ZBT znFg*^XyWX%xF|CibE-O1`GhQ-j@?%C$M$)<*LXIdGL0HxMJDp{VgMMbqu@RQ8iA`q|`GqoWUj zJ!|a)IaZmF8L4z#42nIu8-TagL1}w|9Bmw15rE2=zz*GvNFLBViool>B(Anz4|+(H zUt-{u9S|dt*Ti?z5i;Hvi%d8j@f1gAkcf{;Rtq z6@0Ps_A6HSb2QCmgK=FxoG|=?0D%tgY9L)yn3sU2%|HT>40Cy)VF0*}u-PF-QI+*j zOSt{ShA09w1VKk5Z}U~v_3OJ|-D$wDMKd%Jlo?4i|13Jr19y`t#y}I`Ino|bMPGA+ zlI=(s;J*T*sKvEtCAw^$32l~iskI^Osq!($3&T;|Z4)*p8!7VK<3q~jBZ{rkyp@}=mtw9dpd(!T}s^v}Tg?2U@X#9Fl^D18lT z{#!fDwyn-5Yyddem29+TM)8m>;t90yFyT_!{N({yy)#w`b%osd4}FPw8&#_IVr_vShXK`@8DTSD|KGD-6fx>qr@mu)yIz z-l!@oLtJk@)th0_vbo-%<#9c?aaSLpcU-Fy~j7OKRHL-s(v>5AhL9sAwKSn{CbHW^QQ4AGE++}yOgd% zpPmU>>Ql3|Q&aaT$YoO8Z{C9 zcmUtNduV?h#FwwAx65nrD3ABfJ%%)_9YEi(_rjSQSmBh@aVb;qC13?UwK|HZlCdjx z`0s3gQQ_&BAnw5`lMxN@cRc;cAb`7-{PA>?A$%dbT5lBuOMT63P`9$-BZh9J(U#oe zzN4WR2Z-9`Ls;uDG~GHEN-Q~QohqJL-t|*#?$aUg7Ifez(BUB>pLkD*Fq3y559To*M<%^+qIF0y8CqOQ)X; zx+JswJHXKK`a1d{RAN~J4OA+Hm}0_VyzPM|WbnK?$e%S+rBQgSM}VyUQs)$&a61eZ z{VAyx^%#z#7?+s9q)rp^L}7E&Jzt&_^vnSwfuOE_KZ-68g+D7T*_KBv!j^Ey zHTp5#@-C+)ZLV$gy!(D|es%7}rEqEY*@y|7hCoPqcz@hSeFYqG+(Fg`P=+YfJ!M{l zxNW=y)D)`7{64|qGkdj~Xu&WWGKCroOoi#d<+0EZhajdln|!V(o+)!(Q@7OoPN*O% zmvaiv9Z{*3X_aryw~)+Zw#-lPHsu6O9wbk6Aor=L0wRv1I&N0$Sm0XJp_Z-%d_9s( zK6IUknH8&Ux=-jkNl!@0sPRqX5^3A4g9Zv1kKQHTMMdM9+&fnHVXL#!bu2Bf;epcPQWp`U@^2bOAgAz3>Tvumz6%<&vV{K=37sPL0bPl zeLZT;>#X$e37(QXkPoPjxn@pA^W8+;#m%epixy&3Aqg2OsI0*RN_krloaib<{sMWz zat%%~rMFD*3Ks`S#ex&)GarbrR5fJBOqFttkVysqph}g`PSt6k#ceFDBT2w8X9zVr z&t=d;mFWZJ4_JNr&_Z=Z@&^c-_mj5k);`Y5}5e%r2vC7pajX1HG66CZ_ZHnJ+W}2V(KsHpVv`qxFRqAJNmSf52xj@B9bsE-_Zf2oS-!QGKFx zVZX(8Yqm&)x6!8neIw8FjjEx9SI|ZZj!K|T>WxYQqC{W;^`)4hDsT`ji)CJL6FBa3 zfq;UBC79KkumD5LMa!xK`_WQTU{tD$!eh{uN?QXar&xxfNm5Y?LTbr%S$5pOP$19K ztRzcNN&m)~g(O36vS|9%Qwxg!23#SzSxO9ocJn6;zCQime8JUK7f$46dBz>^bdG#M zKrzekoSH6(Qif%k;NjB4!|}tzp&VM%hTyG&^`9YIaDJhG_zX%Mm<%W>#Qp1QR3{Mx zm}XDm1lYsz0a6c*us}scGS|%bMbc$8`df6wq9uoWQuSOk+4qFN-ej+jg8h9!<~=h0&}T@%lO>B7h-@*blttPLCRAAIm}Jd^r#H0rI$Sdu=YCt%KTJ|s!29yT$dyg5V^Qb+E{ z@te_PGB|>Ue)$?t=`ygTbkFEI_@H^&OuOkZ$3%Q`bkSg$ui-coNcsvF!V0%v?svAI zcAjrM-`@SP0?6zwVM#v+unnShbPi`pgw*DX{f*rhYg?U--QDfoN=Nx?-4$#BJ%yto z)+AR?AZ=)f$wQKz4UT{iNHDrOh4aZgG8DY14-U5?T#i?hFRZRY#6dR`s%9Ncs%dr` zw|;wPzq9^wZ-4vw?#BOlxv{rj7;OFxO}wX@8(UBIay!r8Kil8mdA70kWMdcaY3qYj zWL*DXfAjeUfGV~hD$br|lRECw8=8H;zP9<|={9H6WdZZz9TV>HP7k_uF8T=M5b{3Q z-rg3E8*M)Qac4tjlziBcw7L4PpJA2g_KY_!c5pdgz6A8TzOfrWxOh_z`qzyg(VQfA z@p=qjTFrZ&OZwqSFo6Rb@hv3mHj$_SPU5Ea4!@_fYeOBgA)%VX1OJ?xju$z zgj+I*g3n1+JDWQjTbnO7UwntN(E0JV2j4dSDCgfEHX8R~qBNNN&aqK_WdKHiS_$6E z^R>Tqh?Sl7?H4aL*7tFM4#(PP*nJT`e)jD9=YG0wzPZ))ic3E*3pqY99SkCI1zb9i zSpbP3CU{ZZ*T5UUCwK*t`p7{xF_7&&Bh2=mt?h0o3_Dxf>woP$S=(R3(zpM*x$*Js zpS;-n_{L-+I}q z|LsRbuT~u9cY@S)pDIL@1=O*Do}EG96^C+{9l$c~{~f#ue`tgge}ItqQ(cnyi?{J0 z*rvvV&^v+^CTPNhkeBjvm>*TujMnS?*xi0;%TgI zKXWLtVT1QjLXTuFuGfAHAOMlL{u1I*2XMXxAN!|`XMne?&~$LTG2lY<2T(?6iSoI%PSWv;kp;r229VxS%#q?Xme2sLnfxahV1p1_rC^Dl(?pn z-_2Z*NO$AiU1w^}0Mf!NT`*Z3BD~bgpPCo1xv)!kz*b;|oZ}$-_`~W~_y72Zf2X4P z?zY?uB_J`PlIe*L$*PZFAQo4E;eOeX=N;9p$3YJAPBEkjV;{~Zr%RID-uj*TZLAM= zO0ZWRW)B)TJivPpNFkLM;`PM-=oN@R|0L@>WJ&Bpq_c-CD$W4y@qdd)quz%C9Ja>g z=Kq(h9*l?es*1&-2SJpi8?GXHhN^~iOU$VS1kbQb%S~kbLuEw*J8$u!K?Oygfnvq6E60Rfid-ZMn7(ohcXwm?Z>K z%2%-`UmbX;&tz+miRJ4kF{Ddp)M!t;H0j4b?BIe#+)6jKLqDs)I4o@(L@`%cgb214 zabkKyy$&aB^`_syXu}8cwl7CBK1Jaos5BCU1EW%z6Z8fS%vH*n4$;^Tr>kD~Rkjpg zP$>&)*Zt02)66Q0+kM>p8PqiqLDv#`+O;X%>7x!tQ!JPcQEZu^PSk}FeDC!r@ngj3 znH(6HxuuyZnCv1(sB=}Wu2L+T`1gWyhA|;kD4sGfpR|1-XAKx3r!FE4(>fymk6G>n zpuLb5nsS)6aIsgAh@2OAb+E`5N*=@xd8)Zc;{0H)lJ?1>b`|#o`<}Xo$*jSkY+VQ0 zxH1woEd;}8Vb*`e9rmiFpE8UOmEe=l(`fr@~7HH|+mf3>VqdCPCho){1nSr<) zXu5UGYZmvr(i!>i>`}$tcEeG{A6GZ3b;ePt;R0r-(8z*ITiAUE>AEdhAOt@TDW!@G zEFd&YomZsF3?f1r9pqWkc~jQh^8FJexJk$J1BHm5!@`}0;aT37la{)SazyOK5u zrU!j}wjh1)zwmm|iG6s#+C6QiHkeOPLKn1_zI#saTY1GzbBx;+{$1;KI z*!x|%BUjN{os5u-W?cEv{=_Q@FhvZJ)4P7)p@QC`N!58<&yj z76(0+=r5i?E0Yira%ssF7x8K2;-Q@}YF(3_jGVbFG)y|S)}pcDn)BkF&K4Cn^eR>V zKr{#=3!R(aGOQis@14WpctFunBo|v^)=l=-JJBfydPR20&$k|34RIC78=GBxN5fM@ zp?Qi5@S2pY+d14$`XPh6v(^=)-!SzJ_52J!DRXw|KI-FUlHG4o^y@_OtMFP+vN6M|3_ zj5I+^^j;!90R-4%y^K0}t%b)n)Isa8z|rokMqRJ zQra`X*pC?K6Fqt$*hHmdqqF^2sorL*;I$jNV*X{jMxo&!3(xTtTO$-nu}|G$qg_CK zo?-+ewSl=)7M{?r4S?8LL)L16Co1BUyhHuc9xln_KH~QfRd8negb2BYj-p>hjPDYZ-FvSIOgy7*8&K?|xB$rlmw}0FR zL&MdOLrf5Q3jXB!ph*zp;9f%|)Voq7k+~NNXjo}*sm%dTo#O-_qX;9MC5)oC942Nd zVz*=u$%~2)eT(c%*%e!+fID)2L0tzRai@4s$zbYBva1LK(v>AFKH0@W4#AJ88-sqQ zZ&%R>h@H|W!c*%&vt2(?quOzkm?vhiuM}ySf{f4&;ym%g07)gqCWAk{5#VAxuT;4X zKoYT^Wsudi+csOxa$_~a`eNx}iZ_AR|43-%1Y3Yf$<>iW6#u=z1hZa=z=Ey^Q~tfXV<;^gcL0Jx2cONp2Y)tHF} z{WLohmd*4rod4lin9`+*2@;?6m=+@LqY^XZ(+f$m6AlLi_Ab$Z@Gks@CSl<}@H9-` z`obHgNBm`Uh9O_Sf&&0R(e)b+vv{@lr&dO*2h#AI#DCs)CSl!%gWbV!phst1d@LgKosXBZ0DL; z-)^83O%5?Sm`XX5U}_GtZ~pWrPEW;;?Ig?R>^d7Hn@;c``7@yh3v!8{3B$|iH4~4! zpCV4`84X{UgpG)<;v(O^@U;lnux=SS7d&HeC0M8Nm4?AeNkn)euP(iDUb@8oj?Ul- zr&*Znb#xEjq!GM|({4-<@bwX+##X`D$8p4Jq)jl|M2_GW&VwS3p_~UC!(UNBmN$Em zaV7EwOI26b;ymMpMU(2a+lVLrQqV$~&&_%dva;Gclf9(2{wYS8NVk#g&|UMF$aQ!I zRvc;3Q5~%f@l(UxF(o76)`mg5$Xz4(R);@Tq7NAM4E% zq=*aAvAlC=4dirTu!Q5pi6tK=pPEuyYvvsU&mPtVw%8%%bSB8f2ElO*rSx)E?hdRr z!U^m!$AhNp{;pPc;P5*|8e_^F)H5gBn-6nG5{jPBNDpr{@m9|Df-YGD9eJr*?z6!q z;GlXJP63?hT~~drt;08zbU=PRo?1jYjU1nhv;==Ji>5LenAi~zAz&$X`3xmE zv50HNlUafhfu|=cFY{e+whu@9R#tv_wA5Udmr^iZLSq~U?g6}CWo_(-JOp^Ka`^BS zPyH=QZp`7m%3>hLF|Dv>b>}Im7iMCp z{qb}}#zJq2>xl?BTrFAEPFYQ^Bl%|&$OtHlcS@grGWtxr(p~LwizsHV@%->`1+#%7 zl)Yx(ryoKH}s#CotD4>WLs(+a-0t#2nS*aaZ5qM~mAw+16m2AcMjpYj z#g7a*Dh0#niG=dg3|Ex%n)Bu@rhl73lLBxs*^xUV9uF=bUomUSg_IcwVy`Z-wU=p+ z$a|;J{@Tp=Wn?bN7^ohbvp?oXdei)kHm9Q2l;$#uFfuue*J}R!(`Ent<;v^J^RvUF zU*)KBwtX&P%)w-Sg{519H5G_8vBV=;BIrpDwob~DEC^-s#Ec(90&+=-QuyIq^N2pO z#N1On7pQ-I_iMr`l7&FaHwH+gD2y57mU!u6YOyx)Dia4Zs(p{8)3xJ`;gA=U0U*Wl^Fw5 zi|4+(8-Z!J56%{q6F=iNIvV0w3HnU{INrjzpo5{64%6S$)TA2EwUZrot< zgd(wFKaZSO1*&D2qtMqx|C4rI$nJT-Lf*(O)^I9kT(2Itpr z+GbXx{A%O6n?e-N*^w*t5>-3OJmw_JtefmDm-5lXJjEdwC`r9O_0&*?{t^A?^#f~> z@Yxp{VtvkdEX0s}0hfxNR71wt)>h~F%dP#*&d$c})9u~oTxk-r+eP^~96gLdyIYw* zMMCx)47tKb#PMD&-FnzqIELY;;kwnv&+`1&M(byjnAJA0tA0hIzqC5ndr{d=1#Hdd z_g?+Fa0__-GKLM)5d{S~8D=`Eu{>}29zV$saK~fmfwe$|=?{C8U`YcB`Ogtp+-G#? zQJKSIy}>K6uJlx-AvPsB1PJJa(+y{C6o+}u4ACq+rvHuIbAw@bBFT1jHSH9{mUrjA zQtJrNaHYk1f)Ge1t@Kz<8}>S*hvKN@$S#;o{jpaH=#Kz10Hu)LVG5cazc;uB5JJQv zM<&Gld0Vu6{RHKtBymP=SCL0pk!2`I_I3VDRcvzFy|tZ9<;}-ugC-T*{3(vs^o~v? zf+d8F+r7}UrXu?2ybMpJxumvrF1P@mJ&{yuNmu2Sgp%m11~8}OGt$0V^Wr%s)kuRp4`LLd&cBK*V%Y?0A8Z?I-AM&k(F>o+Az~=)=x1i{hYM$76ueZnSNn}3TM8e@SdVV3!ro$F*x|KaN6cw;Sz_bCE|gctVVKG zNs80 zYn!vQM4tt0*PmotbhY)2m#0A$ah?6kEg?dee`d`pVFDSAoIMM|u~!Rn2~6E;?%%OrP~lc5Y%qX&aausqpHlF=<> z0Www5>K%91@*9N~DZKKX_%)#+_iEG;gg?eRx;XA&yXchsiCvd3rmagv z%s^Op1!!1lW(K#*@N%x<#Y{%=vrI1qMFmn|*ehiQXPeZocui<2j@Kz^`^}E})aQl5 z=fayxn4uBnrED`(NSXj95d?mE*&mP(qwrMOm?KTI7<1?62_vH}#@q>;$X@EF&j`H0 z)FQ+luRRl;Mv)5|(q3p4=PffKnhPKw8PBgO&Rxh|DZA3DD!;q|6%);JXfz((Qm^6f#mXaNv%MBWFUMJQ`M3;Kt#A8 zRQ~)r{-)5~rdmm!@uug>Gl|2TCZ&+9u`D!{g+=J=v^P~F7?6dBX0zz(cLLF%f-sga zSq=KfH$-5>BHl*raz!{%4gN4DRJGMa7!n?-je)}~R3dbu5dt1=!EZh6uMb2YYl|Az zICY(sw5w>*7u@BE&ATfp_K-B_$U}Mats9{9VF_Aw;o#EBRy;L&Y%3(?z%+~CRwFv# zkNT>3gkI@k;Zx@8h)Ss_cSY}ToZuWZ5nt3}`@pGJ2T|6n*kqK?lS6jVaA1=his3}m zTfXS8a}MFM`$HUfmkdSu!Ej#Q;^69^{v35uyIo6^8Bp!BCO(FRODqLT+q56iO`$JNNUnAhApztfA}&J z6fTc^qI_i=L{$i?D_wH1K4q<+0ExyJ#fCP*_A%6ij#hb8MwT#@@=kzK8e|#0A6lCR z?FcmKnpqiF=1tflj_v9ubVeip5we80d{UFa8PxHBVT;B$p8^7@8qoQ(`?{(+5ybgA z)A^lju`h%yG#C_XP>KNsmqN-aPeePWN)U8j9xepeDiW&<&B;P@p+smYFc(XNr?H%= z#~P-uA*RwpfgZ`^3Y4Q7^>lIQoDmj1UrfKz>r-8XMt~Bgts=~@p9#oZ0p;_<$xdh> zf4TG+5okEW5tWGwTOCBOh%kzFgQ)d!*%CO>&Soia@~`W~teTV{fXrY_Wg$i%&;++H zPIm;qQEJNhDfaYPkeAxNiy);Qqu3cv=WJ6l?>QC5Xj~{-u?jjN`QcBXmb@z8=1)Sw z6kZlfe}_UoP!yP*M8hR-t--UNA+knAlpqG@z${GxR3HU)DUZ zG1r9kg1MQoOGA89aCxY;RkH9UkyD9Rr926TxoIH}LJJv^aSZ-_>lndM659fKrVFJ zia_5wH`NUJg(9tnFAP4fTel3;$}lg?nP@z$D>@|m>}!GVdZ(B0Q!D3Nv!FJq<;~ZC z_Ds$}0g)~mw1HW3m|l+(D>_H6;tBw4Iq;`L!^VP8Drnk4tBm{zp`19nAUJaqBOzG@ z(+D*mHEA%8B9KNQwaS%xiAm$z0+X{S&Wc`1Yk7FCOb}v;52m*f!uwBQ+pS z;@{Lt33MpWa7PC5hmra0$$Nb^uLS}rmKh(=f(S+{1ncjcJ)RQ!z=d(qlep8Q0C$X2 zIbJ|{2EQXSqGyk{B{AdR;5%HAh9GnQyG@cT%kB=&kTr=p_ObNEWn~x1*P_`Fx^S>H zL6-J0>d}?q(1q7pZKpn0JprwKC08fnRygV$`KY$a^%y7*<_9GC?%vwo+ubfwZNJ^z z0CnG&^KJ13-tX+mn+L_4^*67!UvBEVFN=52*O07^%{?q$tUY^U&3#4ig1vZEB78&u zg&aMSk2ch_wLAI>?wHst?&;yF&cAByED=7A*x2Ly7+I{skcRa4fhR zcKPQ}qm9ntcnk{>`V`wn$o7n>eS>|j?#9;c+B0NMc)hvz{hOVCr^d%eRzqoLh_cnl zMB11tEaSB7%Ayd%;IhIlcaR`IB+=d&9QRE|gjrOw%uz>AQu(Irya4v|77(r;zfm`!_oZ3lr= zn=d601E*ue{8xm=n)ud1-2B;a)cNY3-{9^oYdoCmInH}Yx9GUYc84(<7t$D0KNDCN zaony%k#q=b=TRy^s<`BJ9T-l)$(g}`QylGP{^j7icdsZGEjpl&J!DJQfX-nb+fE=- zE@wO&h?N4gt|W@Wf?IElbI#~!?m%LnmwC3kTN^k`_osXK@J(nt32~IoDH@H{0;z>6 z#SjWvEv05xCmdt9<9Um6*06oAt#Ri%-r~GU`_V@|y#821&iZ!n4mV9OG!>h|aFLrv z$6bepm%A;cRGRjPN@sBTIWIoPF>5{Ap z8(A`yI+ZbEPS)oi=09R`IM|tUQzI$gXm;?LRhvywGP7lM_;|g#EhMZCrL6IzJ@JXY zo|+8!cC#wDmrY*#q8`{xO&aMxticR~xGg#l5Y^hmtS7HhZE~Xxxh_h<1R#yDJ9n^& z%XQbK?&7L4&+J)ALMS0aTHE?!J=kUXjNkCUPSLQFx3do+1c0f$-r3Ee;%8CRKtqJH zP&_{A1S`?#|*WzC7iZr*_MupP3T; z#xrrr@-k{G5HQ1MViI1{z=m}k&&UJmd_4-0QupG4WG(^V3u;#x3#h3s)~R-lOO4)6 z2Q=NJKQ)3N_<2Tzu?T_6>A=tLg9wS`#e4ym;V*Ni2)S|JKcAu1)r|09nn-zTRXD=C zi0?F@9&OLk^~6R0)!|oDn$Yt(~g>R*tSeW z4MBg|t66ZeS4|L?9;>2~a*+ts46Fd;wJqAT71f%UdhRJ99@#Xs%1eZzs6~ZClo%~g z>DC~PdCpz%7#JxUTWi7*HbWOqV!L_dI!MyL;#<=|E%l=GOllJX6A%DSoMQU2Wd03q zStX6Q9;(HREVA<9J1l7{|FqE)e3=3x)qIpf%^;z~Ahl%xX2h0Oi7`oP1H&HSa=V;2 zQ&t4ZS(79l2`zFZ7NRrxQdU8N+E}NlhPf)JmTS7=$t7f?Uc~a7$$lA zEA??aDXU$(>da@(9eW&iDMo^tO9HYy&JllQYl(~vD^|6Q;IvGm%@`LOdl*SD8)}+E zL`@~Hwq-;$>~$zAc1%szxrd;4kzCG?jNljczDY;HTyl9+7c2OBaPe_G`O$nH%zR;Z z2*QhjgN4Y$jG>!{NZN+!t0eZa=Q`nJ_Jru=tHxSI)sbM{4Voymg48CBnDL7Kmma&& z2;vi%*FNz@rypNXls|QeiEriHgbq`LNul4fV?jZEsx=o|GU9-<@RSI%HE6l^&7^Kz z?k3V{X-V)+ae_}-U!8-nYb0qBh?-0QGmR3Yl7?Jnmqj^wntM%W zB^X%VJqLzlZJaVJb;4jC@#G}t(`yw9?eo$9=b$KH{dh2OvO@@=h+s7a`p5F_{qgn z@_B%h&piH)G%dV=6?dvla1csIDOP9{75PYG*U-f^GpR(0hTJZa*24Tn!9}V~PZk&H zX)F7KCj0w1cuc_-C|B*Swz=-1u-gnu#ra_#Y-5sKVu%xGnX%Y_-Pt3^!$ukIK4DUS zP@T|Cr68^UC3sS}Hy+MM+Hc(>r@R9Cztb}MOcvy5-L}bz(5%kVWB9h z2={O=-oWwpc`8FY+iiy6FZxkha_CI6FzsS<3oB2gaOMq1D4@O`2IPVU3hU!;MdE?4-1EC1B}EDz4UXn0#&asFW9JX%rIv+r#U=Q` zfAj~3m+$SEVg3t6?W2)UTccr-)FYVRH4=2~G>cr$`VqZ>0%%bJI>?$=%j7KJl=qP_ zG(?SUNNnj3Ad>`*9;6I2eUh(*P`lHU;W1(!~p_sH|liOZvA+yLb zD^p4$Z74mvHeK?-TsJnBzyYsT3bA`F&< zpAlHW4J^6W3Y%W17sGS(>i{5~?+*5={tH00mX`4JzZkwSJwY%6oPuZ8rvp!M=HP=I zVctQtRZ_5$FSq%?oI#q&*(dkjGAuE^fmA<&THFL1UP5)GufV}f z*JB+u#MaRW-m;am1iZ~S%8@(6^+;KS3P&t+Sj5?z0!Qo37U#B@Ji#taYys~pBdsmC z^f+67W6)Ntzy=#E9;srjFHYX8e^cqueHo=I0^FTrsU{d8AGC{;nwAQD)-+WaTp^lV za_d^h6d)?mTaVNj=)g%-5G6m7S%OP*bkw_ie?s?(>e(CERKrr2)Xhc?pr+)UrL?$c zZv7jfU#zcwhR-~Ckoe5KalyO);GD^d1Tqhwg_J;Ta>667w(VI#?EA5vCy)^c(I%xX zsX}w#%ao5VdZ&M-$3%iY5&Gf3?*JGz5%Njn=4S4^0kqPvmPYd_;VWU)v|5y_ZC3L& z2(-9)fUbC~F1#&M3_OMQ_8dzIdPu>>=ExEwlOt5;L>yxFBws7u0i#I12zR+2h;E`0|`x#X9Mmv?xnCC$VaXO2OXZ4BaX7Wo?4mMae2^C{eGe zwVz_o;{E#puGwNPK*TiLTK_b2&}c%;YYyM$}*Ojo87k(S-rWvvx!^nH#abw{HSuTwCoA~)Rcgi zqtbch0gQud+c(pT{yI2?q3!{e{6sQM4w@DSUY1+Y7Pt~;N}}9$$jeMF5#|i3Bg%zS zS+p@4qd_Jkk>@k43tGC&g(3lGCUlejWfe9(KZVb({qz38^zOSm%tP!09G?)i%u4qk*|Fl3wLQ|8 zWg8zrp_jr}11Ig(7nT^j4I#o|=!hf}WKcN_L(5I+45JkXpx)ttK4W^0aKl2SuLWME zH>0NHkph_!ufA464H?Sx;}EXB?BA{bsx=o~hO9J!KMPJdy|!iBLVY(PQMG>YL5#99(0h1;?lxsa7YuOmrib+O2fBN;aTs;K~~j3vKCTSNF_#?{+Sf! zJaTh^!XqR79XzNKra)Y!J{dNfSbA)5fz1RNH%Z9p(+V|J>MlJV4KB`l(;pLF4aflT z?DR(O8R3$2r~DGS5v1tZJB}$W$ucTd61eyfb$oXlc{6)BE`sxDsO<@vr8rOgLB7Fl zsp*#m1UUvs;U=hN72ClN%>X=wL5fQEAY-txj9sh2%CbV7VA0OrUh+HvoyDXPrxz(M zPl9X=3M3~7#h|MpoHW%Pd-`KlGeHE(eI=p+@D_u*9GwFpQ0=!V>#YV6rz4*dR}E!+ zI)Nf0zbK|XSxdBcz@2oi-2WPEg5A#hfBL$0=gz{=B`1#44la00R0|j*nee+F-9W|j z%A16W0*^%r>N3?)7S{6tW00Ra^}Jw`RJ}krzU^K)-4q3uRK|1arzxwa&wU3~=B@4p zFrn+cf1+ISJ5Qso46fY<5bg1m!pcQQT{9W!R_CEcjQ+voC{T^n@A%{PWN&Z|(YE9YzK?5z%Bym41eQ+*>Dim)&mtxsG8Cz0#wL6O78pNIHG3LX0xNz``GG z9Z-PdjaXteQtQ^aD%Z4G^oD(vi|+Xa57pcJ>rh2eh#qkv zdVVf*6-M={cfL3NV=^M5(?C)y9|Dk-d>%3us_eiBgs(vTk*sw32&!=flbYznz?I6? z#PR7ED>|RQ6-*E3$BTn2cN$0^=Um=Ri$dC;v=2gWH&Tvw@FI&gn+!s3A z+8XjpvWHY>VO=p`p8 zv<+O^aB}?A5R_Bs;c(QOFjItx15=Df=3seTE(9DIs|($yRC5nBb05h@muWerFO-}f zaLC#L(QB>XnhAZDjv;;2JgZk|DL`*1>5LBmL9S^Cc_F+R%FZj{=m_NEBcaVf}&}|L8=l0*5Gl4SZsO zFJ^9gA37FfV~c1KN6^45hnC-Q!KU&`)S;TkS^*l>sy=FcndlTx)2bSQFt~~D0Du}n z$=b>it|11n{;IaL73Gb^fX0o5!wKZ9*8{Bk05+No@iHw$Kfr;V6tT*Scz z0&z>m*Ej+IIv1(|>pNb7?_7NG1;7zjGczz#idDNZ0}_Eds0~*l|LuJe6Vf6A(7THP zj}eG5CpX%t(-je|ti&ep5Le5l4||`$Tf$xrwvsK)qSPc4Fe(<=>pehaeFN3k^wDNs zR!_EKf0G?m&vC5TSavZ1g9#biJ`M2tu;yip@iVxZ@IG4vSF};6FEZ!$?$$c?^9lE3_iqn8j6QKaq%kH@|Fa}M^b!G zR=o6^E(Bl^qbE<_X&%h9TS_4JmrI!1B_jCVr=zo{V$vz)ux&c7FJauneLe9U%wer* zsiOQSOQkGx=<7rD6@Q2U7=R|)L<(8#skPFt%c5VrQ&yn_%j%-Dn#$_Ji11hupg_&# zg^z=wZdjsZ3Q&MFZ8S`DihZircR~`NUbx_X#xA5bx&#Tw4WbH);Hza(We*Py4LI{2 z<3u=GNDJCFfOV^)k-c|!~YBUia%+k zb@SrZpLd_2s*_+AuYpE1hNRLawZcgFE)b`70bdVdQ{%*_aFH38f#fY^vLl4d%K)9u z@(O%)?MZFzq4a zw|M>xG<{SIz=xhda5);{jFn?Rd!eMyTEi{HI4LxJe%eE>wWKmwnvqvKk{}cqO5M5w?JpF2Uj zg9)PKHyQH>F{0aF+O7wWVJ!IrHAF8jU^)Ot>Z)`trD=1^8VHnd8&zeW4gWl-!JZeZi6{%c6(86SC5n1tE=$=U1N-gzgiwiAA$qg#ney9zhZQGNe zaZ62@_NL5^vib^?lA3pQj34mM8^e@*aV+H3>{vDyNFK%BL1?isnLFph%S(FO|D5?Lb# zOkpd4JaphP%OvOg%-*@-MG0GILk%igHGs=JuBS+0>IcE~azMEu@M&^yAU%0q{=;XH zh_p2vhK!nFOIr(rCb3o*~$6BAn?xQRW!K5OaVV)rI6VR z^ut=vS_Mj@ZcFT&IJ2}eNK*WZF-xw=h>nuJklSW3n&L3e27DAphztMhdY|N@5q`6K zjf|0WTgMpsS{OqHszix&_Xce?UVX^Hf;}LB1NjLe@Bk=FAFC$7G*xuw?3OsOHAJqJ zYG)m!qHn(+LQ`BK9n;|ek>Jxv0m(Fr16^MMk6>@Lv78d{I8D`tH2MpcS*{i^u`!YEq=32!7TMqv)qPvi=$F@R50d<&cJn~W9M5{4HT zA*qGv2(B<^)N-UWXHZFl$k4WFS3?|Tc2tw%o9{wOwTtd3>Tiy*!ed4KqoRJQme>NPET7mlw!pj zs3o=G6c@(4AN03ILS?>Ah;!--%586^(>@))M`CI5! zMb37-aUYa5V16puLEGfv4=o^ztJ3aB8c0_VN=qOj>C!5tmPkcjWes80ogutnTG?36 zR;Fo^zL=juTx2d*cpmo{Tk?GbZXr1S;sg{<%P#3u2tH=}OB?dEEug4Li!#Y-2UQ+M z8%0})E-OV0G668Nf)kw4J1qMP?sZo9}RW}#{-;z5a%NR$?fr%1H#%Yeq%< zhF*&{^njwafpZA()%|VdY^C3Nu{vBun&MbBeycXJcEpXNIU1=}ee)4iy4q5{DmWu{ z8NXFq*ns1DGL#f-a)QGKT`kyZDS^sqb(zd+f0i2t0ziBf63I0O7I((ui*g>eL7}1- z=cxZiGspoCqcOslWt#Ez=yZH|NZ~OiKrldXe1C*oCO;yO00F)9{Iw=$;0lgrj&61@ z56_04(j1+E$p(`PoLxL&I3rSh&(+FH7@(V@KI${A%4t(A5#fsAd?8&>;I6DrubR4i zzO+uh%GIvixsgyVC?TvktZ(e_f~63Z^WAXpu|j>?y4o}=%gf8HR<_VDmZMT4L4xLE zsj+BeR-u-IPti1Z_?;^8LhwTB5@M7%9-m_&nRq542tHD#Y2HHC{;8C&_5Rx$l{f4GV{UDVC(Jc`va&9E3q28)E*mC=aCuBm_l%(c!!HZMeLUPWVY^^;N*kEPLQ z^vVM2Z-_wJs~rz15uB>iM2jYZ)xrXR9(OZnky>81xojFe(N!+rA~qr5=5ig!8o@u# znGt{>$YeI$nHH=G$r7s9BLE@rizFYDS-b1tv_WsEmh34Ai)nQFREhG!i|rGsLP z3!$p#4kg`H3C{%>>RcKCgid1K#+#5)uBqc<84KBWN9SmaC{S-#|3MY?oPcDKC z{3!sH$UG`86+1d(xebaqp}ZQxdryYgq4PKjNl>GY(4||1^{OREk&2GM6l3oV6t-*% ze$!Gyg>akZJtP>Ol4<)lON!_|j&o>>11OmE^XD zp7zlQ2+R!DFzWm)z8hfKa(Gf~=_Opq7#8&u(c zGck$Inx{n>gwx`NS7ly8L`z%Z`ZlA*kDsZO?lOqVU#^t6vi-v*g#tR&s; z$1Rh3Swwn#Dkf@!43%bTQICYW2t5?pG`|;F0-qQS7<|Y&`M=(T33Qwg?ezsy4bo*J z{+dX)W~zY(kVpZ+9%eufp_6_Z{@^7Ux9)so1*85^O&4`t@JDm+vTjig1+*?68Xh>H zy^wlJOVcd8ozpUNO$(L8eC zy70Y1XeUXc(ut3YW&ja~Ds9I?Vj6NL<9aF%@nW17jA}6>suVU~dS~@udp31Yl3+1z zT_E5L9X3%Sc@>3X;7HK3P*?M)1zO8k9nYpT={yR|4Don;SC{(9fajvhKpQFx3X-hX zdC$4z&>M(J1f0w)^OY<~I+b1>&rd`EY_}72Z+{4nu}i{ z`1HhH*$QC5E0tr{xS(Em#txQP%semvi8uU!od>LkBm+NAAAC22SGsrlZ2allOC;as zkU`tv#cI*-L3}uXv5PPRLE7{2x-XDgLbf>g1vl|$Ta4hQDpUZHkNwL%=jZmlkZ$JF+{ey#*cmF2qJ{C&EOh};2 z-OLc#D9&@&By1PQeKX2TR{~w${BVfFR_v;Cu<-ZJlkG={**}8I>G2nfi~Nb8r^g2$ z@7%$Uj=fs^Lf)|T$IG`D30;_0W`cl+8(Alys)fmFPieH6YRO9Ox6uc$_rhM^i#M+} zp*7q$)#^a9%MI=J;>l{bvzk!)`WW1d!s(NcC7LhI+YGE#AMuNU+ zpcFJ(;NIkl7#6bb=aF)W@y*AP&ZCr&u0)k0#d&s~g_xSrUXFzXb9uAXtmRmT4?ppa zfJ@$L!d_;SIsYi7*gQ;X9JT~ z`uzueO#B2VxCa{gg8CY|OYe%ZaKo&Gvp^NmBqleJOra*4TX`rRyQ);-h_5UJY?vvj z6t5Km96(1T=xa2FQ4-i2HxUfYiR$3G_`#@q2=@`OxyU=!5yQ)!A3f}`!u^94yZl4n zN6NwSXoyMb#OoEv{GB#cRCZMaxVc@lgeQ`Hb~--hT^8t<$%}AWxG4FJ8dcUiJ7jP+`cmEu zByh&4K6JB8iQC0li3zVLFs5LaWluKx(4IskV?zIY=ywn}rygjBQXnu{6C#Gwy$f8t ze$4YFVxF4llP8JEswD#IclS2lyxqf?p0xBUuA|b8)i%=F<9ZTgzeD^qX;Ai7Wi~i0 zWOh(z0D!zeUS6o8pc`K8{%v<}^A%e~EI@Kx;?>scZ@>TY0b7%=e?*j0HLse}AJjE_ za#a?n8$H68*d0!hk@d4~AUbuYZ-@*-TWvEcA-xp}-1LdmPUJ)YiYLoyh&?^#7wg2Q zOkX}}-MjbT9zG(|IDfs1{WjthaMlh9`tIHT6JIV8)!fXzf_ZpvU=OK}NBwA!ZcV4K z2ZQ`{0auudD(Eh!r-Q+HM*z}u0mf3mZ(&9{sv*WIO&L8^MRid!lhDOGpv^|i8F8r1 za;+E3MGHYMJE(ywg2FT1D0&(^$t}HVp+VH2w#iG&0r2I`L^a9^qDKk2?=4Fm5Kj7g zU-KW)e1iS-q4yqV6d|N*G)^%zbnZlk31T;Hqwv1CH?Xd{J8xg_ZN1v0VT%^E16?5; zjz3+q59uMu0gB)^yWQt&ueM(P4J~0%*(2>t78KHtj~GBFyX)>e=9WXeeKJ3b8;^Fi zPp9AsY%e2w{CVpGhVY(?97u-+y^*M|o4B>=dIs_uV>)1`q+2UNs}*gL#{TK42B3dx zF72?*63h|IL7#UnopQ^QaeK z5>=+gT^Vu0sk0RMJozx3Zt(6Z9oWLOcRW}R=LZ_x6FAO3N ztKzS)kS08SdN5tVY_>*u8<4~%McmT&L>aDdOd1L(tF5GmeH-X65k`hhyo6LhkJh;@lflvpL4G!gFN zYQgdOPL(kQ%^1oJhDb->s`Id+a*=4uJstx zXd%daN#HVV^vV^)yjkz;5;hy{Niy7aZqtQJb6N;OMl=>~sKLD$7q}O*)r8)jQ!s?l!tBq|JCe3`rqWtF#4&B0Kk=IEiPMJPa^+MI$^0e3jY5Cq zUAb6F5;7*G{j1$lS3l*bO)}xQRE@CC&YvL~WkGTOPx#vxID zs)C_&DknJrl}Q}*g(QRm8AQ=LqDcS%*n(!2OX(W!kqawZy$Pzd7;rtg`0M!4_>v6@ z6$T6#g($YTFe&Z(*ut<+*`h%#;mmk^cow!jENB&s(T9ufDr*)BXfeA(|G{Z$3zh5jADa1%!?2B+(ow}5Wj*Iqbl(-Xs8XEy z5}>^P)xs7F6<`}f_qi8EU1Ysi{bVCfjIKaL8>tqG>kMGqcP<)MG4%kE5*exo4VMHI zM;#|guAyBBca5Us{zspPMJ6ooQTi%2A3 z#zbNf%melRc)R&_b7KzsL1u_8il;}sEfR1Tr_~_<+##oOvU+)xqbnGESa09QW+eQ~ zT$$-bvX~qEj$z(&&q`A#GC*yc7DO;HvpZ)Ma6!A+VUBWNKWlw~Qz)(El+5O&;Z&KX zrKM22tSnb}V2cYItL}+l6Tu-M2ZUl2oIa1@Ech&BU#<UWLdko1r!;Ta0s5g68Rm zv1M|3hQL3zXp^qeP7AErYo>a)f&pAMp2w1g)nsF3^upO~!_tSju;rWSDqv;*V5PJ8#mygX zJ$n3yzhhTJ?x+iOl0k%q2x0hvj-@$lX-odl_DYA_1lYEaV_ggP7w#88_~f>4wKJrs z^uSX9{saH{g8$saKerA>w-7hu1*F8SAAYdEU)bN9^7rP;=QkxeX&AyXnv9gvL??74 zK=L5!WYB+xz#>!!?v74+A})*`A3*5=%@Toy0fkmZED(Ro zmixM-g4ZqVbvs&purdCK3?aQfFRbo;V(82+Zg--cH^7jBy;sz#yC|Tp$vs{N!90CU z4XyW{0e7c^1OHNV#I5{;7Z(@j2ROKKj@??@f)Aa=0uY{`KELXXa9Ip;2HHn=;dhXp z_hJy^g22Rq^A{|?zHLooM;Q6?QQv2LVT!O|=7)<<4nO0jXkaUSHpVUH!_nJG8g-7t zBBz6zN3SK{Tx<_yGGT7NxUp`)z$Qb`hg`7`z~KQr47KvSylRLVH&i+w3?}PH-GbBl zyf__Kuc15ByJ@*T;=EwQ?wxXbz5))reu1#f!Gw<2(D)RoX$NbUawNM_r-$b+2B(O7 zORMw;i1z_~z0!^(Uj7id3@5=pce!miQ10<>`Le!;JmV1TK23bb$zascbY`cHdHmL&Ezb|%Aj?SsEZmy;>B zA3dd3?8Ux;#fH>P-%>509ibB{j39f1@C zH(Lnupf5S!LZXBy?Z2b(IfQuNW%Ok%Y~RSa_=*mN|Iis%ba+xA4GX7eju=j0OEone zG0EFSYP~{^7;m-uBAD?j59k>fc$E;FJX| z58{$TFk(z%E$ltn)qCx6B|^_3h5f?(X`F%~zXISi13oYEYogIxaTwG>X6Q+8X6c`?zxP?-kZCGr%!b=CIU0^Jf7*3z-U=a-Mvz}-0y%wNmQY_6N1{b-KBLyaIZas7xdN@7 zTQoHbg?J5L70CU$G74}NX5I-s$?;83K7eZN)8KRtpoe zx3ZF^NrMG7oLlWbAYu&XKYOzA+?DpeL7-YgM`0KZ?-gaq1c!1bSS~rB#l!u!hKzT` zAvTrhilDOCmf=0K&#^GYN>qFaab)ZS(Sp2)Xjv~GM6QA5@#H;kw^gQmk2xx9w z6&{_&yz*w6!_Om#jt@txxQb6)OxjFB1P5bSGA(cO0l10xo8lNj=$x3noFcYT z3a|mGDs@mCIvr6QWWhEf4;X?euUnljri&6_R_CCU`wN6{5{?7dLZL<` ziWtcKT|fU|)Y~Dg9xN|D2}FM&RDZrAu4IdK&YG>_?AQ{P7}Oi@LB?eI+8uY(0=0BTV%ifu$Lzbpq!H zRapNZstw zZkKgPZI9?f2ra4p7>87F@(;AOV>m!AQ!Hs-$O7g&-Ufj~=ruT^)YG9aaeKzJO}lq~ zeoEgKNTD!PXVTYElo`UrD48VwDUmDe7hIeb# zVN5t3yiUIfN#^BX^j@5GS4}xY^RJ&Ac)QUHrUy~Ga9s77S1&~S7yTi1w=9iXAQHyz zGSU~xE+ELHUXDC6@Ia!vm0m$h4VbrL$x8hiY=dr03}|OQ8&0{J@)S=YBx7F4dQeCO zApj$|P?ueQTEVy)LP*GbRlU8^c|Ga#U~*aqoi%L^*lC(y3xpN><+|6 z81i~>OR29}T%3t>KPko})&y}MDHtdjVeli)AsN+C+!Mg4-1I?KVx{{MS_!5oKq1-0 z0N^^dWGl!~8t~WZ5Bo?_f6)@|)EoeC6x9)@{8o0a+D{G_gC*SbBIaN=AZgyQ_oO&2YoRnUXa&o?m z{&M4KF}#i!q({^5stuArMO{8k}Tm5-E5ZKCKOhqn;3Va8&mB46%$JV{M7o0Yx`Ob32%6S7?fO%E%Lw=>vN0>_BWPbgNZCtknm&$QGG*Xi<+)n zVB;FQebW=%MuFy*&-(x7jV-Y;iTXUl1b~c>#|a>^(lnFS28NDYDFkoaK6!uBQ z>xO5F8CtHk^PvxXJp$yxcuC^XVFe_Hq_6Q^d)UR2=1$~|6O??arfE9_TVyVAk#%$p z&5a2+ZD#N|FaKIA7Y$a%a{V^F2^G4w{0s9X>(Dlqj)#{>izD8gwq(t*DWcy~bq-?$ zx8*c7Qv5!X(r#ymwWcK8skm^Kkl~l^q4x4m!*gjJZ0cayEMe_z^%BQ+UMBjtx7YSw zfMMA#Ia=G5zOxws+vmF*+(2D9xi~vLIMkhhWWI215v4H1Pqz9LMI;g?aZpRiK(VDc zkF|i%FZXh!dP-0L_u;*Vk`Kw##H8yG!#d!?5?;hY$DNV=F%uNxl$b3{*hW>Vg~edI zH#s_i0W3KG#|&=aq&Iy&K0O_O9AG7*j$N>ut=r3?WA%qT0~FjTE92(5FaPqF@+oKa z%X<&yvEn&;`O{!h?^RN`HGre*?>MoBVs$2X1NF9igYZ%sQ5jX4;bMGo&0CE6tH1oE z)LEG#Gca8us=|v7_k;wgdYD})1__p48r3G;y%_Xeq*q)s6qF5%EFMBe2$q3lB!*xq z$wH5F*`5dun!fL`MSaom4R)E@@Cin!Ew5#!xg2x_bj@W^b%~Y(ld7IbI!4*Ed}vOs zT*HABW;oSmPT4ISad%M!UQj-V*VL?md@99#8eDM*TK^(?;BYwpRx#B|ver6agiyqt z(@>aQ2?|5jv7b(dhw7_zV=0Oi&Zs$Hgqd&Fu^p+i!I^cPiE3EB#ub8M+YK1ndeU=p zBas_nX&F$u2?ecR*lup1wWIaLjY+r!aBf$@hj&4pcyi?aU-p|DbS|wj{TG##lQf8q zHkl}I*$9!{O~R^HP{CD*RH@U};e=ay@Y><&%0?`YTB8RZd35}>_wW+w}eX1J7?EW zFw}C8SL8)#B*IJJkhO$A)AeW#4JI&AI7h;CF_+fIf>68|slPRJ0@ujv7mmJ3A(|i5p7#63795PMWS`ED5vz3Hqnih5Mw1sQu=c8fWtzl&R7sn z#lv>o32R+sUzoHkHz?h*K=|F(7szpeGz&c(%MkbM;pf)+cbiKO?%n@#>5mT|+!L0@ z@o_qtm{{iYa|_FPoYFaK5q^t;Z6P3Oh&wEy?e4ZdGN1HhJbI6^(@Wf#i-ZrKk@?~B zTsE7rj}?~T$(-NdN?3xN#KKJ!eu8kvj*u9U78or~uj4wb#j}fKE{6asbR{h?*7nFNf3rrw*sN$wm1r4^pe$j}eBb;gP5|U_aGF70$W&%bl=K zxFbyLS0rhG$dS_%wp|5IrO^eZNOpqlp#&HP5#>V5_o1wiZRn=8xlU)JB3oj88WB(| z!5|6#p_sSM_D%HiwxmhQR@Wqn)D*j9z9v;VH|{LNJ%{o{vM=irXv9JlQx)by<2GUo zi~JO+#75j6c@_8FD@SZPZM+?x;hH7!oWmA%an@pp5{%Ive7pVSmV4T`FNvM8W>64p zCRvEoDhVxhNd52O}9&_pds)sOI2B30HNR)rAMQ z+ARd~=B^bDA{^jSWvNm-P$B+9kj;-UI_kj26$IVgt^eHYZtbqRGIuW} z+-30X&gL#!NKBy6%Hx)L`BW~3B_XX*{{iz0ogrm@2i}UiOqqSL*pef4INZvUR>?%Q zR@@{GJ=|`gnruk0|4z)JE5S@`{EOCCVymN^t}@e}!OIyWlwxMrqyfr_!IVBGmXg+j zNp!Uv&ZDBz>WQl+chme5CIG3Oc*bYdABgP<2ue+XN2M!T)R4v@fZjnCxHQ$IIewq$ z!2vul*3H#`p(HgHV;5V;ujRC$Ta9r=qSKxlwUieH5V3bqtkYp?RGPueBl>pIjJa|Q zB)6_ZA$d?H(~*a{EUsa~_Ri+s4)QviO<#josGT>um0lN+T-n{yVEt1p_GJ4#ViyBV z`K35TqDC^+OmnaTRx6s`9iBRf!~*xaCCeCF^qK6U=!Ap`V*5NK@Z!zx9+6KLs_7H% zcj8Vy!sXg9ScMxBCzprIAZOfw{CCiibjdfp^Wk#xRyfE2YTr)Z7`7KK0-i#U!usp3 z1_{X8leIbT)pEP9x4PKkgy8Rv-~rIAaatTvt;w1e*f6@eVzFz;@ZfpmnnPq|&B{4# z+~Y#H#?&L0*4LF4(VG>mER|=5x71gPi|Dr!sr!iAw~xP=K3b74JovNzS~hpF&Bn}n zYo`}nK8CG%nWmx^JfI6Uu>JNCBH{5n9sCX3i)sqQshhTZdb8H+=b4DiLvc8vq2e*( z-rZ&-o!*@7aFHz~B}o!&Z4^4F{z}N(Ar_c>>=L;E`>`rgji#D>?8+f3hxbYF?KK<~ zIKNohWY7~f=y8bZPI6^_?{Z(`w;34)#WOw{ zpLWebBYby%8Oh9j@H&oliwGW%R$E7>2u83mM5SY$ruqc{(iMe(MT_gywF`FV6h#1R9gSaiel`gG^+=g6e956N~Er7^jAY;uv3ZJ2DkkOGwBqZ-Z5y`JwJ` zkgeDn)%9fUiq;pKRm#Fl>3WdrQ1nN>nGc-o_#VpKLBQUGM&+>8M9ulj7eXpASila$ z2|eSw(}^kp%8$vi8Mw)j330 zGR87`y|a9$^8_(Me}B5zUY3{t>k@_}uHw~TkqlYcT`A(90QDBS{&F_ zfe26>|J2xnJg&_qC_2&28(5Lbn7j43kr6h8z_^$WljkmM3ws{^DT#O{`lCnU;90N# ziih(0>+n8>@bFGFCBK+z1Sj`Y5Zi&=g$RlqafL#c*H_Dbgn`)YzS`V-@n)mjwT%&r zg8Rf($Zk{W3RnkpR*Kw-;QMKc5W39F*Q$Jx@ES49>2FKM)fM}Y=AgWlxbPcox`U2C z%R6~2=)H^k-tM**k@cFkf{G4Zc8L&HwFju5>NQ`KiP!|39D<9jzJ`~5x)AwdGe<|4 z=+PU;V&_0h%Q+>IRcKNR%^5zfxFL+GaDz^oKdf6AtyqFkKwPI0>puxPmpIde2&tS9 zZ=Nra?xt%`L1DJy#B9?KG9FQGqJLCcIIY69nP7nlA zIc0TB_BL5M3Vq6g-kdPo$ZHg@u>o8Nh9O`N?gL1%ANlLg_w1MEXMgzcOZ=WrPaZ5! zLxl(7(UCQQC!BLjK7`pytdIzxEEEL6_grDeQ%&<(}GD2w*fpMu~ZBb58$kZtec(p~0*r>m0 zF)XaBFnDwt8EdZX8eT^LdviKnW-G8ju?gQ>O;7NRv8{ixq%N+n_2|#t_Hz9hAa0HOvIs7WYT!e^`0PwB3A27-aTgIpW`E?gr`#q+lqnJaBQNhT&$hgC!A2FvY z|2RMTX~+RH#f6f@BX2LBvL027>wVnaf#4*4r8ck?+U$P6w(}ZyOyW+tP1(a(-;}R! zd3j#?P9AWR!wPI{ZX;hIPJV2}^2IakjBKs%5km{(Q{HnWyYBQ(fc-$bOxB@s^g38w zm7?g^H_DRIz#m%wTLwp57_alW%(>vE0X0AioyFnqN89XAyB4IBoeJi==eXJ+FRF9y z^AR#f*~EqAGB>BdXZTmLG;=O==Tut@6Ncd!F#$y^9+N@4hrt^$L#R8&`PPA)cb5HB z*yOk>jMrobjD|5MxWY3Hzi8Y9wdqJItB#P||Dr0dLFv4dVBHR!9QYR;zRwqxhaTrPFR<;$4NW?TEn>!gkQveN&{8*GmlSRTu$d6J zP2fF&{;9~W^y!I8vyQ4zVDLJ$0Bh8{t!(7|F| z!u)k>NTyb+&L$!pmn+b0L=xMCWws6*5#Xy5bd$vuLgRo{OEkWAV$d{tieTRMOsVce zs$%i#MoWL_jJ8zl%Svd$hADFhN3FV zeV+Ax9O#KeIA#Q0Qvir9{`V*Ku&5bX1Cco4|2c@74YUx8VJ;IkDHUCt8Pph>-9!+g zvx~``Iwv{j&K9C+gHqkhYRD+Pfd0jc?_T-Yc{>b?+t0s4I#`Y2XtRKX8KosbT)rw; z$cxi=BoR#5g?_d6*Ecv>tOxTMnS}HIh;8DVt=A22b~bVPcz4rliEt32NkxN@BWx^C zrEgP7!ZKpEmM<)|SCuPO( z?EDl$a$Pxq2e@F@4Mrc32xAM~ zN>zL#=xy~i#dV7+7gb_Z^9^ekGzjO`hNtr_6?|c%a_@>pE12Aue+x&$^AnIT9tGlB z1$CK8*xp>p}Z*bK}Jw01bd+Psu=L4x)US*Ng3CBS)q9CK)oGHxIXbnb(W$g`S8b z>#0Bvp4zljU$CJ9UA4un^`sp`aDBJQ(^~q#Ll!ZjF)Ucg*uRy^9+1}XfN;mx_KSbk z*IsSDe7PkBHa2&c)}O6^=@oAFUcY_$lBI4A!ne);pkq)TqP+awSr;O_`D)`U`MtgS z@A9`ts+Vt|A)&BJ4}8I?s=WC3oo8Q_Pi^CXeC%d?>J6@JQ7B8AKU;rbEYLLB=0v_{ z(MR~liB$O$$}-ByvS-LMD?4lRL;c;gt04ktX}xR}f;=(uaF)4H6iqdjHW;K^suGJ@ z2q5ZlyD)}Th^MNi6z@@XDAkn#;9^t{B<1esDe{1>_jZuPLhlmT=H1IC6y5IjdzQ5g zRi2IAp^ND03${y5aUDZeoQpq>Ja@!9W{ZJ$ZaxPVrZNJa3EM%q0{upvKu{qPE+Pyz zL=x9zXP5@-JvKF?B4$eU1XBv8TYq)S32wc@S&X~`SSIx0I^sxM9}zgl?LX{Z)uNE7wlgN4Lfw3ZMg_6xbwox0i?`}*a^ zuXi|-WDB=nc~^;G(L8w{MsSV*N`%S2TKkXg|JeImckS)oi!Pklo4Y{CR{&6O?g7>d zaN+bBl7_qCEANm3gAr?$=W@eH-V9b5m10Lc-?1LsNB>F`1qfKD@xu2?{r`0#iL>`6 z!vPf$Bir&$m81Q!-ZA1k54Pgbm#ik)UE(~a_h#s3_|}`?CgnX7)!Q?+^BZ&}QeJMV zPYhKJMWM$4GFOmdAtJc_6iL;SnkUoT@FmmgD)J@uCVbxB_}j~k?#r!bJ8QVLiOFZweZn^5r^Mdi6C5t|{DJGw z5D@@jjKShx-1`c^X}9BtNT2WvGjfPfNxY73ybKo&sa~~VMXU@su$VqGSiVQ#HmP+- z(@sXfco(>XM~LvlKnchT18o>5qiwhi5(aDSWTUMcp9_O)Kq{-|nQ&G%_Ukb){V-Y& z0M9@I3B!j*qElGKxi52z6rO2IIvoU%(k@<;r%@mv$=HTayM~EyHJ8ZP{31HP`Wb;u zIG&IBky}xGl57`Ou8z}`NRqn@j}5!pvwHm8U8z^xL1gdPh5#ej;4T!eNRrUIXu^ks z+_>RVJ8Hy0G4z`h@paW3>v$o!rCf=~FkW7IT*EttBa9sL1hleCJNQF}zLXR|PD_d< z(V}_;_>G{grYFqdW_pbvpu+v&r=h&BrI{bt7t@9TfFe7IT2_|^gsST8Nty(@V}J!i zpQ%`QKNB&p#~D`JlFI6;W>^`)&_8L$I?V_(Uro^JYHRcUpK!bB{W&|+Di7{|b?@Fo zRLRY(16&=-OZ;LqNitv+AjFHGls1)VK6PcK{+M!UBfYUlXiDFvTJFvr-mE6uPgV6z zxSZsTNq-yT3kgLxe08KZ+8>!gpq1DNbg_RJBD<;t9!6?n(j)T@QJ9u;6w9IY!W!VN za&8~U{+}YHd`03ep9%OXxdBvpU@SvAMO->d1&0T9lYtADXicr& z68Wq6GO`1j)WWjrN%4Jgwu)a;&K_w!f`jpNZqXacmpFfkX6ve-W2^Y%c!F!)qolFw zNZ;`SG_f|CGAifRww2VjLSw~jS6W%;`iyz`fz8gX-4n^YmEcC^pBq82A(DYTX>~p$ z_u~lHkqSm!Omct;iI6C(RFZ1BqqE#j0D#f|ct*1(I*;KerenCZv}7@2tbC)rv;-1Z zN=X3z8AB$!3bVJXyNvK=Z0APh>Z;zfCH5i_^9b)%uFvZ?1}^@VyV zHX3kI0T7&%GEOGm4AaaS6g!GI0%n$$=;0?_;RlfAG{j#SW+))nNg#BoH=;yo7Kn6e z1>4S3+zArJ6B*S+5V5okzRC>}h$h5=DM_RTJlVses3~r}hT8iu5=ntzLlsd-S(f6< z{3<%MgsgEGDKMNIOrKdoyz|NY)=zIl+dpGuRkWotC<0ppYala&lc?6C&WZ(lPmP$e z`_f~3A&rx6qn#0sHi)^+~mgvn)D)^MktQTGD27?-OlK}0bGUT~wqRUJHx+5F;ONc)l9;v~T z17&E0$P)w#I^`ZiZ90l>Zxe&e+vL(fPa>v~zKUX5LD0vPoYduH87ok)YCrqZg!y#C z^tihsr6xjDsh1X*N2k>^5P1*-YQj;))0GivhQxWMPiPKvhZXe@m6J%|fIg5U5oRZY zP)>(xj^!pelexSSnMs@4mnRpJ7etGDv1^)&SmV4THH46wzzb+pBo+ReLjj0#I7J!#o0Pd>F2>KJBh8r%63sql^Oqi|z9mhCk+{X=@ux@;cl0AR{ddt) z=jC{OPDlRu{BFzx6XO*TQ06&TCqTElMN0VjTw26QFZ~k0rVDNG!JcALf5*9Uao4;@RqemXOXDUn4l6lqU*Eg?&pMtdwa3CRq%laS;z z*1;0JAdjUT@;u+WLAWi|`$5iMoU z>~;dwmyL&CD$!K9{$E3n!UF~D+kVW#84#rXUsk~l%|U7W-%b&LRR z1UvlBw&dhHf}1f$?hXn8$l3{VDn6DDo*`)yU$Fw^;Et-@zXlpEz5Yg)NoE%ro9kOlcq-w&|cRji3XO)7`THaxoy`8e2xpLy!4o zEL_bWGUp7%cmSHvC5PL5EQBQrio828o`B8HMP7)}DY0`pP>zW&U-ggyPL9mk10SK9 zsi*;h_3zjgH$3XfSTLD_4Tua8ZnQ`9Hll`eiBH3HUO5Fp9Q!7JIzKX4X8!@w(us3J zg_13X)ih=&wiYb1y!+{^dw=Q5f-9qj?4C!_XU5vBj@i|uL%fR$vty@!^R-w|bF$H< z=Yxysp4}G6B+qS>=@C%@8B8IjT(8K=(jxiFt=gb0e5SZ~5Ucs052m;8;y+pNC~#La z+6*HvriEL)W4zHjPp`Qok8nDP*!;`^-(XK^%NWrmTyhFW>RAUP!82tyTvcd7 z-D%X25AjfpSh%lv7C0En2KUKg^E1QU6nqnD-Q!Pdk9Cvjc?hX&!A@)|bIkScTpX00 z5>HtLe6)ZTpvmBZv^?BS@aK3cr|;}l$6tUukt+QVNAyGkrtMK=X?y2`GqPXS_{{!JcYb+s^n()L1|uQ%$7pd-#Riup_j z0AM5736lT!jj*dsh&{Xu zCo90(-iGV7FJZy@U}&vLEm3C3%VRA({0Tt?m&dcvRN#&18 zSNA2?kqtWb=Y!fAydUsFZ4tYct0);qm>(~Pj{L++r+sj68{=;8_m+OTxAd2#cXx<| ztYtE4_5fYo`uT^S^M}7I{BkRj?~HlOn&RBsxOApu3}StB^7a#}3;^i_7Z~~V{x9_t zbAo(n3-2jNTR-4&2rzKB97YdAQuHc+R1zzE;Egmp=_7>_IH@r96?Ir6SM@Qjkbw^7?NYm@NlamhT)acYKiIuzmj%Q+o}`7yo?+ z3G5!&Wb4E!erHY}Mu*}4HyB(pha%ENW)~Ok)zEf+Bj4QQ9jfUK^(Bg6vIt#AG5}(K zBF!HxAO7@UZh8;h$OV&g5IQ?{vL-9YNk3zbhtM8yE(B6f&;)KpQiZxqv&R4ac5Vb| zi%LeItCbJ?Up{=d0)HZNqE3S-5DJqV1GqXQ6@lSl z1fYt`Dkv%PRFFvjj^7-c@z!z*UGTYzZ*iN*-GWHvyHwsSNOYQv6Tt%*PJ2)R*y=Ii zCZcmi{|zFA(u-LG6V7Wd+=tksl28#t{T62WO4u;H*MhBPMis;=5;31oR!tUWOpwgz zqEBgi;ph9ml*0&y9Wh0u%cnu1f&t#hb@R1q)cvaY+D;^Lo5wKZI5{W9jYIFz(xE~Z z(P~Ek=VH1UDh<@^BF~k0NoYW`tx7ZvQ(1<5N>oYckVjb!1lU=*Lgp?@SW0L+V(($8 z5C@Xxk`@%Q;V>u~2%NcP-Q0mSBFT2s`uUssCU8U(~!a*ZR-l zc`*K)mfR3l>xrM=cdSs=1=Y>q=Zcp`B2Bfh_DuKt9yTw~PK$|oBxn_u=v)fFUwN(AWQn*0iSo+1gILzi)8mh=o-R>iP$(6+0ZjZ ze5qHCvof8~c&$)i4)|L-!u>f}$7m1k8|;gTZ%KxM;=A&MM=&TrigpPCM=q{sU7~9X zGVTbQJ}hqUv63PX8gRs3IlLe=R2fdP6GwsN5`-m_DkBHE9|ITS@g$+V<_sPsp;mw*F@KKcLEZTSrsUuNxW$gptTM0(>hN!kV3$-P z#s9U@tlR$SMzfG|Mz#^xXaLHHgyT=;-vaKiE3n z`~>?LluhYLHEqg^KRxk^zC-w|RZ>xl!01dlA{0d~f~GIOY`xpv*xKoKLAlGzD<9zh zA5T^g6N+S`I0%kJE~Ixk;yq^*Xa)Z-S|G~|j!tmFYsfW)!!fv)Q}*C(t&U)EXnh2_ z9`WewrCfUQU#@lmH$0lBr~lPn=JIFr&C6w^QW#i9DwvBmmnwXB{4o_a3*^hz1H^b&eA=EFXGQbz3i1G&wA&UqETtq z(MZ%p8Wii%F@4~j9wdFypwtOq&6O3{rtdK<03l{GNK8f+hA<@>__0oKMyQXQ(|13g z9gjz!H4P?rO0cqtJR>A0Po=l!`luB@vHmeKfDdrM=ttVd8MJbm3SpTtQO-_-Nd-tB zRb{w1eW+A~r6k95p7$wvZ zW87ihVlWLTWbmoS3C6v?$8zs3v!;f0b-G%yxSI-E*=3jurlnv+6}V}LZ7;a%PW#xh zXmu*pFfXI&7z{{DPPiHy8#o_>);QRRILtPfM0TrM2r$&sY%HQ+VHE(%kl(&>w!%iB z*d>w+7ZEa!YY6^eJ197!ogj-?4v{s(WUj<�W!uqlnyIvfDfLF@hS1BK7!xs$hu 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%0Aej6}VuhKOU+m%IG^YGf?M~J(YYM9+WDOOtePkF9 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 jfk?&zksZL|}TGv^C}2!gx%^Tl_XWA;=V!Rh)nU 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̈́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 From 29b24fa506f7b18feee06da2afda32141bdcd4e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 20 Sep 2022 11:26:05 +0200 Subject: [PATCH 058/120] [Budget] Feature: allow to desactive resources and charges in the configuration A new key `active` (default `true`) allow to activate or desactivate the line in the resource or charge. --- CHANGELOG.md | 1 + .../Config/ConfigRepository.php | 30 ++++++++++++++----- .../DependencyInjection/Configuration.php | 7 ++--- .../ChillBudgetBundle/Form/ChargeType.php | 2 +- .../ChillBudgetBundle/Form/ResourceType.php | 2 +- 5 files changed, 27 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d635f2e06..e0d455e72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to * [workflow]: Fixed: the notification is sent when the user is added to the first step. +* [budget] Feature: allow to desactivate some charges and resources, adding an `active` key in the configuration ## Test releases diff --git a/src/Bundle/ChillBudgetBundle/Config/ConfigRepository.php b/src/Bundle/ChillBudgetBundle/Config/ConfigRepository.php index 73f6fb355..e498ba5a3 100644 --- a/src/Bundle/ChillBudgetBundle/Config/ConfigRepository.php +++ b/src/Bundle/ChillBudgetBundle/Config/ConfigRepository.php @@ -29,44 +29,58 @@ class ConfigRepository $this->charges = $charges; } - public function getChargesKeys(): array + public function getChargesKeys(bool $onlyActive = false): array { - return array_map(static function ($element) { return $element['key']; }, $this->charges); + return array_map(static function ($element) { return $element['key']; }, $this->getCharges($onlyActive)); } /** * @return array where keys are the resource'key and label the ressource label */ - public function getChargesLabels() + public function getChargesLabels(bool $onlyActive = false) { $charges = []; - foreach ($this->charges as $definition) { + foreach ($this->getCharges($onlyActive) as $definition) { $charges[$definition['key']] = $this->normalizeLabel($definition['labels']); } return $charges; } - public function getResourcesKeys(): array + public function getResourcesKeys(bool $onlyActive = false): array { - return array_map(static function ($element) { return $element['key']; }, $this->resources); + return array_map(static function ($element) { return $element['key']; }, $this->getResources($onlyActive)); } /** * @return array where keys are the resource'key and label the ressource label */ - public function getResourcesLabels() + public function getResourcesLabels(bool $onlyActive = false) { $resources = []; - foreach ($this->resources as $definition) { + foreach ($this->getResources($onlyActive) as $definition) { $resources[$definition['key']] = $this->normalizeLabel($definition['labels']); } return $resources; } + private function getCharges(bool $onlyActive = false): array + { + return $onlyActive ? + array_filter($this->charges, function ($el) { return $el['active']; }) + : $this->charges; + } + + private function getResources(bool $onlyActive = false): array + { + return $onlyActive ? + array_filter($this->resources, function ($el) { return $el['active']; }) + : $this->resources; + } + private function normalizeLabel($labels) { $normalizedLabels = []; diff --git a/src/Bundle/ChillBudgetBundle/DependencyInjection/Configuration.php b/src/Bundle/ChillBudgetBundle/DependencyInjection/Configuration.php index a18e17ae6..710fe43f0 100644 --- a/src/Bundle/ChillBudgetBundle/DependencyInjection/Configuration.php +++ b/src/Bundle/ChillBudgetBundle/DependencyInjection/Configuration.php @@ -14,11 +14,6 @@ namespace Chill\BudgetBundle\DependencyInjection; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; -/** - * This is the class that validates and merges configuration from your app/config files. - * - * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/configuration.html} - */ class Configuration implements ConfigurationInterface { public function getConfigTreeBuilder() @@ -37,6 +32,7 @@ class Configuration implements ConfigurationInterface ->info('the key stored in database') ->example('salary') ->end() + ->booleanNode('active')->defaultTrue()->end() ->arrayNode('labels')->isRequired()->requiresAtLeastOneElement() ->arrayPrototype() ->children() @@ -59,6 +55,7 @@ class Configuration implements ConfigurationInterface ->info('the key stored in database') ->example('salary') ->end() + ->booleanNode('active')->defaultTrue()->end() ->arrayNode('labels')->isRequired()->requiresAtLeastOneElement() ->arrayPrototype() ->children() diff --git a/src/Bundle/ChillBudgetBundle/Form/ChargeType.php b/src/Bundle/ChillBudgetBundle/Form/ChargeType.php index 1e054d86b..85e7e45aa 100644 --- a/src/Bundle/ChillBudgetBundle/Form/ChargeType.php +++ b/src/Bundle/ChillBudgetBundle/Form/ChargeType.php @@ -103,7 +103,7 @@ class ChargeType extends AbstractType private function getTypes() { $charges = $this->configRepository - ->getChargesLabels(); + ->getChargesLabels(true); // rewrite labels to filter in language foreach ($charges as $key => $labels) { diff --git a/src/Bundle/ChillBudgetBundle/Form/ResourceType.php b/src/Bundle/ChillBudgetBundle/Form/ResourceType.php index 51e3f6799..5ad388a39 100644 --- a/src/Bundle/ChillBudgetBundle/Form/ResourceType.php +++ b/src/Bundle/ChillBudgetBundle/Form/ResourceType.php @@ -87,7 +87,7 @@ class ResourceType extends AbstractType private function getTypes() { $resources = $this->configRepository - ->getResourcesLabels(); + ->getResourcesLabels(true); // rewrite labels to filter in language foreach ($resources as $key => $labels) { From cdb9d967ff48016f9f2bbcf8fc746009cb86e07c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 20 Sep 2022 11:49:14 +0200 Subject: [PATCH 059/120] [person] Feature: on evaluation, add an url field on the admin --- CHANGELOG.md | 1 + .../ChillPersonBundle/Form/SocialWork/EvaluationType.php | 5 +++++ src/Bundle/ChillPersonBundle/translations/messages.fr.yml | 1 + 3 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0d455e72..1131b2117 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to * [workflow]: Fixed: the notification is sent when the user is added to the first step. * [budget] Feature: allow to desactivate some charges and resources, adding an `active` key in the configuration +* [person] Feature: on Evaluation, allow to configure an URL from the admin ## Test releases diff --git a/src/Bundle/ChillPersonBundle/Form/SocialWork/EvaluationType.php b/src/Bundle/ChillPersonBundle/Form/SocialWork/EvaluationType.php index bbe82b59f..c1261754c 100644 --- a/src/Bundle/ChillPersonBundle/Form/SocialWork/EvaluationType.php +++ b/src/Bundle/ChillPersonBundle/Form/SocialWork/EvaluationType.php @@ -16,6 +16,7 @@ use Chill\MainBundle\Form\Type\TranslatableStringFormType; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\SocialWork\Evaluation; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\UrlType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -40,6 +41,10 @@ class EvaluationType extends AbstractType ->add('title', TranslatableStringFormType::class, [ 'label' => 'Nom', ]) + ->add('url', UrlType::class, [ + 'label' => 'evaluation.url', + 'required' => false, + ]) ->add('delay', DateIntervalType::class, [ 'label' => 'evaluation.delay', 'required' => false, diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index e105dd45f..28565ff23 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -675,6 +675,7 @@ origin: evaluation: delay: Délai notificationDelay: Délai de notification + url: Lien internet goal: desactivationDate: Date de désactivation From 66f7ef8c51d66bb88b6cd2b457da1981a12da1a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 20 Sep 2022 12:00:01 +0200 Subject: [PATCH 060/120] prepare for beta2 release --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1131b2117..030b4ba33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,12 +11,15 @@ and this project adheres to ## Unreleased + +## Test releases + +### 2.0.0-beta2 + * [workflow]: Fixed: the notification is sent when the user is added to the first step. * [budget] Feature: allow to desactivate some charges and resources, adding an `active` key in the configuration * [person] Feature: on Evaluation, allow to configure an URL from the admin -## Test releases - ### 2022-06 * [workflow]: added pagination to workflow list page From 59b22dbb6dbc898d42cee26ac38a5b031918c3cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 20 Sep 2022 17:02:35 +0200 Subject: [PATCH 061/120] [person][export] Fixed: rename the alias for `accompanying_period` to `acp` in filter associated with person --- CHANGELOG.md | 1 + ...bstractAccompanyingPeriodExportElement.php | 20 ++++++++++--------- .../AccompanyingPeriodClosingFilter.php | 4 ++-- .../AccompanyingPeriodFilter.php | 6 +++--- .../AccompanyingPeriodOpeningFilter.php | 4 ++-- 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 030b4ba33..f4e0ab7f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to ## Unreleased +* [person][export] Fixed: rename the alias for `accompanying_period` to `acp` in filter associated with person ## Test releases diff --git a/src/Bundle/ChillPersonBundle/Export/AbstractAccompanyingPeriodExportElement.php b/src/Bundle/ChillPersonBundle/Export/AbstractAccompanyingPeriodExportElement.php index 264f47955..d7c98a7d7 100644 --- a/src/Bundle/ChillPersonBundle/Export/AbstractAccompanyingPeriodExportElement.php +++ b/src/Bundle/ChillPersonBundle/Export/AbstractAccompanyingPeriodExportElement.php @@ -25,13 +25,17 @@ class AbstractAccompanyingPeriodExportElement */ protected function addJoinAccompanyingPeriod(QueryBuilder $query): void { - if (false === $this->havingAccompanyingPeriodInJoin($query)) { - if (false === in_array('person', $query->getAllAliases(), true)) { - throw new LogicException("the alias 'person' does not exists in " - . 'query builder'); - } + if (false === in_array('person', $query->getAllAliases(), true)) { + throw new LogicException("the alias 'person' does not exists in " + . 'query builder'); + } - $query->join('person.accompanyingPeriods', 'accompanying_period'); + if (!in_array('acppart', $query->getAllAliases(), true)) { + $query->join('person.accompanyingPeriodParticipations', 'acppart'); + } + + if (!in_array('acp', $query->getAllAliases(), true)) { + $query->join('acppart.accompanyingPeriod', 'acp'); } } @@ -40,8 +44,6 @@ class AbstractAccompanyingPeriodExportElement */ protected function havingAccompanyingPeriodInJoin(QueryBuilder $query): bool { - $joins = $query->getDQLPart('join') ?? []; - - return in_array('accompanying_period', $query->getAllAliases(), true); + return in_array('acp', $query->getAllAliases(), true); } } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodClosingFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodClosingFilter.php index e56782847..9258c9b0a 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodClosingFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodClosingFilter.php @@ -32,8 +32,8 @@ class AccompanyingPeriodClosingFilter extends AbstractAccompanyingPeriodExportEl $this->addJoinAccompanyingPeriod($qb); $clause = $qb->expr()->andX( - $qb->expr()->lte('accompanying_period.closingDate', ':date_to'), - $qb->expr()->gte('accompanying_period.closingDate', ':date_from') + $qb->expr()->lte('acp.closingDate', ':date_to'), + $qb->expr()->gte('acp.closingDate', ':date_from') ); $qb->andWhere($clause); diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodFilter.php index 5a7ccb4a3..0c97ac6bf 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodFilter.php @@ -34,12 +34,12 @@ class AccompanyingPeriodFilter extends AbstractAccompanyingPeriodExportElement i $clause = $qb->expr()->andX(); $clause->add( - $qb->expr()->lte('accompanying_period.openingDate', ':date_to') + $qb->expr()->lte('acp.openingDate', ':date_to') ); $clause->add( $qb->expr()->orX( - $qb->expr()->gte('accompanying_period.closingDate', ':date_from'), - $qb->expr()->isNull('accompanying_period.closingDate') + $qb->expr()->gte('acp.closingDate', ':date_from'), + $qb->expr()->isNull('acp.closingDate') ) ); diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodOpeningFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodOpeningFilter.php index 33805d392..00ff885ba 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodOpeningFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AccompanyingPeriodOpeningFilter.php @@ -32,8 +32,8 @@ class AccompanyingPeriodOpeningFilter extends AbstractAccompanyingPeriodExportEl $this->addJoinAccompanyingPeriod($qb); $clause = $qb->expr()->andX( - $qb->expr()->lte('accompanying_period.openingDate', ':date_to'), - $qb->expr()->gte('accompanying_period.openingDate', ':date_from') + $qb->expr()->lte('acp.openingDate', ':date_to'), + $qb->expr()->gte('acp.openingDate', ':date_from') ); $qb->andWhere($clause); From 5b5470c259d21a146a60a4f7f8e1044548441900 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Tue, 20 Sep 2022 17:12:41 +0200 Subject: [PATCH 062/120] add logger to make prod feedback debug easier --- src/Bundle/ChillMainBundle/Controller/ExportController.php | 6 ++++++ src/Bundle/ChillMainBundle/Export/ExportManager.php | 6 ++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Controller/ExportController.php b/src/Bundle/ChillMainBundle/Controller/ExportController.php index a7404b879..aba48474f 100644 --- a/src/Bundle/ChillMainBundle/Controller/ExportController.php +++ b/src/Bundle/ChillMainBundle/Controller/ExportController.php @@ -443,6 +443,12 @@ class ExportController extends AbstractController } $rawData = unserialize($serialized); + + $this->logger->notice('[export] choices for an export unserialized', [ + 'key' => $key, + 'rawData' => json_encode($rawData) + ]); + $alias = $rawData['alias']; $formCenters = $this->createCreateFormExport($alias, 'generate_centers'); diff --git a/src/Bundle/ChillMainBundle/Export/ExportManager.php b/src/Bundle/ChillMainBundle/Export/ExportManager.php index ed24f1661..dee69237f 100644 --- a/src/Bundle/ChillMainBundle/Export/ExportManager.php +++ b/src/Bundle/ChillMainBundle/Export/ExportManager.php @@ -277,15 +277,17 @@ class ExportManager //handle aggregators $this->handleAggregators($export, $query, $data[ExportType::AGGREGATOR_KEY], $centers); - $this->logger->debug('current query is ' . $query->getDQL(), [ - 'class' => self::class, 'function' => __FUNCTION__, + $this->logger->notice('[export] will execute this qb in export', [ + 'dql' => $query->getDQL() ]); + } else { throw new UnexpectedValueException('The method `intiateQuery` should return ' . 'a `\\Doctrine\\ORM\\NativeQuery` or a `Doctrine\\ORM\\QueryBuilder` ' . 'object.'); } + $result = $export->getResult($query, $data[ExportType::EXPORT_KEY]); if (!is_iterable($result)) { From 052c0e1969184139a2df42fe77c48de6f1199a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 20 Sep 2022 18:50:04 +0200 Subject: [PATCH 063/120] [CS] remove dead code --- .../Export/AbstractAccompanyingPeriodExportElement.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/AbstractAccompanyingPeriodExportElement.php b/src/Bundle/ChillPersonBundle/Export/AbstractAccompanyingPeriodExportElement.php index d7c98a7d7..4fa0b9ad8 100644 --- a/src/Bundle/ChillPersonBundle/Export/AbstractAccompanyingPeriodExportElement.php +++ b/src/Bundle/ChillPersonBundle/Export/AbstractAccompanyingPeriodExportElement.php @@ -38,12 +38,4 @@ class AbstractAccompanyingPeriodExportElement $query->join('acppart.accompanyingPeriod', 'acp'); } } - - /** - * Return true if "accompanying_period" alias is present in the query alises. - */ - protected function havingAccompanyingPeriodInJoin(QueryBuilder $query): bool - { - return in_array('acp', $query->getAllAliases(), true); - } } From 42c395ecc9221e0d88c8a7792cd583cf8f377a3a Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Tue, 20 Sep 2022 17:12:41 +0200 Subject: [PATCH 064/120] export: add logger to make feedback on deployed instances easier to debug --- src/Bundle/ChillMainBundle/Controller/ExportController.php | 6 ++++++ src/Bundle/ChillMainBundle/Export/ExportManager.php | 6 ++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Controller/ExportController.php b/src/Bundle/ChillMainBundle/Controller/ExportController.php index ffd73b777..4ced2cacc 100644 --- a/src/Bundle/ChillMainBundle/Controller/ExportController.php +++ b/src/Bundle/ChillMainBundle/Controller/ExportController.php @@ -442,6 +442,12 @@ class ExportController extends AbstractController } $rawData = unserialize($serialized); + + $this->logger->notice('[export] choices for an export unserialized', [ + 'key' => $key, + 'rawData' => json_encode($rawData) + ]); + $alias = $rawData['alias']; $formCenters = $this->createCreateFormExport($alias, 'generate_centers'); diff --git a/src/Bundle/ChillMainBundle/Export/ExportManager.php b/src/Bundle/ChillMainBundle/Export/ExportManager.php index c1384a3b8..df14a8ac8 100644 --- a/src/Bundle/ChillMainBundle/Export/ExportManager.php +++ b/src/Bundle/ChillMainBundle/Export/ExportManager.php @@ -264,15 +264,17 @@ class ExportManager //handle aggregators $this->handleAggregators($export, $query, $data[ExportType::AGGREGATOR_KEY], $centers); - $this->logger->debug('current query is ' . $query->getDQL(), [ - 'class' => self::class, 'function' => __FUNCTION__, + $this->logger->notice('[export] will execute this qb in export', [ + 'dql' => $query->getDQL() ]); + } else { throw new UnexpectedValueException('The method `intiateQuery` should return ' . 'a `\\Doctrine\\ORM\\NativeQuery` or a `Doctrine\\ORM\\QueryBuilder` ' . 'object.'); } + $result = $export->getResult($query, $data[ExportType::EXPORT_KEY]); if (!is_iterable($result)) { From d599792de88e5fc7daccab34dad9eb5361385624 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 21 Sep 2022 10:16:08 +0200 Subject: [PATCH 065/120] enable commented filter (!?) --- .../config/services/exports_social_actions.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml index 9b257e684..450899659 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_social_actions.yaml @@ -9,12 +9,12 @@ services: ## FILTERS - #chill.person.export.filter_social_work_type: - # class: Chill\PersonBundle\Export\Filter\SocialWorkFilters\SocialWorkTypeFilter - # autowire: true - # autoconfigure: true - # tags: - # - { name: chill.export_filter, alias: social_work_type_filter } + chill.person.export.filter_social_work_type: + class: Chill\PersonBundle\Export\Filter\SocialWorkFilters\SocialWorkTypeFilter + autowire: true + autoconfigure: true + tags: + - { name: chill.export_filter, alias: social_work_type_filter } chill.person.export.filter_scope: class: Chill\PersonBundle\Export\Filter\SocialWorkFilters\ScopeFilter From b25a1c3cbb63eae58aaa01ef25d45724a86c716e Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Mon, 19 Sep 2022 17:01:00 +0200 Subject: [PATCH 066/120] correct import path with filter/aggr --- .../Export/Aggregator/ActivityReasonAggregatorTest.php | 6 ++---- .../Tests/Export/Aggregator/ActivityTypeAggregatorTest.php | 6 ++---- .../Tests/Export/Aggregator/ActivityUserAggregatorTest.php | 6 ++---- .../Tests/Export/Filter/ActivityReasonFilterTest.php | 6 ++---- .../Filter/PersonHavingActivityBetweenDateFilterTest.php | 6 ++---- .../Aggregator/PersonAggregators/AgeAggregatorTest.php | 6 ++---- .../Aggregator/PersonAggregators/GenderAggregatorTest.php | 6 ++---- .../PersonAggregators/NationalityAggregatorTest.php | 6 ++---- .../AccompanyingCourseFilters/CurrentUserJobFilterTest.php | 4 ++-- .../CurrentUserScopeFilterTest.php | 4 ++-- .../Filter/PersonFilters/AccompanyingPeriodFilterTest.php | 4 ++-- .../{BirthdayFilterTest.php => BirthdateFilterTest.php} | 2 +- 12 files changed, 23 insertions(+), 39 deletions(-) rename src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/{BirthdayFilterTest.php => BirthdateFilterTest.php} (96%) diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityReasonAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityReasonAggregatorTest.php index 436bfc697..79c0924f7 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityReasonAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityReasonAggregatorTest.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Tests\Export\Aggregator; +use Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityReasonAggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; /** @@ -21,10 +22,7 @@ use Chill\MainBundle\Test\Export\AbstractAggregatorTest; */ final class ActivityReasonAggregatorTest extends AbstractAggregatorTest { - /** - * @var \Chill\ActivityBundle\Export\Aggregator\ActivityReasonAggregator - */ - private $aggregator; + private ActivityReasonAggregator $aggregator; protected function setUp(): void { diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityTypeAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityTypeAggregatorTest.php index f6efe17a5..bd88a7d3b 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityTypeAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityTypeAggregatorTest.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Tests\Export\Aggregator; +use Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityTypeAggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; /** @@ -21,10 +22,7 @@ use Chill\MainBundle\Test\Export\AbstractAggregatorTest; */ final class ActivityTypeAggregatorTest extends AbstractAggregatorTest { - /** - * @var \Chill\ActivityBundle\Export\Aggregator\ActivityReasonAggregator - */ - private $aggregator; + private ActivityTypeAggregator $aggregator; protected function setUp(): void { diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityUserAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityUserAggregatorTest.php index 1447f473b..0e620040f 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityUserAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityUserAggregatorTest.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Tests\Export\Aggregator; +use Chill\ActivityBundle\Export\Aggregator\ActivityUserAggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; /** @@ -21,10 +22,7 @@ use Chill\MainBundle\Test\Export\AbstractAggregatorTest; */ final class ActivityUserAggregatorTest extends AbstractAggregatorTest { - /** - * @var \Chill\ActivityBundle\Export\Aggregator\ActivityUserAggregator - */ - private $aggregator; + private ActivityUserAggregator $aggregator; protected function setUp(): void { diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php index 5b8ae08c3..72f052f54 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Tests\Export\Filter; +use Chill\ActivityBundle\Export\Filter\PersonFilters\ActivityReasonFilter; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Doctrine\Common\Collections\ArrayCollection; @@ -20,10 +21,7 @@ use Doctrine\Common\Collections\ArrayCollection; */ final class ActivityReasonFilterTest extends AbstractFilterTest { - /** - * @var \Chill\PersonBundle\Export\Filter\GenderFilter - */ - private $filter; + private ActivityReasonFilter $filter; protected function setUp(): void { diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonHavingActivityBetweenDateFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonHavingActivityBetweenDateFilterTest.php index 94d99c2a7..507f03323 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonHavingActivityBetweenDateFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonHavingActivityBetweenDateFilterTest.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Tests\Export\Filter; +use Chill\ActivityBundle\Export\Filter\PersonFilters\PersonHavingActivityBetweenDateFilter; use Chill\MainBundle\Test\Export\AbstractFilterTest; use DateTime; use function array_slice; @@ -21,10 +22,7 @@ use function array_slice; */ final class PersonHavingActivityBetweenDateFilterTest extends AbstractFilterTest { - /** - * @var \Chill\PersonBundle\Export\Filter\PersonHavingActivityBetweenDateFilter - */ - private $filter; + private PersonHavingActivityBetweenDateFilter $filter; protected function setUp(): void { diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/AgeAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/AgeAggregatorTest.php index a0d869462..031ded74c 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/AgeAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/AgeAggregatorTest.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\PersonAggregators; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Export\Aggregator\PersonAggregators\AgeAggregator; use DateTime; /** @@ -20,10 +21,7 @@ use DateTime; */ final class AgeAggregatorTest extends AbstractAggregatorTest { - /** - * @var \Chill\PersonBundle\Export\Aggregator\PersonAggregators\AgeAggregator - */ - private $aggregator; + private AgeAggregator $aggregator; protected function setUp(): void { diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/GenderAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/GenderAggregatorTest.php index 6389ac33d..16bca62d9 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/GenderAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/GenderAggregatorTest.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\PersonAggregators; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Export\Aggregator\PersonAggregators\GenderAggregator; /** * @internal @@ -19,10 +20,7 @@ use Chill\MainBundle\Test\Export\AbstractAggregatorTest; */ final class GenderAggregatorTest extends AbstractAggregatorTest { - /** - * @var \Chill\PersonBundle\Export\Aggregator\PersonAggregators\GenderAggregator - */ - private $aggregator; + private GenderAggregator $aggregator; protected function setUp(): void { diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/NationalityAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/NationalityAggregatorTest.php index c093d96cc..7de9587eb 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/NationalityAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/NationalityAggregatorTest.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\PersonAggregators; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Export\Aggregator\PersonAggregators\NationalityAggregator; /** * @internal @@ -19,10 +20,7 @@ use Chill\MainBundle\Test\Export\AbstractAggregatorTest; */ final class NationalityAggregatorTest extends AbstractAggregatorTest { - /** - * @var \Chill\PersonBundle\Export\Aggregator\PersonAggregators\NationalityAggregator - */ - private $aggregator; + private NationalityAggregator $aggregator; protected function setUp(): void { diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilterTest.php index 77881a218..ab2c703b6 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilterTest.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Test\Export\AbstractFilterTest; -use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\UserJobFilter; +use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CurrentUserJobFilter; use Doctrine\ORM\EntityManagerInterface; /** @@ -21,7 +21,7 @@ use Doctrine\ORM\EntityManagerInterface; */ final class CurrentUserJobFilterTest extends AbstractFilterTest { - private UserJobFilter $filter; + private CurrentUserJobFilter $filter; protected function setUp(): void { diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilterTest.php index 1ea4d40b0..d4f4712bc 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilterTest.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Test\Export\AbstractFilterTest; -use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\UserScopeFilter; +use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\CurrentUserScopeFilter; use Doctrine\ORM\EntityManagerInterface; /** @@ -21,7 +21,7 @@ use Doctrine\ORM\EntityManagerInterface; */ final class CurrentUserScopeFilterTest extends AbstractFilterTest { - private UserScopeFilter $filter; + private CurrentUserScopeFilter $filter; protected function setUp(): void { diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodFilterTest.php index fb975000a..943f20d73 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodFilterTest.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Filter\PersonFilters; use Chill\MainBundle\Test\Export\AbstractFilterTest; -use Chill\PersonBundle\Export\Filter\PersonFilters\BirthdateFilter; +use Chill\PersonBundle\Export\Filter\PersonFilters\AccompanyingPeriodFilter; use DateTime; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; @@ -22,7 +22,7 @@ use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; */ final class AccompanyingPeriodFilterTest extends AbstractFilterTest { - private BirthdateFilter $filter; + private AccompanyingPeriodFilter $filter; protected function setUp(): void { diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/BirthdayFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/BirthdateFilterTest.php similarity index 96% rename from src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/BirthdayFilterTest.php rename to src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/BirthdateFilterTest.php index 4ff4f5ce3..68664b8b7 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/BirthdayFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/BirthdateFilterTest.php @@ -19,7 +19,7 @@ use DateTime; * @internal * @coversNothing */ -final class BirthdayFilterTest extends AbstractFilterTest +final class BirthdateFilterTest extends AbstractFilterTest { private BirthdateFilter $filter; From 37dcbe92c043ac1a9ec9d404927872c1e0de8643 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Tue, 20 Sep 2022 14:46:52 +0200 Subject: [PATCH 067/120] export: fix translation --- src/Bundle/ChillPersonBundle/translations/messages.fr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index ddaf847c2..557a6a24b 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -386,7 +386,7 @@ Deathdate before: Décédé avant cette date Alive: Vivant Deceased: Décédé Filter in relation to this date: Filtrer par rapport à cette date -"Filtered by a state of %deadOrAlive% at this date %date_calc%": Filtré par personnes qui sont %deadOrAlive% à cette date %date_calc% +"Filtered by a state of %deadOrAlive%: at this date %date_calc%": Filtré par personnes qui sont %deadOrAlive% à cette date %date_calc% Filter by person's age: Filtrer les personnes par age "Filtered by person's age: between %min_age% and %max_age%": "Filtré par âge de la personne entre %min_age% et %max_age%" From 390009b39501da63ce2a7b7c2c1a96be30843b65 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Mon, 19 Sep 2022 19:32:13 +0200 Subject: [PATCH 068/120] export: create new filter tests --- .../PersonHavingActivityBetweenDateFilter.php | 5 +- .../ACPFilters/ActivityTypeFilterTest.php | 85 ++++++++++++++ .../ACPFilters/BySocialActionFilterTest.php | 82 +++++++++++++ .../ACPFilters/BySocialIssueFilterTest.php | 83 +++++++++++++ .../Filter/ACPFilters/ByUserFilterTest.php | 82 +++++++++++++ .../Filter/ACPFilters/EmergencyFilterTest.php | 67 +++++++++++ .../ACPFilters/LocationTypeFilterTest.php | 82 +++++++++++++ .../ACPFilters/SentReceivedFilterTest.php | 67 +++++++++++ .../Filter/ACPFilters/UserFilterTest.php | 81 +++++++++++++ .../Filter/ACPFilters/UserScopeFilterTest.php | 82 +++++++++++++ .../Export/Filter/ActivityDateFilterTest.php | 69 +++++++++++ .../Export/Filter/ActivityTypeFilterTest.php | 81 +++++++++++++ .../ActivityReasonFilterTest.php | 82 +++++++++++++ ...sonHavingActivityBetweenDateFilterTest.php | 83 +++++++++++++ .../EvaluationTypeFilterTest.php | 83 +++++++++++++ .../EvaluationFilters/MaxDateFilterTest.php | 70 +++++++++++ .../CompositionFilterTest.php | 82 +++++++++++++ .../AccompanyingPeriodClosingFilterTest.php | 89 ++++++++++++++ .../AccompanyingPeriodOpeningFilterTest.php | 89 ++++++++++++++ .../Filter/PersonFilters/AgeFilterTest.php | 75 ++++++++++++ .../PersonFilters/DeadOrAliveFilterTest.php | 73 ++++++++++++ .../PersonFilters/DeathdateFilterTest.php | 69 +++++++++++ .../PersonFilters/MaritalStatusFilterTest.php | 83 +++++++++++++ .../PersonFilters/NationalityFilterTest.php | 78 ++++++++++++ ...sidentialAddressAtThirdpartyFilterTest.php | 89 ++++++++++++++ .../ResidentialAddressAtUserFilterTest.php | 69 +++++++++++ .../SocialWorkFilters/ReferrerFilterTest.php | 77 ++++++++++++ .../SocialWorkTypeFilterTest.php | 111 ++++++++++++++++++ 28 files changed, 2166 insertions(+), 2 deletions(-) create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialActionFilterTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialIssueFilterTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/EmergencyFilterTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/LocationTypeFilterTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/SentReceivedFilterTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserFilterTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserScopeFilterTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityDateFilterTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityTypeFilterTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/ActivityReasonFilterTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilterTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/EvaluationTypeFilterTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/MaxDateFilterTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Filter/HouseholdFilters/CompositionFilterTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodClosingFilterTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodOpeningFilterTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AgeFilterTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeadOrAliveFilterTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeathdateFilterTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/MaritalStatusFilterTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/NationalityFilterTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilterTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtUserFilterTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ReferrerFilterTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/SocialWorkTypeFilterTest.php diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilter.php index 871271aaa..c15411b4e 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilter.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Export\Filter\PersonFilters; +use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\ActivityReason; use Chill\ActivityBundle\Repository\ActivityReasonRepository; use Chill\MainBundle\Export\ExportElementValidatedInterface; @@ -59,10 +60,10 @@ class PersonHavingActivityBetweenDateFilter implements ExportElementValidatedInt public function alterQuery(QueryBuilder $qb, $data) { - // create a query for activity + // create a subquery for activity $sqb = $qb->getEntityManager()->createQueryBuilder(); $sqb->select('person_person_having_activity.id') - ->from('ChillActivityBundle:Activity', 'activity_person_having_activity') + ->from(Activity::class, 'activity_person_having_activity') ->join('activity_person_having_activity.person', 'person_person_having_activity'); // add clause between date diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php new file mode 100644 index 000000000..1be583bb6 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php @@ -0,0 +1,85 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.person.export.filter_activitytype'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(ActivityType::class, 'at') + ->select('at') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $a) { + $data[] = [ + 'accepted_activitytypes' => $a + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join(Activity::class, 'activity', Expr\Join::WITH, 'activity.accompanyingPeriod = acp') + ->join('activity.activityType', 'acttype'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialActionFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialActionFilterTest.php new file mode 100644 index 000000000..1480e3569 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialActionFilterTest.php @@ -0,0 +1,82 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.activity.export.bysocialaction_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(SocialAction::class, 'sa') + ->select('sa') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $a) { + $data[] = [ + 'accepted_socialactions' => $a + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.socialActions', 'actsocialaction'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialIssueFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialIssueFilterTest.php new file mode 100644 index 000000000..6af3ea97c --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialIssueFilterTest.php @@ -0,0 +1,83 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.activity.export.bysocialissue_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(SocialIssue::class, 'si') + ->select('si') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $a) { + $data[] = [ + 'accepted_socialissues' => $a + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.socialIssues', 'actsocialissue') + , + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php new file mode 100644 index 000000000..93810433d --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php @@ -0,0 +1,82 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.activity.export.byuser_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(User::class, 'u') + ->select('u') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $a) { + $data[] = [ + 'accepted_users' => $a + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.users', 'actusers'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/EmergencyFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/EmergencyFilterTest.php new file mode 100644 index 000000000..d90523bed --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/EmergencyFilterTest.php @@ -0,0 +1,67 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.activity.export.emergency_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + return [ + ['accepted_emergency' => true ], + ['accepted_emergency' => false ], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/LocationTypeFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/LocationTypeFilterTest.php new file mode 100644 index 000000000..dcac884b9 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/LocationTypeFilterTest.php @@ -0,0 +1,82 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.activity.export.locationtype_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(LocationType::class, 'lt') + ->select('lt') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $a) { + $data[] = [ + 'accepted_locationtype' => $a + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.location', 'actloc'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/SentReceivedFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/SentReceivedFilterTest.php new file mode 100644 index 000000000..b40443ee4 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/SentReceivedFilterTest.php @@ -0,0 +1,67 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.activity.export.sentreceived_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + return [ + ['accepted_sentreceived' => Activity::SENTRECEIVED_SENT ], + ['accepted_sentreceived' => Activity::SENTRECEIVED_RECEIVED ] + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserFilterTest.php new file mode 100644 index 000000000..cc1a1b81a --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserFilterTest.php @@ -0,0 +1,81 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.activity.export.user_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(User::class, 'u') + ->select('u') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $a) { + $data[] = [ + 'accepted_users' => $a + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserScopeFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserScopeFilterTest.php new file mode 100644 index 000000000..31ff8ca57 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserScopeFilterTest.php @@ -0,0 +1,82 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.activity.export.userscope_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(Scope::class, 's') + ->select('s') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $a) { + $data[] = [ + 'accepted_userscope' => $a + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.user', 'actuser'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityDateFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityDateFilterTest.php new file mode 100644 index 000000000..b65d3808d --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityDateFilterTest.php @@ -0,0 +1,69 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.activity.export.date_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + return [ + [ + 'date_from' => \DateTime::createFromFormat('Y-m-d', '2020-01-01'), + 'date_to' => \DateTime::createFromFormat('Y-m-d', '2021-01-01'), + ] + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityTypeFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityTypeFilterTest.php new file mode 100644 index 000000000..12ace7c67 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityTypeFilterTest.php @@ -0,0 +1,81 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.activity.export.type_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(ActivityType::class, 'at') + ->select('at') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $a) { + $data[] = [ + 'types' => $a + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/ActivityReasonFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/ActivityReasonFilterTest.php new file mode 100644 index 000000000..e6b14b44c --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/ActivityReasonFilterTest.php @@ -0,0 +1,82 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.activity.export.reason_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(ActivityReason::class, 'ar') + ->select('ar') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $a) { + $data[] = [ + 'reasons' => $a + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.reasons', 'actreasons'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilterTest.php new file mode 100644 index 000000000..5cad68da4 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilterTest.php @@ -0,0 +1,83 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.activity.export.person_having_an_activity_between_date_filter'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(ActivityReason::class, 'ar') + ->select('ar') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $a) { + $data[] = [ + 'date_from' => \DateTime::createFromFormat('Y-m-d', '2021-07-01'), + 'date_to' => \DateTime::createFromFormat('Y-m-d', '2022-07-01'), + 'reasons' => $a + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/EvaluationTypeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/EvaluationTypeFilterTest.php new file mode 100644 index 000000000..8ba2b46c1 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/EvaluationTypeFilterTest.php @@ -0,0 +1,83 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.person.export.filter_evaluationtype'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(Evaluation::class, 'e') + ->select('e') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $r) { + $data[] = [ + 'accepted_evaluationtype' => $r + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('workeval.id') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw') + ->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/MaxDateFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/MaxDateFilterTest.php new file mode 100644 index 000000000..5c9533850 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/MaxDateFilterTest.php @@ -0,0 +1,70 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.person.export.filter_maxdate'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + return [ + ['maxdate' => false ], + ['maxdate' => true ] + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('workeval.id') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw') + ->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/HouseholdFilters/CompositionFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/HouseholdFilters/CompositionFilterTest.php new file mode 100644 index 000000000..6892c0afc --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/HouseholdFilters/CompositionFilterTest.php @@ -0,0 +1,82 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.person.export.filter_household_composition'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(HouseholdCompositionType::class, 'r') + ->select('r') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $r) { + $data[] = [ + 'accepted_composition' => $r, + 'on_date' => \DateTime::createFromFormat('Y-m-d', '2022-05-01'), + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('h.id') + ->from(Household::class, 'h'), + ]; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodClosingFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodClosingFilterTest.php new file mode 100644 index 000000000..bf975c5a1 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodClosingFilterTest.php @@ -0,0 +1,89 @@ +filter = self::$container->get('chill.person.export.filter_accompanying_period_closing'); + } catch (ServiceNotFoundException $e) { + $this->markTestSkipped('The current configuration does not use accompanying_periods'); + } + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + return [ + [ + 'date_from' => \DateTime::createFromFormat('Y-m-d', '2000-01-01'), + 'date_to' => \DateTime::createFromFormat('Y-m-d', '2010-01-01'), + ], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container + ->get('doctrine.orm.entity_manager'); + + return [ + $em->createQueryBuilder() + ->select('person.firstName') + ->from('ChillPersonBundle:Person', 'person'), + $em->createQueryBuilder() + ->select('person.firstName') + ->from('ChillPersonBundle:Person', 'person') + // add a dummy where clause + ->where('person.firstname IS NOT NULL'), + $em->createQueryBuilder() + ->select('count(IDENTITY(p))') + ->from('ChillPersonBundle:Person', 'person') + // add a dummy where clause + ->where('person.firstname IS NOT NULL'), + $em->createQueryBuilder() + ->select('count(IDENTITY(p))') + ->from('ChillPersonBundle:Person', 'person') + ->join('person.accompanyingPeriods', 'accompanying_period') + // add a dummy where clause + ->where('person.firstname IS NOT NULL'), + $em->createQueryBuilder() + ->select('activity.date AS date') + ->select('activity.attendee as attendee') + ->from('ChillActivityBundle:Activity', 'activity') + ->join('activity.person', 'person') + ->join('person.center', 'center'), + ]; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodOpeningFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodOpeningFilterTest.php new file mode 100644 index 000000000..54ed981ec --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodOpeningFilterTest.php @@ -0,0 +1,89 @@ +filter = self::$container->get('chill.person.export.filter_accompanying_period_opening'); + } catch (ServiceNotFoundException $e) { + $this->markTestSkipped('The current configuration does not use accompanying_periods'); + } + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData() + { + return [ + [ + 'date_from' => \DateTime::createFromFormat('Y-m-d', '2000-01-01'), + 'date_to' => \DateTime::createFromFormat('Y-m-d', '2010-01-01'), + ], + ]; + } + + public function getQueryBuilders() + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container + ->get('doctrine.orm.entity_manager'); + + return [ + $em->createQueryBuilder() + ->select('person.firstName') + ->from('ChillPersonBundle:Person', 'person'), + $em->createQueryBuilder() + ->select('person.firstName') + ->from('ChillPersonBundle:Person', 'person') + // add a dummy where clause + ->where('person.firstname IS NOT NULL'), + $em->createQueryBuilder() + ->select('count(IDENTITY(p))') + ->from('ChillPersonBundle:Person', 'person') + // add a dummy where clause + ->where('person.firstname IS NOT NULL'), + $em->createQueryBuilder() + ->select('count(IDENTITY(p))') + ->from('ChillPersonBundle:Person', 'person') + ->join('person.accompanyingPeriods', 'accompanying_period') + // add a dummy where clause + ->where('person.firstname IS NOT NULL'), + $em->createQueryBuilder() + ->select('activity.date AS date') + ->select('activity.attendee as attendee') + ->from('ChillActivityBundle:Activity', 'activity') + ->join('activity.person', 'person') + ->join('person.center', 'center'), + ]; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AgeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AgeFilterTest.php new file mode 100644 index 000000000..30ca43dfd --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AgeFilterTest.php @@ -0,0 +1,75 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.person.export.filter_age'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + return [ + [ + 'min_age' => '18', + 'max_age' => '60', + 'date_calc' => \DateTime::createFromFormat('Y-m-d', '2020-05-01'), + ], + [ // ça devrait faire boum ! + 'min_age' => '35', + 'max_age' => '30', + 'date_calc' => \DateTime::createFromFormat('Y-m-d', '2020-05-01'), + ], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('p.id') + ->from(Person::class, 'p'), + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeadOrAliveFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeadOrAliveFilterTest.php new file mode 100644 index 000000000..ea2fa62cb --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeadOrAliveFilterTest.php @@ -0,0 +1,73 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.person.export.filter_dead_or_alive'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + return [ + [ + 'person_state' => 'alive', + 'date_calc' => \DateTime::createFromFormat('Y-m-d', '2021-05-01'), + ], + [ + 'person_state' => 'deceased', + 'date_calc' => \DateTime::createFromFormat('Y-m-d', '2021-05-01'), + ], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('p.id') + ->from(Person::class, 'p'), + ]; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeathdateFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeathdateFilterTest.php new file mode 100644 index 000000000..cb711af43 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeathdateFilterTest.php @@ -0,0 +1,69 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.person.export.filter_deathdate'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + return [ + [ + 'date_from' => \DateTime::createFromFormat('Y-m-d', '2020-05-01'), + 'date_to' => \DateTime::createFromFormat('Y-m-d', '2022-05-01'), + ] + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('p.id') + ->from(Person::class, 'p'), + ]; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/MaritalStatusFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/MaritalStatusFilterTest.php new file mode 100644 index 000000000..8ce747f63 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/MaritalStatusFilterTest.php @@ -0,0 +1,83 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.person.export.filter_marital_status'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(MaritalStatus::class, 'm') + ->select('m') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $m) { + $data[] = [ + 'maritalStatus' => $m, + 'calc_date' => \DateTime::createFromFormat('Y-m-d', '2022-05-01'), + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('p.id') + ->from(Person::class, 'p'), + ]; + } +} + diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/NationalityFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/NationalityFilterTest.php new file mode 100644 index 000000000..4ea2c44cc --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/NationalityFilterTest.php @@ -0,0 +1,78 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.person.export.filter_nationality'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $countries = $em->getRepository(Country::class)->findAll(); + + $data = []; + + foreach ($countries as $c) { + $data[] = [ + 'nationalities' => $c + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('p.id') + ->from(Person::class, 'p'), + ]; + } + +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilterTest.php new file mode 100644 index 000000000..8bd07f75c --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilterTest.php @@ -0,0 +1,89 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.person.export.filter_residential_address_at_thirdparty'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $array = $em->createQueryBuilder() + ->from(ThirdPartyCategory::class, 'tpc') + ->select('tpc') + ->getQuery() + ->getResult(); + + $data = []; + + foreach ($array as $r) { + $data[] = [ + 'thirdparty_cat' => $r, + 'date_calc' => \DateTime::createFromFormat('Y-m-d', '2022-05-01'), + ]; + } + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('p.id') + ->from(Person::class, 'p') + ->join(ResidentialAddress::class, 'resaddr', Expr\Join::WITH, 'resaddr.person = p') + ->join('p.center', 'center') + ->join('resaddr.hostThirdParty', 'tparty') + ->join('tparty.categories', 'tpartycat') + , + ]; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtUserFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtUserFilterTest.php new file mode 100644 index 000000000..6a2cde4b0 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtUserFilterTest.php @@ -0,0 +1,69 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.person.export.filter_residential_address_at_user'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + return []; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('p.id') + ->from(Person::class, 'p') + ->join(Person\ResidentialAddress::class, 'resaddr', Expr\Join::WITH, 'resaddr.person = p') + ->join('p.center', 'center'), + ]; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ReferrerFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ReferrerFilterTest.php new file mode 100644 index 000000000..c5cc9c523 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ReferrerFilterTest.php @@ -0,0 +1,77 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.person.export.filter_treatingagent'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $em = self::$container->get(EntityManagerInterface::class); + + $users = $em->getRepository(User::class)->findAll(); + + $data = []; + + foreach ($users as $u) { + $data[] = [ + 'accepted_agents' => $u + ]; + } + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('acpw.id') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw'), + ]; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/SocialWorkTypeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/SocialWorkTypeFilterTest.php new file mode 100644 index 000000000..2e3b392ae --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/SocialWorkTypeFilterTest.php @@ -0,0 +1,111 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.person.export.filter_social_work_type'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + $action_repository = self::$container->get(SocialActionRepository::class); + $goal_repository = self::$container->get(GoalRepository::class); + $result_repository = self::$container->get(ResultRepository::class); + + $actions = []; + $goals = []; + $results = []; + + $social_actions = $action_repository->findAll(); + foreach ($social_actions as $action) { + $actions[] = $action->getId(); + $goals_by_action = $goal_repository->findBySocialActionWithDescendants($action); + foreach ($goals_by_action as $goal) { + $goals[] = $goal->getId(); + $results_by_goal = $result_repository->findByGoal($goal); + foreach ($results_by_goal as $result) { + $results[] = $result->getId(); + } + } + $results_by_action = $result_repository->findBySocialActionWithDescendants($action); + foreach ($results_by_action as $result) { + $results[] = $result->getId(); + } + } + + sort($actions); + sort($goals); + sort($results); + + $actions = array_unique($actions); + $goals = array_unique($goals); + $results = array_unique($results); + + $data = [ + [ + 'actionType' => implode(',', $actions), + 'goal' => implode(',', $goals), + 'result' => implode(',', $results), + ] + ]; + /// TODO ne fonctionne pas + var_dump($data); + + return $data; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('acpw.id') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw'), + ]; + } +} \ No newline at end of file From eb112b8a8590f6bc5bd329752977d7aa843cce67 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 21 Sep 2022 10:33:49 +0200 Subject: [PATCH 069/120] exports: remove duplicate test cfr commit 18a6a5a7ebeefc5999d16f7a3edf9bf89ba8e9e8 --- .../ACPFilters/ActivityTypeFilterTest.php | 2 +- .../config/services/export.yaml | 4 +- .../ActivityTypeFilterTest.php | 80 ------------------- 3 files changed, 3 insertions(+), 83 deletions(-) delete mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilterTest.php diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php index 1be583bb6..10f16247e 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php @@ -37,7 +37,7 @@ final class ActivityTypeFilterTest extends AbstractFilterTest $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_activitytype'); + $this->filter = self::$container->get('chill.activity.export.filter_activitytype'); } public function getFilter() diff --git a/src/Bundle/ChillActivityBundle/config/services/export.yaml b/src/Bundle/ChillActivityBundle/config/services/export.yaml index 9abf33487..bdaae8c8a 100644 --- a/src/Bundle/ChillActivityBundle/config/services/export.yaml +++ b/src/Bundle/ChillActivityBundle/config/services/export.yaml @@ -67,10 +67,10 @@ services: name: chill.export_filter alias: 'activity_person_having_ac_bw_date_filter' - chill.person.export.filter_activitytype: + chill.activity.export.filter_activitytype: class: Chill\ActivityBundle\Export\Filter\ACPFilters\ActivityTypeFilter tags: - - { name: chill.export_filter, alias: accompanyingcourse_activitytype_filter } + - { name: chill.export_filter, alias: 'accompanyingcourse_activitytype_filter' } chill.activity.export.locationtype_filter: class: Chill\ActivityBundle\Export\Filter\ACPFilters\LocationTypeFilter diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilterTest.php deleted file mode 100644 index bce092b6e..000000000 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilterTest.php +++ /dev/null @@ -1,80 +0,0 @@ -prophesize(); - - $request->willExtend(Request::class); - $request->getLocale()->willReturn('fr'); - - $this->filter = self::$container->get('chill.person.export.filter_activitytype'); - } - - public function getFilter() - { - return $this->filter; - } - - public function getFormData(): array - { - $em = self::$container->get(EntityManagerInterface::class); - - $array = $em->createQueryBuilder() - ->from(ActivityType::class, 'at') - ->select('at') - ->getQuery() - ->getResult(); - - $data = []; - - foreach ($array as $t) { - $data[] = ['accepted_activitytypes' => $t]; - } - - return $data; - } - - public function getQueryBuilders(): array - { - if (null === self::$kernel) { - self::bootKernel(); - } - - $em = self::$container->get(EntityManagerInterface::class); - - return [ - $em->createQueryBuilder() - ->from('ChillPersonBundle:AccompanyingPeriod', 'acp') - ->select('acp.id'), - ]; - } -} From 5b3cd9eb2037a26e055020eb0573440219be45c2 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 21 Sep 2022 10:50:12 +0200 Subject: [PATCH 070/120] report export test --- .../Export/Filter/ReportDateFilterTest.php | 69 +++++++++++++++++++ .../config/services/export.yaml | 5 +- 2 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 src/Bundle/ChillReportBundle/Tests/Export/Filter/ReportDateFilterTest.php diff --git a/src/Bundle/ChillReportBundle/Tests/Export/Filter/ReportDateFilterTest.php b/src/Bundle/ChillReportBundle/Tests/Export/Filter/ReportDateFilterTest.php new file mode 100644 index 000000000..27c107f9d --- /dev/null +++ b/src/Bundle/ChillReportBundle/Tests/Export/Filter/ReportDateFilterTest.php @@ -0,0 +1,69 @@ +prophesize(); + + $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); + + $this->filter = self::$container->get('chill.report.export.filter_date'); + } + + public function getFilter() + { + return $this->filter; + } + + public function getFormData(): array + { + return [ + [ + 'date_from' => \DateTime::createFromFormat('Y-m-d', '2021-07-01'), + 'date_to' => \DateTime::createFromFormat('Y-m-d', '2022-07-01'), + ] + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('r.id') + ->from(Report::class, 'r') + ]; + } +} diff --git a/src/Bundle/ChillReportBundle/config/services/export.yaml b/src/Bundle/ChillReportBundle/config/services/export.yaml index 1ae624118..9650b9e33 100644 --- a/src/Bundle/ChillReportBundle/config/services/export.yaml +++ b/src/Bundle/ChillReportBundle/config/services/export.yaml @@ -8,6 +8,7 @@ services: tags: - { name: chill.export_elements_provider, prefix: 'report' } - Chill\ReportBundle\Export\Filter\ReportDateFilter: + chill.report.export.filter_date: + class: Chill\ReportBundle\Export\Filter\ReportDateFilter tags: - - { name: chill.export_filter, alias: 'report_date' } + - { name: chill.export_filter, alias: 'report_date_filter' } From c059b7700e63e9612ad6aad7a2b11d4e20ac07b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 21 Sep 2022 11:10:35 +0200 Subject: [PATCH 071/120] Improve label for aliases in "Filter by Activity" and use of new-style EntityRepository for ActivityType * [activity][export] Feature: improve label for aliases in "Filter by activity type" * [activity][export] DX/Feature: use of an `ActivityTypeRepositoryInterface` instead of the old-style EntityRepository --- CHANGELOG.md | 2 + .../Controller/ActivityController.php | 6 +- .../Entity/ActivityType.php | 5 ++ .../Aggregator/ActivityTypeAggregator.php | 6 +- .../Filter/ACPFilters/ActivityTypeFilter.php | 42 ++++++-------- .../Export/Filter/ActivityTypeFilter.php | 34 ++++++----- .../Form/Type/TranslatableActivityType.php | 20 ++----- .../Repository/ActivityTypeRepository.php | 58 ++++++++++++++++--- .../ActivityTypeRepositoryInterface.php | 15 +++++ 9 files changed, 121 insertions(+), 67 deletions(-) create mode 100644 src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepositoryInterface.php diff --git a/CHANGELOG.md b/CHANGELOG.md index f4e0ab7f0..1a1fd7210 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to * [person][export] Fixed: rename the alias for `accompanying_period` to `acp` in filter associated with person +* [activity][export] Feature: improve label for aliases in "Filter by activity type" +* [activity][export] DX/Feature: use of an `ActivityTypeRepositoryInterface` instead of the old-style EntityRepository ## Test releases diff --git a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php index 2c380ad52..fa6cde7c1 100644 --- a/src/Bundle/ChillActivityBundle/Controller/ActivityController.php +++ b/src/Bundle/ChillActivityBundle/Controller/ActivityController.php @@ -17,7 +17,7 @@ use Chill\ActivityBundle\Form\ActivityType; use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface; use Chill\ActivityBundle\Repository\ActivityRepository; use Chill\ActivityBundle\Repository\ActivityTypeCategoryRepository; -use Chill\ActivityBundle\Repository\ActivityTypeRepository; +use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface; use Chill\ActivityBundle\Security\Authorization\ActivityVoter; use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable; use Chill\MainBundle\Repository\LocationRepository; @@ -55,7 +55,7 @@ final class ActivityController extends AbstractController private ActivityTypeCategoryRepository $activityTypeCategoryRepository; - private ActivityTypeRepository $activityTypeRepository; + private ActivityTypeRepositoryInterface $activityTypeRepository; private CenterResolverManagerInterface $centerResolver; @@ -75,7 +75,7 @@ final class ActivityController extends AbstractController public function __construct( ActivityACLAwareRepositoryInterface $activityACLAwareRepository, - ActivityTypeRepository $activityTypeRepository, + ActivityTypeRepositoryInterface $activityTypeRepository, ActivityTypeCategoryRepository $activityTypeCategoryRepository, PersonRepository $personRepository, ThirdPartyRepository $thirdPartyRepository, diff --git a/src/Bundle/ChillActivityBundle/Entity/ActivityType.php b/src/Bundle/ChillActivityBundle/Entity/ActivityType.php index 845b31ca2..a2b15ee23 100644 --- a/src/Bundle/ChillActivityBundle/Entity/ActivityType.php +++ b/src/Bundle/ChillActivityBundle/Entity/ActivityType.php @@ -516,6 +516,11 @@ class ActivityType return $this->userVisible; } + public function hasCategory(): bool + { + return null !== $this->getCategory(); + } + /** * Is active * return true if the type is active. diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php index 5c285ca3e..cada50387 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Export\Aggregator; use Chill\ActivityBundle\Export\Declarations; -use Chill\ActivityBundle\Repository\ActivityTypeRepository; +use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface; use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Closure; @@ -25,12 +25,12 @@ class ActivityTypeAggregator implements AggregatorInterface { public const KEY = 'activity_type_aggregator'; - protected ActivityTypeRepository $activityTypeRepository; + protected ActivityTypeRepositoryInterface $activityTypeRepository; protected TranslatableStringHelperInterface $translatableStringHelper; public function __construct( - ActivityTypeRepository $activityTypeRepository, + ActivityTypeRepositoryInterface $activityTypeRepository, TranslatableStringHelperInterface $translatableStringHelper ) { $this->activityTypeRepository = $activityTypeRepository; diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ActivityTypeFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ActivityTypeFilter.php index 7c0b202bd..d17a6af99 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ActivityTypeFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ActivityTypeFilter.php @@ -13,8 +13,9 @@ namespace Chill\ActivityBundle\Export\Filter\ACPFilters; use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\ActivityType; +use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface; use Chill\MainBundle\Export\FilterInterface; -use Chill\MainBundle\Templating\TranslatableStringHelper; +use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr\Andx; @@ -22,15 +23,17 @@ use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; -/** - * TODO merge with ActivityTypeFilter in ChillActivity (!?). - */ class ActivityTypeFilter implements FilterInterface { - private TranslatableStringHelper $translatableStringHelper; + private ActivityTypeRepositoryInterface $activityTypeRepository; - public function __construct(TranslatableStringHelper $translatableStringHelper) - { + private TranslatableStringHelperInterface $translatableStringHelper; + + public function __construct( + ActivityTypeRepositoryInterface $activityTypeRepository, + TranslatableStringHelperInterface $translatableStringHelper + ) { + $this->activityTypeRepository = $activityTypeRepository; $this->translatableStringHelper = $translatableStringHelper; } @@ -45,21 +48,10 @@ class ActivityTypeFilter implements FilterInterface $qb->join(Activity::class, 'activity', Expr\Join::WITH, 'activity.accompanyingPeriod = acp'); } - if (!in_array('acttype', $qb->getAllAliases(), true)) { - $qb->join('activity.activityType', 'acttype'); - } + $clause = $qb->expr()->in('activity.activityType', ':selected_activity_types'); - $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->in('acttype.id', ':activitytypes'); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); - $qb->setParameter('activitytypes', $data['accepted_activitytypes']); + $qb->andWhere($clause); + $qb->setParameter('selected_activity_types', $data['types']); } public function applyOn() @@ -71,8 +63,12 @@ class ActivityTypeFilter implements FilterInterface { $builder->add('accepted_activitytypes', EntityType::class, [ 'class' => ActivityType::class, + 'choices' => $this->activityTypeRepository->findAllActive(), 'choice_label' => function (ActivityType $aty) { - return $this->translatableStringHelper->localize($aty->getName()); + return + ($aty->hasCategory() ? $this->translatableStringHelper->localize($aty->getCategory()->getName()) . ' > ' : '') + . + $this->translatableStringHelper->localize($aty->getName()); }, 'multiple' => true, 'expanded' => true, @@ -88,7 +84,7 @@ class ActivityTypeFilter implements FilterInterface } return ['Filtered by activity types: only %activitytypes%', [ - '%activitytypes%' => implode(', ou ', $types), + '%activitytypes%' => implode(', ', $types), ]]; } diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ActivityTypeFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ActivityTypeFilter.php index 77d887fdf..7442fabb7 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ActivityTypeFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ActivityTypeFilter.php @@ -13,7 +13,7 @@ namespace Chill\ActivityBundle\Export\Filter; use Chill\ActivityBundle\Entity\ActivityType; use Chill\ActivityBundle\Export\Declarations; -use Chill\ActivityBundle\Repository\ActivityTypeRepository; +use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface; use Chill\MainBundle\Export\ExportElementValidatedInterface; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; @@ -28,13 +28,13 @@ use function count; class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInterface { - protected ActivityTypeRepository $activityTypeRepository; + protected ActivityTypeRepositoryInterface $activityTypeRepository; protected TranslatableStringHelperInterface $translatableStringHelper; public function __construct( TranslatableStringHelperInterface $translatableStringHelper, - ActivityTypeRepository $activityTypeRepository + ActivityTypeRepositoryInterface $activityTypeRepository ) { $this->translatableStringHelper = $translatableStringHelper; $this->activityTypeRepository = $activityTypeRepository; @@ -47,16 +47,9 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter public function alterQuery(QueryBuilder $qb, $data) { - $where = $qb->getDQLPart('where'); $clause = $qb->expr()->in('activity.activityType', ':selected_activity_types'); - if ($where instanceof Expr\Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); + $qb->andWhere($clause); $qb->setParameter('selected_activity_types', $data['types']); } @@ -68,11 +61,26 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter public function buildForm(FormBuilderInterface $builder) { $builder->add('types', EntityType::class, [ + 'choices' => $this->activityTypeRepository->findAllActive(), 'class' => ActivityType::class, - 'choice_label' => fn (ActivityType $type) => $this->translatableStringHelper->localize($type->getName()), - 'group_by' => fn (ActivityType $type) => $this->translatableStringHelper->localize($type->getCategory()->getName()), + 'choice_label' => function (ActivityType $aty) { + return + ($aty->hasCategory() ? $this->translatableStringHelper->localize($aty->getCategory()->getName()) . ' > ' : '') + . + $this->translatableStringHelper->localize($aty->getName()); + }, + 'group_by' => function (ActivityType $type) { + if (!$type->hasCategory()) { + return null; + } + + return $this->translatableStringHelper->localize($type->getCategory()->getName()); + }, 'multiple' => true, 'expanded' => false, + 'attr' => [ + 'class' => 'select2' + ] ]); } diff --git a/src/Bundle/ChillActivityBundle/Form/Type/TranslatableActivityType.php b/src/Bundle/ChillActivityBundle/Form/Type/TranslatableActivityType.php index dc6328709..8140043a4 100644 --- a/src/Bundle/ChillActivityBundle/Form/Type/TranslatableActivityType.php +++ b/src/Bundle/ChillActivityBundle/Form/Type/TranslatableActivityType.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Form\Type; use Chill\ActivityBundle\Entity\ActivityType; -use Chill\ActivityBundle\Repository\ActivityTypeRepository; +use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\QueryBuilder; @@ -23,37 +23,25 @@ use Symfony\Component\OptionsResolver\OptionsResolver; class TranslatableActivityType extends AbstractType { - protected ActivityTypeRepository $activityTypeRepository; + protected ActivityTypeRepositoryInterface $activityTypeRepository; protected TranslatableStringHelperInterface $translatableStringHelper; public function __construct( TranslatableStringHelperInterface $helper, - ActivityTypeRepository $activityTypeRepository + ActivityTypeRepositoryInterface $activityTypeRepository ) { $this->translatableStringHelper = $helper; $this->activityTypeRepository = $activityTypeRepository; } - public function buildForm(FormBuilderInterface $builder, array $options) - { - /** @var QueryBuilder $qb */ - $qb = $options['query_builder']; - - if (true === $options['active_only']) { - $qb->where($qb->expr()->eq('at.active', ':active')); - $qb->setParameter('active', true, Types::BOOLEAN); - } - } - public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults( [ 'class' => ActivityType::class, 'active_only' => true, - 'query_builder' => $this->activityTypeRepository - ->createQueryBuilder('at'), + 'choices' => $this->activityTypeRepository->findAllActive(), 'choice_label' => function (ActivityType $type) { return $this->translatableStringHelper->localize($type->getName()); }, diff --git a/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepository.php b/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepository.php index 735b70054..aee3706cf 100644 --- a/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepository.php +++ b/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepository.php @@ -13,18 +13,58 @@ namespace Chill\ActivityBundle\Repository; use Chill\ActivityBundle\Entity\ActivityType; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\EntityRepository; use Doctrine\Persistence\ManagerRegistry; +use UnexpectedValueException; -/** - * @method ActivityType|null find($id, $lockMode = null, $lockVersion = null) - * @method ActivityType|null findOneBy(array $criteria, array $orderBy = null) - * @method ActivityType[] findAll() - * @method ActivityType[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) - */ -class ActivityTypeRepository extends ServiceEntityRepository +final class ActivityTypeRepository implements ActivityTypeRepositoryInterface { - public function __construct(ManagerRegistry $registry) + private EntityRepository $repository; + + public function __construct(EntityManagerInterface $em) { - parent::__construct($registry, ActivityType::class); + $this->repository = $em->getRepository(ActivityType::class); } + + /** + * @return array|ActivityType[] + */ + public function findAllActive(): array + { + return $this->findBy(['active' => true]); + } + + public function find($id): ?ActivityType + { + return $this->repository->find($id); + } + + /** + * @return array|ActivityType[] + */ + public function findAll(): array + { + return $this->repository->findAll(); + } + + /** + * @return array|ActivityType[] + */ + 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): ?ActivityType + { + return $this->repository->findOneBy($criteria); + } + + public function getClassName(): string + { + return ActivityType::class; + } + + } diff --git a/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepositoryInterface.php b/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepositoryInterface.php new file mode 100644 index 000000000..533798e5e --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepositoryInterface.php @@ -0,0 +1,15 @@ + Date: Wed, 21 Sep 2022 11:29:08 +0200 Subject: [PATCH 072/120] exports: create new filter tests --- src/Bundle/ChillMainBundle/Test/Export/AbstractFilterTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Bundle/ChillMainBundle/Test/Export/AbstractFilterTest.php b/src/Bundle/ChillMainBundle/Test/Export/AbstractFilterTest.php index bef4fd200..8e9761c67 100644 --- a/src/Bundle/ChillMainBundle/Test/Export/AbstractFilterTest.php +++ b/src/Bundle/ChillMainBundle/Test/Export/AbstractFilterTest.php @@ -24,7 +24,6 @@ use function is_string; /** * Helper to test filters. * - * @internal */ abstract class AbstractFilterTest extends KernelTestCase { From 7ef84b9fd07ed0a78e12c14a608d20fff6fcc57e Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 21 Sep 2022 13:27:26 +0200 Subject: [PATCH 073/120] exports: create new aggregator test (untested) --- .../GeographicalUnitStatAggregatorTest.php | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregatorTest.php diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregatorTest.php new file mode 100644 index 000000000..84204e629 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregatorTest.php @@ -0,0 +1,57 @@ +aggregator = self::$container->get('chill.person.export.aggregator_geographicalunitstat'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + , + ]; + } +} From 3c8dbe56fc918126c58ce39277b05e480ddd4b82 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 21 Sep 2022 12:20:34 +0200 Subject: [PATCH 074/120] exports: create new aggregator tests + minor corrections in aggregators --- .../ACPAggregators/DateAggregator.php | 9 +-- .../ActivityReasonAggregator.php | 4 +- .../BySocialActionAggregatorTest.php | 59 +++++++++++++++++ .../BySocialIssueAggregatorTest.php | 59 +++++++++++++++++ .../ByThirdpartyAggregatorTest.php | 59 +++++++++++++++++ .../ACPAggregators/ByUserAggregatorTest.php | 59 +++++++++++++++++ .../ACPAggregators/DateAggregatorTest.php | 66 +++++++++++++++++++ .../LocationTypeAggregatorTest.php | 59 +++++++++++++++++ .../UserScopeAggregatorTest.php | 59 +++++++++++++++++ .../ActivityReasonAggregatorTest.php | 65 ++++++++++++++++++ .../Test/Export/AbstractAggregatorTest.php | 1 - .../EvaluationTypeAggregator.php | 2 +- .../MaritalStatusAggregator.php | 1 + .../AdministrativeLocationAggregatorTest.php | 58 ++++++++++++++++ .../ClosingMotiveAggregatorTest.php | 58 ++++++++++++++++ .../ConfidentialAggregatorTest.php | 57 ++++++++++++++++ .../DurationAggregatorTest.php | 57 ++++++++++++++++ .../EmergencyAggregatorTest.php | 57 ++++++++++++++++ .../EvaluationAggregatorTest.php | 59 +++++++++++++++++ .../IntensityAggregatorTest.php | 57 ++++++++++++++++ .../JobAggregatorTest.php | 58 ++++++++++++++++ .../OriginAggregatorTest.php | 58 ++++++++++++++++ .../ReferrerAggregatorTest.php | 58 ++++++++++++++++ .../RequestorAggregatorTest.php | 58 ++++++++++++++++ .../ScopeAggregatorTest.php | 58 ++++++++++++++++ .../SocialActionAggregatorTest.php | 58 ++++++++++++++++ .../SocialIssueAggregatorTest.php | 58 ++++++++++++++++ .../StepAggregatorTest.php | 59 +++++++++++++++++ .../EvaluationTypeAggregatorTest.php | 59 +++++++++++++++++ .../ChildrenNumberAggregatorTest.php | 64 ++++++++++++++++++ .../CompositionAggregatorTest.php | 64 ++++++++++++++++++ .../CountryOfBirthAggregatorTest.php | 59 +++++++++++++++++ .../HouseholdPositionAggregatorTest.php | 63 ++++++++++++++++++ .../MaritalStatusAggregatorTest.php | 58 ++++++++++++++++ .../ActionTypeAggregatorTest.php | 8 ++- .../GoalAggregatorTest.php | 8 ++- .../GoalResultAggregatorTest.php | 60 +++++++++++++++++ .../JobAggregatorTest.php | 59 +++++++++++++++++ .../ReferrerAggregatorTest.php | 8 ++- .../ResultAggregatorTest.php | 8 ++- .../ScopeAggregatorTest.php | 59 +++++++++++++++++ 41 files changed, 1925 insertions(+), 22 deletions(-) create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialActionAggregatorTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialIssueAggregatorTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByThirdpartyAggregatorTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/DateAggregatorTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/LocationTypeAggregatorTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/PersonAggregators/ActivityReasonAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/JobAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/RequestorAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/StepAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/CompositionAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/CountryOfBirthAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/HouseholdPositionAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/MaritalStatusAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalResultAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/JobAggregatorTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ScopeAggregatorTest.php diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php index 5603b1b78..5147936c9 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php @@ -50,18 +50,15 @@ class DateAggregator implements AggregatorInterface switch ($data['frequency']) { case 'month': $fmt = 'MM'; - -break; + break; case 'week': $fmt = 'IW'; - -break; + break; case 'year': $fmt = 'YYYY'; $order = 'DESC'; - -break; // order DESC does not works ! + break; // order DESC does not works ! default: throw new RuntimeException(sprintf("The frequency data '%s' is invalid.", $data['frequency'])); diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/PersonAggregators/ActivityReasonAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/PersonAggregators/ActivityReasonAggregator.php index 9d7cd3116..f82793e71 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/PersonAggregators/ActivityReasonAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/PersonAggregators/ActivityReasonAggregator.php @@ -55,10 +55,10 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali { // add select element if ('reasons' === $data['level']) { - $elem = 'reasons.id'; + $elem = 'actreasons.id'; $alias = 'activity_reasons_id'; } elseif ('categories' === $data['level']) { - $elem = 'category.id'; + $elem = 'actreasoncat.id'; $alias = 'activity_categories_id'; } else { throw new RuntimeException('The data provided are not recognized.'); diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialActionAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialActionAggregatorTest.php new file mode 100644 index 000000000..b4da861e8 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialActionAggregatorTest.php @@ -0,0 +1,59 @@ +aggregator = self::$container->get('chill.activity.export.bysocialaction_aggregator'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.accompanyingPeriod', 'actacp') + ->join('activity.socialActions', 'actsocialaction') + , + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialIssueAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialIssueAggregatorTest.php new file mode 100644 index 000000000..ab447739e --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialIssueAggregatorTest.php @@ -0,0 +1,59 @@ +aggregator = self::$container->get('chill.activity.export.bysocialissue_aggregator'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.accompanyingPeriod', 'actacp') + ->join('activity.socialIssues', 'actsocialissue') + , + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByThirdpartyAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByThirdpartyAggregatorTest.php new file mode 100644 index 000000000..e65ffec62 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByThirdpartyAggregatorTest.php @@ -0,0 +1,59 @@ +aggregator = self::$container->get('chill.activity.export.bythirdparty_aggregator'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.accompanyingPeriod', 'actacp') + ->join('activity.thirdParties', 'acttparty') + , + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php new file mode 100644 index 000000000..da1f0ecac --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php @@ -0,0 +1,59 @@ +aggregator = self::$container->get('chill.activity.export.byuser_aggregator'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.accompanyingPeriod', 'actacp') + ->join('activity.users', 'actusers') + , + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/DateAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/DateAggregatorTest.php new file mode 100644 index 000000000..f8775b8bf --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/DateAggregatorTest.php @@ -0,0 +1,66 @@ +aggregator = self::$container->get('chill.activity.export.date_aggregator'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [ + 'frequency' => 'month', + ], + [ + 'frequency' => 'week', + ], + [ + 'frequency' => 'year', + ] + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.accompanyingPeriod', 'actacp') + , + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/LocationTypeAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/LocationTypeAggregatorTest.php new file mode 100644 index 000000000..8f8b866bc --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/LocationTypeAggregatorTest.php @@ -0,0 +1,59 @@ +aggregator = self::$container->get('chill.activity.export.locationtype_aggregator'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.accompanyingPeriod', 'actacp') + ->join('activity.location', 'actloc') + , + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php new file mode 100644 index 000000000..3434f1a1c --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php @@ -0,0 +1,59 @@ +aggregator = self::$container->get('chill.activity.export.userscope_aggregator'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.accompanyingPeriod', 'actacp') + ->join('activity.user', 'actuser') + , + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/PersonAggregators/ActivityReasonAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/PersonAggregators/ActivityReasonAggregatorTest.php new file mode 100644 index 000000000..6de364ae7 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/PersonAggregators/ActivityReasonAggregatorTest.php @@ -0,0 +1,65 @@ +aggregator = self::$container->get('chill.activity.export.reason_aggregator'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [ + 'level' => 'reasons', + ], + [ + 'level' => 'categories', + ] + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.person', 'actperson') + ->innerJoin('activity.reasons', 'actreasons') + ->join('actreasons.category', 'actreasoncat') + , + ]; + } +} diff --git a/src/Bundle/ChillMainBundle/Test/Export/AbstractAggregatorTest.php b/src/Bundle/ChillMainBundle/Test/Export/AbstractAggregatorTest.php index cf5fe5c31..40792a1c6 100644 --- a/src/Bundle/ChillMainBundle/Test/Export/AbstractAggregatorTest.php +++ b/src/Bundle/ChillMainBundle/Test/Export/AbstractAggregatorTest.php @@ -25,7 +25,6 @@ use function is_string; /** * Helper which creates a set of test for aggregators. * - * @internal */ abstract class AbstractAggregatorTest extends KernelTestCase { diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregator.php index b4f5e0071..bf0f48ab4 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregator.php @@ -39,7 +39,7 @@ class EvaluationTypeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('IDENTITY(eval.evaluation) AS evaluationtype_aggregator'); + $qb->addSelect('IDENTITY(workeval.evaluation) AS evaluationtype_aggregator'); $groupBy = $qb->getDQLPart('groupBy'); diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/MaritalStatusAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/MaritalStatusAggregator.php index e799d8dfa..2ed1752b0 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/MaritalStatusAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/MaritalStatusAggregator.php @@ -59,6 +59,7 @@ final class MaritalStatusAggregator implements AggregatorInterface public function buildForm(FormBuilderInterface $builder) { + // no form } public function getLabels($key, array $values, $data) diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregatorTest.php new file mode 100644 index 000000000..82a879b19 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregatorTest.php @@ -0,0 +1,58 @@ +aggregator = self::$container->get('chill.person.export.aggregator_administrative_location'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.administrativeLocation', 'acploc') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregatorTest.php new file mode 100644 index 000000000..279b621d4 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregatorTest.php @@ -0,0 +1,58 @@ +aggregator = self::$container->get('chill.person.export.aggregator_closingmotive'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.closingMotive', 'acpmotive') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregatorTest.php new file mode 100644 index 000000000..ab0e3dae3 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregatorTest.php @@ -0,0 +1,57 @@ +aggregator = self::$container->get('chill.person.export.aggregator_confidential'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregatorTest.php new file mode 100644 index 000000000..056c396e0 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregatorTest.php @@ -0,0 +1,57 @@ +aggregator = self::$container->get('chill.person.export.aggregator_duration'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregatorTest.php new file mode 100644 index 000000000..0b8ff2b12 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregatorTest.php @@ -0,0 +1,57 @@ +aggregator = self::$container->get('chill.person.export.aggregator_emergency'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregatorTest.php new file mode 100644 index 000000000..8c67eb649 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregatorTest.php @@ -0,0 +1,59 @@ +aggregator = self::$container->get('chill.person.export.aggregator_evaluation'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw') + ->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregatorTest.php new file mode 100644 index 000000000..b3da8ea13 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregatorTest.php @@ -0,0 +1,57 @@ +aggregator = self::$container->get('chill.person.export.aggregator_intensity'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/JobAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/JobAggregatorTest.php new file mode 100644 index 000000000..8e14ffe4c --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/JobAggregatorTest.php @@ -0,0 +1,58 @@ +aggregator = self::$container->get('chill.person.export.aggregator_referrer_job'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.job', 'acpjob') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregatorTest.php new file mode 100644 index 000000000..cdd709226 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregatorTest.php @@ -0,0 +1,58 @@ +aggregator = self::$container->get('chill.person.export.aggregator_origin'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.origin', 'acporigin') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregatorTest.php new file mode 100644 index 000000000..789baa228 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregatorTest.php @@ -0,0 +1,58 @@ +aggregator = self::$container->get('chill.person.export.aggregator_referrer'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.user', 'acpuser') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/RequestorAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/RequestorAggregatorTest.php new file mode 100644 index 000000000..f888e2535 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/RequestorAggregatorTest.php @@ -0,0 +1,58 @@ +aggregator = self::$container->get('chill.person.export.aggregator_requestor'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.participations', 'acppart') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregatorTest.php new file mode 100644 index 000000000..05fe5e980 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregatorTest.php @@ -0,0 +1,58 @@ +aggregator = self::$container->get('chill.person.export.aggregator_referrer_scope'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.scopes', 'acpscope') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregatorTest.php new file mode 100644 index 000000000..cdf27c1a5 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregatorTest.php @@ -0,0 +1,58 @@ +aggregator = self::$container->get('chill.person.export.aggregator_socialaction'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregatorTest.php new file mode 100644 index 000000000..912a2b7c6 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregatorTest.php @@ -0,0 +1,58 @@ +aggregator = self::$container->get('chill.person.export.aggregator_socialissue'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.socialIssues', 'acpsocialissue') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/StepAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/StepAggregatorTest.php new file mode 100644 index 000000000..ee11eeee4 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/StepAggregatorTest.php @@ -0,0 +1,59 @@ +aggregator = self::$container->get('chill.person.export.aggregator_step'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [ + 'on_date' => \DateTime::createFromFormat('Y-m-d', '2022-01-01'), + ], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregatorTest.php new file mode 100644 index 000000000..60b8d838c --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregatorTest.php @@ -0,0 +1,59 @@ +aggregator = self::$container->get('chill.person.export.aggregator_evaluationtype'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw') + ->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregatorTest.php new file mode 100644 index 000000000..bef052329 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregatorTest.php @@ -0,0 +1,64 @@ +aggregator = self::$container->get('chill.person.export.aggregator_household_childrennumber'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [ + 'on_date' => \DateTime::createFromFormat('Y-m-d', '2022-07-01') + ], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.participations', 'acppart') + ->join('acppart.person', 'partperson') + ->join('partperson.householdParticipations', 'member') + ->join('member.household', 'household') + ->join('household.compositions', 'composition') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/CompositionAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/CompositionAggregatorTest.php new file mode 100644 index 000000000..fc808c03d --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/CompositionAggregatorTest.php @@ -0,0 +1,64 @@ +aggregator = self::$container->get('chill.person.export.aggregator_household_composition'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [ + 'on_date' => \DateTime::createFromFormat('Y-m-d', '2022-07-01') + ], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.participations', 'acppart') + ->join('acppart.person', 'partperson') + ->join('partperson.householdParticipations', 'member') + ->join('member.household', 'household') + ->join('household.compositions', 'composition') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/CountryOfBirthAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/CountryOfBirthAggregatorTest.php new file mode 100644 index 000000000..90596d892 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/CountryOfBirthAggregatorTest.php @@ -0,0 +1,59 @@ +aggregator = self::$container->get('chill.person.export.aggregator_country_of_birth'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + ['group_by_level' => 'continent'], + ['group_by_level' => 'country',], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(person.id)') + ->from(Person::class, 'person') + ->leftJoin('person.countryOfBirth', 'countryOfBirth') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/HouseholdPositionAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/HouseholdPositionAggregatorTest.php new file mode 100644 index 000000000..91a22d05a --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/HouseholdPositionAggregatorTest.php @@ -0,0 +1,63 @@ +aggregator = self::$container->get('chill.person.export.aggregator_household_position'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [ + 'date_position' => \DateTime::createFromFormat('Y-m-d', '2022-07-01') + ], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(person.id)') + ->from(Person::class, 'person') + ->join(HouseholdMember::class, 'householdmember', Expr\Join::WITH, 'householdmember.person = person') + ->join('person.center', 'center') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/MaritalStatusAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/MaritalStatusAggregatorTest.php new file mode 100644 index 000000000..25ddb602d --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/MaritalStatusAggregatorTest.php @@ -0,0 +1,58 @@ +aggregator = self::$container->get('chill.person.export.aggregator_marital_status'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(person.id)') + ->from(Person::class, 'person') + ->join('person.maritalStatus', 'personmarital') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregatorTest.php index ab0492d11..ed83c3af7 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregatorTest.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\SocialWorkAggregators; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\ActionTypeAggregator; /** @@ -34,14 +35,14 @@ final class ActionTypeAggregatorTest extends AbstractAggregatorTest return $this->aggregator; } - public function getFormData() + public function getFormData(): array { return [ [], ]; } - public function getQueryBuilders() + public function getQueryBuilders(): array { if (null === self::$kernel) { self::bootKernel(); @@ -53,7 +54,8 @@ final class ActionTypeAggregatorTest extends AbstractAggregatorTest return [ $em->createQueryBuilder() ->select('count(acpw.id)') - ->from('ChillPersonBundle:AccompanyingPeriodWork', 'acpw'), + ->from(AccompanyingPeriodWork::class, 'acpw') + , ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalAggregatorTest.php index ef5bd5097..979f3c488 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalAggregatorTest.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\SocialWorkAggregators; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\GoalAggregator; /** @@ -34,14 +35,14 @@ final class GoalAggregatorTest extends AbstractAggregatorTest return $this->aggregator; } - public function getFormData() + public function getFormData(): array { return [ [], ]; } - public function getQueryBuilders() + public function getQueryBuilders(): array { if (null === self::$kernel) { self::bootKernel(); @@ -53,7 +54,8 @@ final class GoalAggregatorTest extends AbstractAggregatorTest return [ $em->createQueryBuilder() ->select('count(acpw.id)') - ->from('ChillPersonBundle:AccompanyingPeriodWork', 'acpw'), + ->from(AccompanyingPeriodWork::class, 'acpw') + , ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalResultAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalResultAggregatorTest.php new file mode 100644 index 000000000..37642fa33 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalResultAggregatorTest.php @@ -0,0 +1,60 @@ +aggregator = self::$container->get('chill.person.export.aggregator_goalresult'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw') + ->join('acpw.goals', 'goal') + ->join('goal.results', 'goalresult') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/JobAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/JobAggregatorTest.php new file mode 100644 index 000000000..c686e66b6 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/JobAggregatorTest.php @@ -0,0 +1,59 @@ +aggregator = self::$container->get('chill.person.export.aggregator_treatingagent_job'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw') + ->join('acpw.referrers', 'acpwuser') + , + ]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ReferrerAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ReferrerAggregatorTest.php index 446cf1e37..b8070398a 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ReferrerAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ReferrerAggregatorTest.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\SocialWorkAggregators; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\ReferrerAggregator; /** @@ -34,14 +35,14 @@ final class ReferrerAggregatorTest extends AbstractAggregatorTest return $this->aggregator; } - public function getFormData() + public function getFormData(): array { return [ [], ]; } - public function getQueryBuilders() + public function getQueryBuilders(): array { if (null === self::$kernel) { self::bootKernel(); @@ -53,7 +54,8 @@ final class ReferrerAggregatorTest extends AbstractAggregatorTest return [ $em->createQueryBuilder() ->select('count(acpw.id)') - ->from('ChillPersonBundle:AccompanyingPeriodWork', 'acpw'), + ->from(AccompanyingPeriodWork::class, 'acpw') + , ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ResultAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ResultAggregatorTest.php index a34a16a8a..984e296bc 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ResultAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ResultAggregatorTest.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\SocialWorkAggregators; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\ResultAggregator; /** @@ -34,14 +35,14 @@ final class ResultAggregatorTest extends AbstractAggregatorTest return $this->aggregator; } - public function getFormData() + public function getFormData(): array { return [ [], ]; } - public function getQueryBuilders() + public function getQueryBuilders(): array { if (null === self::$kernel) { self::bootKernel(); @@ -53,7 +54,8 @@ final class ResultAggregatorTest extends AbstractAggregatorTest return [ $em->createQueryBuilder() ->select('count(acpw.id)') - ->from('ChillPersonBundle:AccompanyingPeriodWork', 'acpw'), + ->from(AccompanyingPeriodWork::class, 'acpw') + , ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ScopeAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ScopeAggregatorTest.php new file mode 100644 index 000000000..3cece9ed9 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ScopeAggregatorTest.php @@ -0,0 +1,59 @@ +aggregator = self::$container->get('chill.person.export.aggregator_treatingagent_scope'); + } + + public function getAggregator() + { + return $this->aggregator; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getQueryBuilders(): array + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.works', 'acpw') + ->join('acpw.referrers', 'acpwuser') + , + ]; + } +} From 8b6493356584ccb1f30cfaceb0c26b2d7d7427c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 21 Sep 2022 12:08:00 +0200 Subject: [PATCH 075/120] [activity][export]: Fixed: rename the alias for accompanying period, to be suitable for usage with acp filters --- .../Export/Export/LinkedToACP/AvgActivityDuration.php | 2 +- .../Export/Export/LinkedToACP/AvgActivityVisitDuration.php | 2 +- .../Export/Export/LinkedToACP/CountActivity.php | 2 +- .../Export/Export/LinkedToACP/SumActivityDuration.php | 2 +- .../Export/Export/LinkedToACP/SumActivityVisitDuration.php | 2 +- .../Export/Export/LinkedToPerson/CountActivity.php | 4 ++-- .../Export/Export/LinkedToPerson/ListActivity.php | 2 +- .../Export/Export/LinkedToPerson/StatActivityDuration.php | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php index b2346b8fd..5dae4c0ef 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php @@ -84,7 +84,7 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { $qb = $this->repository->createQueryBuilder('activity') - ->join('activity.accompanyingPeriod', 'actacp'); + ->join('activity.accompanyingPeriod', 'acp'); $qb->select('AVG(activity.durationTime) as export_avg_activity_duration'); diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php index 0195992fe..560ae8754 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php @@ -84,7 +84,7 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { $qb = $this->repository->createQueryBuilder('activity') - ->join('activity.accompanyingPeriod', 'actacp'); + ->join('activity.accompanyingPeriod', 'acp'); $qb->select('AVG(activity.travelTime) as export_avg_activity_visit_duration'); diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php index e4202c33a..004c2b14e 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php @@ -87,7 +87,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface $qb = $this->repository->createQueryBuilder('activity'); if (!in_array('actacp', $qb->getAllAliases(), true)) { - $qb->join('activity.accompanyingPeriod', 'actacp'); + $qb->join('activity.accompanyingPeriod', 'acp'); } $qb->select('COUNT(activity.id) as export_count_activity'); diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php index 2e87623ef..021966d90 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php @@ -86,7 +86,7 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface $qb = $this->repository->createQueryBuilder('activity'); if (!in_array('actacp', $qb->getAllAliases(), true)) { - $qb->join('activity.accompanyingPeriod', 'actacp'); + $qb->join('activity.accompanyingPeriod', 'acp'); } $qb->select('SUM(activity.durationTime) as export_sum_activity_duration'); diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php index 5bb6d542e..d64ac9200 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php @@ -86,7 +86,7 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac $qb = $this->repository->createQueryBuilder('activity'); if (!in_array('actacp', $qb->getAllAliases(), true)) { - $qb->join('activity.accompanyingPeriod', 'actacp'); + $qb->join('activity.accompanyingPeriod', 'acp'); } $qb->select('SUM(activity.travelTime) as export_sum_activity_visit_duration'); diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php index 9f4d15ec7..f12507df8 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php @@ -86,8 +86,8 @@ class CountActivity implements ExportInterface, GroupedExportInterface $qb = $this->activityRepository->createQueryBuilder('activity'); - if (!in_array('actperson', $qb->getAllAliases(), true)) { - $qb->join('activity.person', 'actperson'); + if (!in_array('person', $qb->getAllAliases(), true)) { + $qb->join('activity.person', 'person'); } $qb->select('COUNT(activity.id) as export_count_activity'); diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/ListActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/ListActivity.php index 17b9bd687..78368b0eb 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/ListActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/ListActivity.php @@ -210,7 +210,7 @@ class ListActivity implements ListInterface, GroupedExportInterface $qb ->from('ChillActivityBundle:Activity', 'activity') - ->join('activity.person', 'actperson') + ->join('activity.person', 'person') ->join('actperson.center', 'actcenter') ->andWhere('actcenter IN (:authorized_centers)') ->setParameter('authorized_centers', $centers); diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php index bcb4da05d..9013b4648 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php @@ -120,7 +120,7 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface } return $qb->select($select) - ->join('activity.person', 'actperson') + ->join('activity.person', 'person') ->join('actperson.center', 'actcenter') ->where($qb->expr()->in('actcenter', ':centers')) ->setParameter(':centers', $centers); From 4e82126bedaf3446a9a568cc0223e882ff64a66e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 21 Sep 2022 12:08:55 +0200 Subject: [PATCH 076/120] [activity][export] Fixed: fixed inconsistencies with date filters --- CHANGELOG.md | 1 + .../ActiveOneDayBetweenDatesFilter.php | 19 ++----------------- .../OpenBetweenDatesFilter.php | 14 +++----------- 3 files changed, 6 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a1fd7210..2fa9e2bb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to * [person][export] Fixed: rename the alias for `accompanying_period` to `acp` in filter associated with person * [activity][export] Feature: improve label for aliases in "Filter by activity type" * [activity][export] DX/Feature: use of an `ActivityTypeRepositoryInterface` instead of the old-style EntityRepository +* [person][export] Fixed: some inconsistency with date filter on accompanying courses ## Test releases diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilter.php index 6e2fa3288..ae9535a67 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilter.php @@ -29,24 +29,9 @@ class ActiveOneDayBetweenDatesFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $where = $qb->getDQLPart('where'); + $clause = "OVERLAPSI (acp.openingDate, acp.closingDate), (:datefrom, :dateto) = 'TRUE'"; - $clause = $qb->expr()->orX( - $qb->expr()->lt('(acp.openingDate + 1)', ':dateto'), - $qb->expr()->andX( - $qb->expr()->lt('acp.openingDate', ':datefrom'), - $qb->expr()->isNull('acp.closingDate') - ), - $qb->expr()->gt('(acp.closingDate - 1)', ':datefrom') - ); - - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); + $qb->andWhere($clause); $qb->setParameter('datefrom', $data['date_from'], Types::DATE_MUTABLE); $qb->setParameter('dateto', $data['date_to'], Types::DATE_MUTABLE); } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php index a782fdb88..b47f2020d 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php @@ -29,20 +29,12 @@ class OpenBetweenDatesFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->andX( - $qb->expr()->lt('acp.openingDate', ':datefrom'), - $qb->expr()->gt('acp.closingDate', ':dateto') + $qb->expr()->gte('acp.openingDate', ':datefrom'), + $qb->expr()->lte('acp.openingDate', ':dateto') ); - if ($where instanceof Andx) { - $where->add($clause); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); + $qb->andWhere($clause); $qb->setParameter('datefrom', $data['date_from'], Types::DATE_MUTABLE); $qb->setParameter('dateto', $data['date_to'], Types::DATE_MUTABLE); } From 01fb93e9e0d72fe1b3f80115a51c384ce5599779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 21 Sep 2022 16:13:15 +0200 Subject: [PATCH 077/120] [export][activity] count activities only once if the activity is present multiple times due to JOIN --- .../Export/Export/LinkedToACP/CountActivity.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php index 004c2b14e..566ee1ef4 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php @@ -90,7 +90,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface $qb->join('activity.accompanyingPeriod', 'acp'); } - $qb->select('COUNT(activity.id) as export_count_activity'); + $qb->select('COUNT(DISTINCT activity.id) as export_count_activity'); return $qb; } From ca6fde934b4e7b59bf4b375e1ca5113745fdb4eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 21 Sep 2022 16:14:07 +0200 Subject: [PATCH 078/120] [export][person] Fixed: fixed inconsistency in requestor query A condition was not present in a subquery, causing to take all courses into account, not the one concerned by the filter --- .../Filter/AccompanyingCourseFilters/RequestorFilter.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/RequestorFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/RequestorFilter.php index c0338b42e..e81cf3114 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/RequestorFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/RequestorFilter.php @@ -70,10 +70,6 @@ final class RequestorFilter implements FilterInterface case 'other_person': $expr = $this->em->getExpressionBuilder(); - if (!in_array('acppart', $qb->getAllAliases(), true)) { - $qb->join('acp.participations', 'acppart'); - } - $clause = $expr->andX( $expr->isNotNull('acp.requestorPerson'), $expr->notIn( @@ -85,6 +81,7 @@ final class RequestorFilter implements FilterInterface ->join('acp2.participations', 'acppart2') ->select('identity(acp2.requestorPerson)') ->where($expr->eq('acp2.requestorPerson', 'acppart2.person')) + ->andWhere('acp2.id = acp.id') ->getDQL() ) ); From cf9b9b3c75ea6c4800a203834f4d0cca04c15082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 21 Sep 2022 17:28:36 +0200 Subject: [PATCH 079/120] [person][export] Fixed: use left join for related entities in accompanying course aggregators --- CHANGELOG.md | 1 + .../AdministrativeLocationAggregator.php | 15 +++++-------- .../ClosingMotiveAggregator.php | 22 +++++++------------ .../ConfidentialAggregator.php | 9 +------- .../DurationAggregator.php | 11 ++-------- .../EmergencyAggregator.php | 9 +------- .../EvaluationAggregator.php | 17 ++++++-------- .../IntensityAggregator.php | 9 +------- .../JobAggregator.php | 15 +++++-------- .../OriginAggregator.php | 15 +++++-------- .../ReferrerAggregator.php | 15 +++++-------- .../RequestorAggregator.php | 10 +-------- .../ScopeAggregator.php | 15 +++++-------- .../SocialActionAggregator.php | 16 ++++++-------- .../SocialIssueAggregator.php | 12 +++------- .../StepAggregator.php | 11 +++------- .../EvaluationTypeAggregator.php | 17 ++++++-------- .../ActionTypeAggregator.php | 13 +++++------ .../SocialWorkAggregators/GoalAggregator.php | 21 ++++++++---------- .../SocialWorkAggregators/JobAggregator.php | 17 ++++++-------- .../ReferrerAggregator.php | 15 +++++-------- .../ResultAggregator.php | 17 ++++++-------- .../SocialWorkAggregators/ScopeAggregator.php | 15 +++++-------- .../Export/Export/CountEvaluation.php | 2 +- 24 files changed, 113 insertions(+), 206 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fa9e2bb0..df4dab422 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to * [activity][export] Feature: improve label for aliases in "Filter by activity type" * [activity][export] DX/Feature: use of an `ActivityTypeRepositoryInterface` instead of the old-style EntityRepository * [person][export] Fixed: some inconsistency with date filter on accompanying courses +* [person][export] Fixed: use left join for related entities in accompanying course aggregators ## Test releases diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregator.php index 13185c2a2..c0d20e7c7 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregator.php @@ -40,18 +40,11 @@ class AdministrativeLocationAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acploc', $qb->getAllAliases(), true)) { - $qb->join('acp.administrativeLocation', 'acploc'); + $qb->leftJoin('acp.administrativeLocation', 'acploc'); } $qb->addSelect('IDENTITY(acp.administrativeLocation) AS location_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('location_aggregator'); - } else { - $qb->groupBy('location_aggregator'); - } + $qb->addGroupBy('location_aggregator'); } public function applyOn(): string @@ -71,6 +64,10 @@ class AdministrativeLocationAggregator implements AggregatorInterface return 'Administrative location'; } + if (null === $value) { + return ''; + } + $l = $this->locationRepository->find($value); return $l->getName() . ' (' . $this->translatableStringHelper->localize($l->getLocationType()->getTitle()) . ')'; diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregator.php index 033ba4365..f9923c554 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregator.php @@ -15,6 +15,7 @@ use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive; use Chill\PersonBundle\Export\Declarations; +use Chill\PersonBundle\Repository\AccompanyingPeriod\ClosingMotiveRepository; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; @@ -26,10 +27,10 @@ class ClosingMotiveAggregator implements AggregatorInterface private TranslatableStringHelper $translatableStringHelper; public function __construct( - EntityManagerInterface $em, + ClosingMotiveRepository $motiveRepository, TranslatableStringHelper $translatableStringHelper ) { - $this->motiveRepository = $em->getRepository(ClosingMotive::class); + $this->motiveRepository = $motiveRepository; $this->translatableStringHelper = $translatableStringHelper; } @@ -40,19 +41,8 @@ class ClosingMotiveAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('acpmotive', $qb->getAllAliases(), true)) { - $qb->join('acp.closingMotive', 'acpmotive'); - } - $qb->addSelect('IDENTITY(acp.closingMotive) AS closingmotive_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('closingmotive_aggregator'); - } else { - $qb->groupBy('closingmotive_aggregator'); - } + $qb->addGroupBy('closingmotive_aggregator'); } public function applyOn(): string @@ -72,6 +62,10 @@ class ClosingMotiveAggregator implements AggregatorInterface return 'Closing motive'; } + if (NULL === $value) { + return ''; + } + $cm = $this->motiveRepository->find($value); return $this->translatableStringHelper->localize( diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregator.php index ec9bdd348..a2a4919aa 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregator.php @@ -35,14 +35,7 @@ class ConfidentialAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { $qb->addSelect('acp.confidential AS confidential_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('confidential_aggregator'); - } else { - $qb->groupBy('confidential_aggregator'); - } + $qb->addGroupBy('confidential_aggregator'); } public function applyOn(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregator.php index b43b0ba11..ee54da2af 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregator.php @@ -44,15 +44,8 @@ final class DurationAggregator implements AggregatorInterface // et ajouter une fonction custom qui calcule plus précisément les intervals, comme doctrineum/date-interval // https://packagist.org/packages/doctrineum/date-interval#3.1.0 (mais composer fait un conflit de dépendance) - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('duration_aggregator'); - } else { - $qb->groupBy('duration_aggregator'); - } - - $qb->orderBy('duration_aggregator'); + $qb->addGroupBy('duration_aggregator'); + $qb->addOrderBy('duration_aggregator'); } public function applyOn(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregator.php index f59491ec5..297faf049 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregator.php @@ -35,14 +35,7 @@ class EmergencyAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { $qb->addSelect('acp.emergency AS emergency_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('emergency_aggregator'); - } else { - $qb->groupBy('emergency_aggregator'); - } + $qb->addGroupBy('emergency_aggregator'); } public function applyOn(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregator.php index 120506aae..292303cea 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregator.php @@ -41,22 +41,15 @@ final class EvaluationAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acpw', $qb->getAllAliases(), true)) { - $qb->join('acp.works', 'acpw'); + $qb->leftJoin('acp.works', 'acpw'); } if (!in_array('workeval', $qb->getAllAliases(), true)) { - $qb->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval'); + $qb->leftJoin('acpw.accompanyingPeriodWorkEvaluations', 'workeval'); } $qb->addSelect('IDENTITY(workeval.evaluation) AS evaluation_aggregator'); - - $groupby = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('evaluation_aggregator'); - } else { - $qb->groupBy('evaluation_aggregator'); - } + $qb->addGroupBy('evaluation_aggregator'); } public function applyOn(): string @@ -76,6 +69,10 @@ final class EvaluationAggregator implements AggregatorInterface return 'Evaluation'; } + if (null === $value) { + return ''; + } + $e = $this->evaluationRepository->find($value); return $this->translatableStringHelper->localize( diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregator.php index 711f7ec0d..c418ea39f 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregator.php @@ -35,14 +35,7 @@ class IntensityAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { $qb->addSelect('acp.intensity AS intensity_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('intensity_aggregator'); - } else { - $qb->groupBy('intensity_aggregator'); - } + $qb->addGroupBy('intensity_aggregator'); } public function applyOn(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php index 7634ea37f..0809f7654 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php @@ -40,18 +40,11 @@ final class JobAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acpjob', $qb->getAllAliases(), true)) { - $qb->join('acp.job', 'acpjob'); + $qb->leftJoin('acp.job', 'acpjob'); } $qb->addSelect('IDENTITY(acp.job) AS job_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('job_aggregator'); - } else { - $qb->groupBy('job_aggregator'); - } + $qb->addGroupBy('job_aggregator'); } public function applyOn(): string @@ -71,6 +64,10 @@ final class JobAggregator implements AggregatorInterface return 'Job'; } + if (null === $value) { + return ''; + } + $j = $this->jobRepository->find($value); return $this->translatableStringHelper->localize( diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregator.php index 925160a2d..c7a178fa3 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregator.php @@ -42,18 +42,11 @@ final class OriginAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acporigin', $qb->getAllAliases(), true)) { - $qb->join('acp.origin', 'acporigin'); + $qb->leftJoin('acp.origin', 'acporigin'); } $qb->addSelect('acporigin.id AS origin_aggregator'); - - $groupby = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('origin_aggregator'); - } else { - $qb->groupBy('origin_aggregator'); - } + $qb->addGroupBy('origin_aggregator'); } public function applyOn(): string @@ -73,6 +66,10 @@ final class OriginAggregator implements AggregatorInterface return 'Origin'; } + if (null === $value) { + return ''; + } + $o = $this->repository->find($value); return $this->translatableStringHelper->localize( diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php index 22eca6f76..7851fe203 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php @@ -40,18 +40,11 @@ final class ReferrerAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acpuser', $qb->getAllAliases(), true)) { - $qb->join('acp.user', 'acpuser'); + $qb->leftJoin('acp.user', 'acpuser'); } $qb->addSelect('acpuser.id AS referrer_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('referrer_aggregator'); - } else { - $qb->groupBy('referrer_aggregator'); - } + $qb->addGroupBy('referrer_aggregator'); } public function applyOn(): string @@ -71,6 +64,10 @@ final class ReferrerAggregator implements AggregatorInterface return 'Referrer'; } + if (null === $value) { + return ''; + } + $r = $this->userRepository->find($value); return $this->userRender->renderString($r, []); diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/RequestorAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/RequestorAggregator.php index 80195bdcc..2f716c92a 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/RequestorAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/RequestorAggregator.php @@ -57,15 +57,7 @@ final class RequestorAggregator implements AggregatorInterface END ) AS requestor_aggregator "); - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('requestor_aggregator'); - } else { - $qb->groupBy('requestor_aggregator'); - } - - // TODO 'order by' does not works ! + $qb->addGroupBy('requestor_aggregator'); } public function applyOn(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregator.php index 5b2901921..0ff6aa747 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregator.php @@ -40,18 +40,11 @@ final class ScopeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acpscope', $qb->getAllAliases(), true)) { - $qb->join('acp.scopes', 'acpscope'); + $qb->leftJoin('acp.scopes', 'acpscope'); } $qb->addSelect('acpscope.id as scope_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('scope_aggregator'); - } else { - $qb->groupBy('scope_aggregator'); - } + $qb->addGroupBy('scope_aggregator'); } public function applyOn(): string @@ -71,6 +64,10 @@ final class ScopeAggregator implements AggregatorInterface return 'Scope'; } + if (null === $value) { + return ''; + } + $s = $this->scopeRepository->find($value); return $this->translatableStringHelper->localize( diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregator.php index 39ad085e3..dc8d2cfb3 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregator.php @@ -41,18 +41,12 @@ final class SocialActionAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acpw', $qb->getAllAliases(), true)) { + // here, we will only see accompanying period linked with a socialAction $qb->join('acp.works', 'acpw'); } - $qb->addSelect('IDENTITY(acpw.socialAction) AS socialaction_aggregator'); // DISTINCT ?? - - $groupby = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('socialaction_aggregator'); - } else { - $qb->groupBy('socialaction_aggregator'); - } + $qb->addSelect('IDENTITY(acpw.socialAction) AS socialaction_aggregator'); + $qb->addGroupBy('socialaction_aggregator'); } public function applyOn(): string @@ -72,6 +66,10 @@ final class SocialActionAggregator implements AggregatorInterface return 'Social action'; } + if (null === $value) { + return ''; + } + $sa = $this->actionRepository->find($value); return $this->actionRender->renderString($sa, []); diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregator.php index ac5ac94c0..98fdbad0c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregator.php @@ -40,18 +40,12 @@ final class SocialIssueAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acpsocialissue', $qb->getAllAliases(), true)) { + // we will see accompanying period linked with social issues $qb->join('acp.socialIssues', 'acpsocialissue'); } - + $qb->addSelect('acpsocialissue.id as socialissue_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('socialissue_aggregator'); - } else { - $qb->groupBy('socialissue_aggregator'); - } + $qb->addGroupBy('socialissue_aggregator'); } public function applyOn(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php index 5c923fdc2..2a1a3db25 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php @@ -41,15 +41,9 @@ final class StepAggregator implements AggregatorInterface //, FilterInterface public function alterQuery(QueryBuilder $qb, $data) { $qb->addSelect('acp.step AS step_aggregator'); + $qb->addGroupBy('step_aggregator'); - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('step_aggregator'); - } else { - $qb->groupBy('step_aggregator'); - } - + /* // add date in where clause $where = $qb->getDQLPart('where'); @@ -69,6 +63,7 @@ final class StepAggregator implements AggregatorInterface //, FilterInterface $qb->add('where', $where); $qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE); + */ } public function applyOn(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregator.php index bf0f48ab4..29420d5c9 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregator.php @@ -39,15 +39,8 @@ class EvaluationTypeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('IDENTITY(workeval.evaluation) AS evaluationtype_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('evaluationtype_aggregator'); - } else { - $qb->groupBy('evaluationtype_aggregator'); - } + $qb->addSelect('IDENTITY(workeval.evaluation) AS eval_evaluationtype_aggregator'); + $qb->addGroupBy('eval_evaluationtype_aggregator'); } public function applyOn(): string @@ -67,6 +60,10 @@ class EvaluationTypeAggregator implements AggregatorInterface return 'Evaluation type'; } + if (null === $value) { + return ''; + } + $ev = $this->evaluationRepository->find($value); return $this->translatableStringHelper->localize($ev->getTitle()); @@ -75,7 +72,7 @@ class EvaluationTypeAggregator implements AggregatorInterface public function getQueryKeys($data): array { - return ['evaluationtype_aggregator']; + return ['eval_evaluationtype_aggregator']; } public function getTitle(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php index fb64b3e01..37d50bf51 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php @@ -44,14 +44,7 @@ final class ActionTypeAggregator implements AggregatorInterface } $qb->addSelect('acpwsocialaction.id as action_type_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('action_type_aggregator'); - } else { - $qb->groupBy('action_type_aggregator'); - } + $qb->addGroupBy('action_type_aggregator'); } public function applyOn() @@ -71,6 +64,10 @@ final class ActionTypeAggregator implements AggregatorInterface return 'Social Action Type'; } + if (null === $value) { + return ''; + } + $sa = $this->socialActionRepository->find($value); return $this->actionRender->renderString($sa, []); diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php index bc534c678..0f418a678 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php @@ -38,18 +38,11 @@ final class GoalAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('goal', $qb->getAllAliases(), true)) { - $qb->join('acpw.goals', 'goal'); + $qb->leftJoin('acpw.goals', 'goal'); } - $qb->addSelect('IDENTITY(goal.goal) as goal_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('goal_aggregator'); - } else { - $qb->groupBy('goal_aggregator'); - } + $qb->addSelect('IDENTITY(goal.goal) as acpw_goal_aggregator'); + $qb->addGroupBy('acpw_goal_aggregator'); } public function applyOn(): string @@ -69,8 +62,12 @@ final class GoalAggregator implements AggregatorInterface return 'Goal Type'; } + if (null === $value) { + return ''; + } + $g = $this->goalRepository->find($value); - + return $this->translatableStringHelper->localize( $g->getTitle() ); @@ -79,7 +76,7 @@ final class GoalAggregator implements AggregatorInterface public function getQueryKeys($data): array { - return ['goal_aggregator']; + return ['acpw_goal_aggregator']; } public function getTitle(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/JobAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/JobAggregator.php index 4d27aa873..ca2e36d97 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/JobAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/JobAggregator.php @@ -40,18 +40,11 @@ final class JobAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acpwuser', $qb->getAllAliases(), true)) { - $qb->join('acpw.referrers', 'acpwuser'); + $qb->leftJoin('acpw.referrers', 'acpwuser'); } - $qb->addSelect('IDENTITY(acpwuser.userJob) as job_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('job_aggregator'); - } else { - $qb->groupBy('job_aggregator'); - } + $qb->addSelect('IDENTITY(acpwuser.userJob) as job_aggregator') + ->addGroupBy('job_aggregator'); } public function applyOn(): string @@ -71,6 +64,10 @@ final class JobAggregator implements AggregatorInterface return 'Job'; } + if (null === $value) { + return ''; + } + $j = $this->jobRepository->find($value); return $this->translatableStringHelper->localize( diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ReferrerAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ReferrerAggregator.php index 35e1e218d..46ef78035 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ReferrerAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ReferrerAggregator.php @@ -40,18 +40,11 @@ final class ReferrerAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acpwuser', $qb->getAllAliases(), true)) { - $qb->join('acpw.referrers', 'acpwuser'); + $qb->leftJoin('acpw.referrers', 'acpwuser'); } $qb->addSelect('acpwuser.id AS referrer_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('referrer_aggregator'); - } else { - $qb->groupBy('referrer_aggregator'); - } + $qb->addGroupBy('referrer_aggregator'); } public function applyOn(): string @@ -71,6 +64,10 @@ final class ReferrerAggregator implements AggregatorInterface return 'Referrer'; } + if (null === $value) { + return ''; + } + $r = $this->userRepository->find($value); return $this->userRender->renderString($r, []); diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php index 8f7ac0624..054b0ab55 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php @@ -41,15 +41,8 @@ final class ResultAggregator implements AggregatorInterface $qb->join('acpw.results', 'result'); } - $qb->addSelect('result.id as result_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('result_aggregator'); - } else { - $qb->groupBy('result_aggregator'); - } + $qb->addSelect('result.id AS acpw_result_aggregator'); + $qb->addGroupBy('acpw_result_aggregator'); } public function applyOn(): string @@ -69,6 +62,10 @@ final class ResultAggregator implements AggregatorInterface return 'Result Type'; } + if (null === $value) { + return ''; + } + $r = $this->resultRepository->find($value); return $this->translatableStringHelper->localize( @@ -79,7 +76,7 @@ final class ResultAggregator implements AggregatorInterface public function getQueryKeys($data): array { - return ['result_aggregator']; + return ['acpw_result_aggregator']; } public function getTitle(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ScopeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ScopeAggregator.php index ee479d503..1cf29d46f 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ScopeAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ScopeAggregator.php @@ -40,18 +40,11 @@ final class ScopeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acpwuser', $qb->getAllAliases(), true)) { - $qb->join('acpw.referrers', 'acpwuser'); + $qb->leftJoin('acpw.referrers', 'acpwuser'); } $qb->addSelect('IDENTITY(acpwuser.mainScope) as scope_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('scope_aggregator'); - } else { - $qb->groupBy('scope_aggregator'); - } + $qb->addGroupBy('scope_aggregator'); } public function applyOn(): string @@ -71,6 +64,10 @@ final class ScopeAggregator implements AggregatorInterface return 'Scope'; } + if (null === $value) { + return ''; + } + $s = $this->scopeRepository->find($value); return $this->translatableStringHelper->localize( diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php b/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php index 7178a54c8..634b351cf 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php @@ -98,7 +98,7 @@ class CountEvaluation implements ExportInterface, GroupedExportInterface $qb->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval'); } - $qb->select('COUNT(workeval.id) AS export_result'); + $qb->select('COUNT(DISTINCT workeval.id) AS export_result'); return $qb; } From 37662a4187a2b74013481dbc4591e016595cdeef Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Wed, 21 Sep 2022 18:04:34 +0200 Subject: [PATCH 080/120] batch replacing 'actacp' by 'acp' --- exports_alias_conventions.csv | 2 +- exports_alias_conventions.md | 2 +- .../Export/Export/LinkedToACP/CountActivity.php | 2 +- .../Export/Export/LinkedToACP/SumActivityDuration.php | 2 +- .../Export/Export/LinkedToACP/SumActivityVisitDuration.php | 2 +- .../Aggregator/ACPAggregators/BySocialActionAggregatorTest.php | 2 +- .../Aggregator/ACPAggregators/BySocialIssueAggregatorTest.php | 2 +- .../Aggregator/ACPAggregators/ByThirdpartyAggregatorTest.php | 2 +- .../Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php | 2 +- .../Export/Aggregator/ACPAggregators/DateAggregatorTest.php | 2 +- .../Aggregator/ACPAggregators/LocationTypeAggregatorTest.php | 2 +- .../Aggregator/ACPAggregators/UserScopeAggregatorTest.php | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/exports_alias_conventions.csv b/exports_alias_conventions.csv index 59b4c0fee..ab32cda8e 100644 --- a/exports_alias_conventions.csv +++ b/exports_alias_conventions.csv @@ -39,7 +39,7 @@ Household::class,,,household ,HouseholdComposition::class,household.compositions,composition Activity::class,,,activity ,Person::class,activity.person,actperson -,AccompanyingPeriod::class,activity.accompanyingPeriod,actacp +,AccompanyingPeriod::class,activity.accompanyingPeriod,acp ,Person::class,activity_person_having_activity.person,person_person_having_activity ,ActivityReason::class,activity_person_having_activity.reasons,reasons_person_having_activity ,ActivityType::class,activity.activityType,acttype diff --git a/exports_alias_conventions.md b/exports_alias_conventions.md index f04c97ff2..579d004a5 100644 --- a/exports_alias_conventions.md +++ b/exports_alias_conventions.md @@ -47,7 +47,7 @@ These are alias conventions : | | HouseholdComposition::class | household.compositions | composition | | Activity::class | | | activity | | | Person::class | activity.person | actperson | -| | AccompanyingPeriod::class | activity.accompanyingPeriod | actacp | +| | AccompanyingPeriod::class | activity.accompanyingPeriod | acp | | | Person::class | activity\_person\_having\_activity.person | person\_person\_having\_activity | | | ActivityReason::class | activity\_person\_having\_activity.reasons | reasons\_person\_having\_activity | | | ActivityType::class | activity.activityType | acttype | diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php index 566ee1ef4..2162b8b58 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php @@ -86,7 +86,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface { $qb = $this->repository->createQueryBuilder('activity'); - if (!in_array('actacp', $qb->getAllAliases(), true)) { + if (!in_array('acp', $qb->getAllAliases(), true)) { $qb->join('activity.accompanyingPeriod', 'acp'); } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php index 021966d90..5c85046ae 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php @@ -85,7 +85,7 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface { $qb = $this->repository->createQueryBuilder('activity'); - if (!in_array('actacp', $qb->getAllAliases(), true)) { + if (!in_array('acp', $qb->getAllAliases(), true)) { $qb->join('activity.accompanyingPeriod', 'acp'); } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php index d64ac9200..af1a0c30a 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php @@ -85,7 +85,7 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac { $qb = $this->repository->createQueryBuilder('activity'); - if (!in_array('actacp', $qb->getAllAliases(), true)) { + if (!in_array('acp', $qb->getAllAliases(), true)) { $qb->join('activity.accompanyingPeriod', 'acp'); } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialActionAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialActionAggregatorTest.php index b4da861e8..d9b3a8cfb 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialActionAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialActionAggregatorTest.php @@ -51,7 +51,7 @@ final class BySocialActionAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(activity.id)') ->from(Activity::class, 'activity') - ->join('activity.accompanyingPeriod', 'actacp') + ->join('activity.accompanyingPeriod', 'acp') ->join('activity.socialActions', 'actsocialaction') , ]; diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialIssueAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialIssueAggregatorTest.php index ab447739e..87e8462db 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialIssueAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialIssueAggregatorTest.php @@ -51,7 +51,7 @@ final class BySocialIssueAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(activity.id)') ->from(Activity::class, 'activity') - ->join('activity.accompanyingPeriod', 'actacp') + ->join('activity.accompanyingPeriod', 'acp') ->join('activity.socialIssues', 'actsocialissue') , ]; diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByThirdpartyAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByThirdpartyAggregatorTest.php index e65ffec62..c9a824dc3 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByThirdpartyAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByThirdpartyAggregatorTest.php @@ -51,7 +51,7 @@ final class ByThirdpartyAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(activity.id)') ->from(Activity::class, 'activity') - ->join('activity.accompanyingPeriod', 'actacp') + ->join('activity.accompanyingPeriod', 'acp') ->join('activity.thirdParties', 'acttparty') , ]; diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php index da1f0ecac..00243ce68 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php @@ -51,7 +51,7 @@ final class ByUserAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(activity.id)') ->from(Activity::class, 'activity') - ->join('activity.accompanyingPeriod', 'actacp') + ->join('activity.accompanyingPeriod', 'acp') ->join('activity.users', 'actusers') , ]; diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/DateAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/DateAggregatorTest.php index f8775b8bf..c5a1b083c 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/DateAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/DateAggregatorTest.php @@ -59,7 +59,7 @@ final class DateAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(activity.id)') ->from(Activity::class, 'activity') - ->join('activity.accompanyingPeriod', 'actacp') + ->join('activity.accompanyingPeriod', 'acp') , ]; } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/LocationTypeAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/LocationTypeAggregatorTest.php index 8f8b866bc..69be2c900 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/LocationTypeAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/LocationTypeAggregatorTest.php @@ -51,7 +51,7 @@ final class LocationTypeAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(activity.id)') ->from(Activity::class, 'activity') - ->join('activity.accompanyingPeriod', 'actacp') + ->join('activity.accompanyingPeriod', 'acp') ->join('activity.location', 'actloc') , ]; diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php index 3434f1a1c..73943ce7f 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php @@ -51,7 +51,7 @@ final class UserScopeAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(activity.id)') ->from(Activity::class, 'activity') - ->join('activity.accompanyingPeriod', 'actacp') + ->join('activity.accompanyingPeriod', 'acp') ->join('activity.user', 'actuser') , ]; From e487bdf7fd05fc53553e80cd42f19f361f14b37d Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Thu, 22 Sep 2022 13:21:36 +0200 Subject: [PATCH 081/120] fix issue628 when result is null --- .../Export/Formatter/SpreadSheetFormatter.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Export/Formatter/SpreadSheetFormatter.php b/src/Bundle/ChillMainBundle/Export/Formatter/SpreadSheetFormatter.php index 6d6a685b7..ab9b2d660 100644 --- a/src/Bundle/ChillMainBundle/Export/Formatter/SpreadSheetFormatter.php +++ b/src/Bundle/ChillMainBundle/Export/Formatter/SpreadSheetFormatter.php @@ -496,8 +496,13 @@ class SpreadSheetFormatter implements FormatterInterface // 3. iterate on `keysExportElementAssociation` to store the callable // in cache foreach ($keysExportElementAssociation as $key => [$element, $data]) { - $this->cacheDisplayableResult[$key] = - $element->getLabels($key, array_unique($allValues[$key]), $data); + // handle the case when there is not results lines (query is empty) + if ([] === $allValues) { + $this->cacheDisplayableResult[$key] = $element->getLabels($key, ['_header'], $data); + } else { + $this->cacheDisplayableResult[$key] = + $element->getLabels($key, array_unique($allValues[$key]), $data); + } } // the cache is initialized ! From 4fe4f6a87777146cca8702c09e354e778a775bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 22 Sep 2022 14:01:30 +0200 Subject: [PATCH 082/120] DXFeature: ClosingMotiveRepository implements correct interace --- .../ClosingMotiveAggregator.php | 5 +-- .../ClosingMotiveRepository.php | 34 ++++++++++++++++++- .../ClosingMotiveRepositoryInterface.php | 13 +++++++ 3 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepositoryInterface.php diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregator.php index f9923c554..de276f007 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregator.php @@ -16,18 +16,19 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Repository\AccompanyingPeriod\ClosingMotiveRepository; +use Chill\PersonBundle\Repository\AccompanyingPeriod\ClosingMotiveRepositoryInterface; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; class ClosingMotiveAggregator implements AggregatorInterface { - private EntityManagerInterface $em; + private ClosingMotiveRepositoryInterface $motiveRepository; private TranslatableStringHelper $translatableStringHelper; public function __construct( - ClosingMotiveRepository $motiveRepository, + ClosingMotiveRepositoryInterface $motiveRepository, TranslatableStringHelper $translatableStringHelper ) { $this->motiveRepository = $motiveRepository; diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepository.php index ea5b1feff..283dac151 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepository.php @@ -15,8 +15,9 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query\ResultSetMappingBuilder; +use UnexpectedValueException; -final class ClosingMotiveRepository +final class ClosingMotiveRepository implements ClosingMotiveRepositoryInterface { private EntityManagerInterface $entityManager; @@ -54,4 +55,35 @@ final class ClosingMotiveRepository ->createNativeQuery($sql, $rsm) ->getResult(); } + + public function find($id): ?ClosingMotive + { + return $this->repository->find($id); + } + + /** + * @return array|ClosingMotive[] + */ + public function findAll(): array + { + return $this->repository->findAll(); + } + + /** + * @return array|ClosingMotive[] + */ + 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): ?ClosingMotive + { + return $this->findOneBy($criteria); + } + + public function getClassName(): string + { + return ClosingMotive::class; + } } diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepositoryInterface.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepositoryInterface.php new file mode 100644 index 000000000..fbea29a0b --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepositoryInterface.php @@ -0,0 +1,13 @@ + Date: Thu, 22 Sep 2022 16:58:29 +0200 Subject: [PATCH 083/120] [Activity][Export] Fixed: use leftJoin on aggregators --- .../BySocialActionAggregator.php | 15 ++++----- .../BySocialIssueAggregator.php | 15 ++++----- .../ACPAggregators/ByThirdpartyAggregator.php | 15 ++++----- .../ACPAggregators/ByUserAggregator.php | 15 ++++----- .../ACPAggregators/DateAggregator.php | 33 +++++-------------- .../ACPAggregators/LocationTypeAggregator.php | 15 ++++----- .../ACPAggregators/UserScopeAggregator.php | 15 ++++----- .../Aggregator/ActivityTypeAggregator.php | 13 +++----- .../Aggregator/ActivityUserAggregator.php | 9 ++--- 9 files changed, 54 insertions(+), 91 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialActionAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialActionAggregator.php index f35fe6e6b..251f2e9d4 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialActionAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialActionAggregator.php @@ -41,18 +41,11 @@ class BySocialActionAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('actsocialaction', $qb->getAllAliases(), true)) { - $qb->join('activity.socialActions', 'actsocialaction'); + $qb->leftJoin('activity.socialActions', 'actsocialaction'); } $qb->addSelect('actsocialaction.id AS socialaction_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('socialaction_aggregator'); - } else { - $qb->groupBy('socialaction_aggregator'); - } + $qb->addGroupBy('socialaction_aggregator'); } public function applyOn(): string @@ -72,6 +65,10 @@ class BySocialActionAggregator implements AggregatorInterface return 'Social action'; } + if (null === $value) { + return ''; + } + $sa = $this->actionRepository->find($value); return $this->actionRender->renderString($sa, []); diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialIssueAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialIssueAggregator.php index 1126b9373..d7abcfd49 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialIssueAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialIssueAggregator.php @@ -41,18 +41,11 @@ class BySocialIssueAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('actsocialissue', $qb->getAllAliases(), true)) { - $qb->join('activity.socialIssues', 'actsocialissue'); + $qb->leftJoin('activity.socialIssues', 'actsocialissue'); } $qb->addSelect('actsocialissue.id AS socialissue_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('socialissue_aggregator'); - } else { - $qb->groupBy('socialissue_aggregator'); - } + $qb->addGroupBy('socialissue_aggregator'); } public function applyOn(): string @@ -72,6 +65,10 @@ class BySocialIssueAggregator implements AggregatorInterface return 'Social issues'; } + if (null === $value) { + return ''; + } + $i = $this->issueRepository->find($value); return $this->issueRender->renderString($i, []); diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByThirdpartyAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByThirdpartyAggregator.php index 4b56c6fc7..ebd813af4 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByThirdpartyAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByThirdpartyAggregator.php @@ -41,18 +41,11 @@ class ByThirdpartyAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acttparty', $qb->getAllAliases(), true)) { - $qb->join('activity.thirdParties', 'acttparty'); + $qb->leftJoin('activity.thirdParties', 'acttparty'); } $qb->addSelect('acttparty.id AS thirdparty_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('thirdparty_aggregator'); - } else { - $qb->groupBy('thirdparty_aggregator'); - } + $qb->addGroupBy('thirdparty_aggregator'); } public function applyOn(): string @@ -72,6 +65,10 @@ class ByThirdpartyAggregator implements AggregatorInterface return 'Accepted thirdparty'; } + if (null === $value) { + return ''; + } + $tp = $this->thirdPartyRepository->find($value); return $this->thirdPartyRender->renderString($tp, []); diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByUserAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByUserAggregator.php index 3787e5286..9eb5c307e 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByUserAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByUserAggregator.php @@ -41,18 +41,11 @@ class ByUserAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('actusers', $qb->getAllAliases(), true)) { - $qb->join('activity.users', 'actusers'); + $qb->leftJoin('activity.users', 'actusers'); } $qb->addSelect('actusers.id AS users_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('users_aggregator'); - } else { - $qb->groupBy('users_aggregator'); - } + $qb->addGroupBy('users_aggregator'); } public function applyOn(): string @@ -72,6 +65,10 @@ class ByUserAggregator implements AggregatorInterface return 'Accepted users'; } + if (null === $value) { + return ''; + } + $u = $this->userRepository->find($value); return $this->userRender->renderString($u, []); diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php index 5147936c9..982f0d6ed 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php @@ -49,11 +49,11 @@ class DateAggregator implements AggregatorInterface switch ($data['frequency']) { case 'month': - $fmt = 'MM'; + $fmt = 'YYYY-MM'; break; case 'week': - $fmt = 'IW'; + $fmt = 'YYYY-IW'; break; case 'year': @@ -65,22 +65,8 @@ class DateAggregator implements AggregatorInterface } $qb->addSelect(sprintf("TO_CHAR(activity.date, '%s') AS date_aggregator", $fmt)); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('date_aggregator'); - } else { - $qb->groupBy('date_aggregator'); - } - - $orderBy = $qb->getDQLPart('orderBy'); - - if (!empty($orderBy)) { - $qb->addOrderBy('date_aggregator', $order); - } else { - $qb->orderBy('date_aggregator', $order); - } + $qb->addGroupBy('date_aggregator'); + $qb->addOrderBy('date_aggregator', $order); } public function applyOn(): string @@ -106,15 +92,12 @@ class DateAggregator implements AggregatorInterface return 'by ' . $data['frequency']; } + if (null === $value) { + return ''; + } + switch ($data['frequency']) { case 'month': - $month = DateTime::createFromFormat('!m', $value); - - return sprintf( - '%02d (%s)', - $value, - $month->format('M') - ); case 'week': //return $this->translator->trans('for week') .' '. $value ; diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/LocationTypeAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/LocationTypeAggregator.php index d05c0eb41..a4c1087db 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/LocationTypeAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/LocationTypeAggregator.php @@ -41,18 +41,11 @@ class LocationTypeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('actloc', $qb->getAllAliases(), true)) { - $qb->join('activity.location', 'actloc'); + $qb->leftJoin('activity.location', 'actloc'); } $qb->addSelect('IDENTITY(actloc.locationType) AS locationtype_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('locationtype_aggregator'); - } else { - $qb->groupBy('locationtype_aggregator'); - } + $qb->addGroupBy('locationtype_aggregator'); } public function applyOn(): string @@ -72,6 +65,10 @@ class LocationTypeAggregator implements AggregatorInterface return 'Accepted locationtype'; } + if (null === $value) { + return ''; + } + $lt = $this->locationTypeRepository->find($value); return $this->translatableStringHelper->localize( diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/UserScopeAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/UserScopeAggregator.php index faaa08b8e..1b7041d9d 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/UserScopeAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/UserScopeAggregator.php @@ -41,18 +41,11 @@ class UserScopeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('actuser', $qb->getAllAliases(), true)) { - $qb->join('activity.user', 'actuser'); + $qb->leftJoin('activity.user', 'actuser'); } $qb->addSelect('IDENTITY(actuser.mainScope) AS userscope_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('userscope_aggregator'); - } else { - $qb->groupBy('userscope_aggregator'); - } + $qb->addGroupBy('userscope_aggregator'); } public function applyOn(): string @@ -72,6 +65,10 @@ class UserScopeAggregator implements AggregatorInterface return 'Scope'; } + if (null === $value) { + return ''; + } + $s = $this->scopeRepository->find($value); return $this->translatableStringHelper->localize( diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php index cada50387..74df67d22 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php @@ -49,14 +49,7 @@ class ActivityTypeAggregator implements AggregatorInterface } $qb->addSelect(sprintf('IDENTITY(activity.activityType) AS %s', self::KEY)); - - $groupby = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy(self::KEY); - } else { - $qb->groupBy(self::KEY); - } + $qb->addGroupBy(self::KEY); } public function applyOn(): string @@ -79,6 +72,10 @@ class ActivityTypeAggregator implements AggregatorInterface return 'Activity type'; } + if (null === $value) { + return ''; + } + $t = $this->activityTypeRepository->find($value); return $this->translatableStringHelper->localize($t->getName()); diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUserAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUserAggregator.php index 39c7c52cb..8e29f9cb0 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUserAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUserAggregator.php @@ -61,14 +61,15 @@ class ActivityUserAggregator implements AggregatorInterface public function getLabels($key, $values, $data): Closure { - // preload users at once - $this->userRepository->findBy(['id' => $values]); - - return function ($value) { + return function ($value) { if ('_header' === $value) { return 'Activity user'; } + if (null === $value) { + return ''; + } + $u = $this->userRepository->find($value); return $this->userRender->renderString($u, []); From 89bdc76565aa80524d61b355102818b7895ec7cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 22 Sep 2022 18:15:59 +0200 Subject: [PATCH 084/120] handle unknown in gender aggregator --- .../Export/Aggregator/PersonAggregators/GenderAggregator.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GenderAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GenderAggregator.php index 0b94cbd4f..3cfadc355 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GenderAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GenderAggregator.php @@ -62,6 +62,9 @@ final class GenderAggregator implements AggregatorInterface case Person::BOTH_GENDER: return $this->translator->trans('both'); + case Person::NO_INFORMATION: + return $this->translator->trans('unknown'); + case null: return $this->translator->trans('Not given'); From 6cdb3033dbba36887b2524677d354602cb80352c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 23 Sep 2022 21:51:26 +0200 Subject: [PATCH 085/120] [export][person] Fixed: filter per person's age: use calculatio non year interval --- .../Export/Filter/PersonFilters/AgeFilter.php | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php index bada59a8d..eb83f3ff0 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php @@ -34,17 +34,20 @@ class AgeFilter implements ExportElementValidatedInterface, FilterInterface $where = $qb->getDQLPart('where'); $min = null !== $data['min_age'] ? $data['min_age'] : 0; - $max = null !== $data['max_age'] ? $data['max_age'] : 200; + $max = null !== $data['max_age'] ? $data['max_age'] : 3000; $calc = $data['date_calc']; + $minDate = $calc->sub(new \DateInterval('P' . $max . 'Y')); + $maxDate = $calc->sub(new \DateInterval('P' . $min . 'Y')); + $clause = $qb->expr()->andX( $qb->expr()->gte( - 'DATE_DIFF(:calc_date, person.birthdate)/365', - ':min_age' + 'person.birthdate', + ':min_date' ), $qb->expr()->lte( - 'DATE_DIFF(:calc_date, person.birthdate)/365', - ':max_age' + 'person.birthdate', + ':max_date' ) ); @@ -55,9 +58,8 @@ class AgeFilter implements ExportElementValidatedInterface, FilterInterface } $qb->add('where', $where); - $qb->setParameter('min_age', $min); - $qb->setParameter('max_age', $max); - $qb->setParameter('calc_date', $calc); + $qb->setParameter('min_date', $minDate); + $qb->setParameter('max_date', $maxDate); } public function applyOn() @@ -77,7 +79,8 @@ class AgeFilter implements ExportElementValidatedInterface, FilterInterface $builder->add('date_calc', ChillDateType::class, [ 'label' => 'Calculate age in relation to this date', - 'data' => new DateTime('now'), + 'data' => new \DateTimeImmutable('now'), + 'input' => 'datetime_immutable' ]); } From 75713af0e08e178576ca28d00a11a5244627ce3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 23 Sep 2022 21:55:00 +0200 Subject: [PATCH 086/120] [export][person] fix alias name for person in CountPersonWithAccompanyingCourse export --- .../Export/Export/CountPersonWithAccompanyingCourse.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php b/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php index 51d85c479..057611032 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php @@ -95,10 +95,10 @@ class CountPersonWithAccompanyingCourse implements ExportInterface, GroupedExpor } if (!in_array('partperson', $qb->getAllAliases(), true)) { - $qb->join('acppart.person', 'partperson'); + $qb->join('acppart.person', 'person'); } - $qb->select('COUNT(DISTINCT partperson.id) AS export_result'); + $qb->select('COUNT(DISTINCT person.id) AS export_result'); return $qb; } From 6eff1962df4e8ed40f8f94d3a54b9d2c43d1dc32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 26 Sep 2022 14:43:04 +0200 Subject: [PATCH 087/120] Fix import and add a "not null" condition on duration time --- .../Export/LinkedToACP/AvgActivityDuration.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php index 5dae4c0ef..3fc975147 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php @@ -17,6 +17,7 @@ use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; @@ -34,7 +35,6 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface public function buildForm(FormBuilderInterface $builder) { - // TODO: Implement buildForm() method. } public function getAllowedFormattersTypes(): array @@ -55,7 +55,7 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface public function getLabels($key, array $values, $data) { if ('export_avg_activity_duration' !== $key) { - throw new LogicException("the key {$key} is not used by this export"); + throw new \LogicException("the key {$key} is not used by this export"); } return static fn ($value) => '_header' === $value ? 'Average activities linked to an accompanying period duration' : $value; @@ -83,10 +83,12 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { - $qb = $this->repository->createQueryBuilder('activity') - ->join('activity.accompanyingPeriod', 'acp'); + $qb = $this->repository->createQueryBuilder('activity'); - $qb->select('AVG(activity.durationTime) as export_avg_activity_duration'); + $qb + ->join('activity.accompanyingPeriod', 'acp') + ->select('AVG(activity.durationTime) as export_avg_activity_duration') + ->andWhere($qb->expr()->isNotNull('activity.durationTime')); return $qb; } From 58eb089d1caf5b04a9b30edaf9ce93de5a8632f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 26 Sep 2022 14:49:51 +0200 Subject: [PATCH 088/120] Fix use import and add a "not null" condition on travel time --- .../Export/LinkedToACP/AvgActivityVisitDuration.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php index 560ae8754..69533a09d 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php @@ -17,6 +17,7 @@ use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; @@ -55,7 +56,7 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac public function getLabels($key, array $values, $data) { if ('export_avg_activity_visit_duration' !== $key) { - throw new LogicException("the key {$key} is not used by this export"); + throw new \LogicException("the key {$key} is not used by this export"); } return static fn ($value) => '_header' === $value ? 'Average activities linked to an accompanying period visit duration' : $value; @@ -83,10 +84,13 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { - $qb = $this->repository->createQueryBuilder('activity') - ->join('activity.accompanyingPeriod', 'acp'); + $qb = $this->repository->createQueryBuilder('activity'); - $qb->select('AVG(activity.travelTime) as export_avg_activity_visit_duration'); + $qb + ->join('activity.accompanyingPeriod', 'acp') + ->select('AVG(activity.travelTime) as export_avg_activity_visit_duration') + ->andWhere($qb->expr()->isNotNull('activity.travelTime')) + ; return $qb; } From 2c46886e36d41e4d0f71b8a672af3015467572ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 26 Sep 2022 14:59:31 +0200 Subject: [PATCH 089/120] Fix use import and add a "not null" condition on travel time --- .../Export/Export/LinkedToACP/SumActivityDuration.php | 6 ++++-- .../Export/Export/LinkedToACP/SumActivityVisitDuration.php | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php index 5c85046ae..e9dbaff29 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php @@ -17,6 +17,7 @@ use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; @@ -55,7 +56,7 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface public function getLabels($key, array $values, $data) { if ('export_sum_activity_duration' !== $key) { - throw new LogicException("the key {$key} is not used by this export"); + throw new \LogicException("the key {$key} is not used by this export"); } return static fn ($value) => '_header' === $value ? 'Sum activities linked to an accompanying period duration' : $value; @@ -89,7 +90,8 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface $qb->join('activity.accompanyingPeriod', 'acp'); } - $qb->select('SUM(activity.durationTime) as export_sum_activity_duration'); + $qb->select('SUM(activity.durationTime) as export_sum_activity_duration') + ->andWhere($qb->expr()->isNotNull('activity.durationTime')); return $qb; } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php index af1a0c30a..d41994562 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php @@ -17,6 +17,7 @@ use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; @@ -55,7 +56,7 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac public function getLabels($key, array $values, $data) { if ('export_sum_activity_visit_duration' !== $key) { - throw new LogicException("the key {$key} is not used by this export"); + throw new \LogicException("the key {$key} is not used by this export"); } return static fn ($value) => '_header' === $value ? 'Sum activities linked to an accompanying period visit duration' : $value; @@ -89,7 +90,8 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac $qb->join('activity.accompanyingPeriod', 'acp'); } - $qb->select('SUM(activity.travelTime) as export_sum_activity_visit_duration'); + $qb->select('SUM(activity.travelTime) as export_sum_activity_visit_duration') + ->andWhere($qb->expr()->isNotNull('activity.travelTime')); return $qb; } From 95f7622923fc6f19ca3ce442d3b5e78dead66b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 26 Sep 2022 16:45:36 +0200 Subject: [PATCH 090/120] [Export][person] Fixed: error and more precision on household composition A filter and two aggregators create a join between composition and household, but with a parameter at different date. Each aggregator and filter now have a custom alias, to allow to set this date parameter on each join. --- .../ChillMainBundle/Export/ExportManager.php | 4 +- .../ChildrenNumberAggregator.php | 56 +++++++------------ .../CompositionAggregator.php | 49 +++++++--------- .../Export/Export/CountHousehold.php | 3 +- .../HouseholdFilters/CompositionFilter.php | 37 +++++------- 5 files changed, 57 insertions(+), 92 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Export/ExportManager.php b/src/Bundle/ChillMainBundle/Export/ExportManager.php index df14a8ac8..ba15d0cc0 100644 --- a/src/Bundle/ChillMainBundle/Export/ExportManager.php +++ b/src/Bundle/ChillMainBundle/Export/ExportManager.php @@ -536,8 +536,8 @@ class ExportManager . 'an ExportInterface.'); } - if (null === $centers || [] === $centers) { - // we want to try if at least one center is reachable + if (null === $centers || [] !== $centers) { + // we want to try if at least one center is reachabler return [] !== $this->authorizationHelper->getReachableCenters( $this->user, $role diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregator.php index 240da475e..2343b2bab 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregator.php @@ -16,6 +16,7 @@ use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Export\Declarations; use DateTime; use Doctrine\DBAL\Types\Types; +use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; @@ -39,39 +40,23 @@ class ChildrenNumberAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('composition', $qb->getAllAliases(), true)) { - $qb->join('household.compositions', 'composition'); + if (!in_array('composition_children', $qb->getAllAliases(), true)) { + $clause = $qb->expr()->andX( + $qb->expr()->lte('composition_children.startDate', ':ondate_composition_children'), + $qb->expr()->orX( + $qb->expr()->gt('composition_children.endDate', ':ondate_composition_children'), + $qb->expr()->isNull('composition_children.endDate') + ) + ); + + $qb->leftJoin('household.compositions', 'composition_children', Expr\Join::WITH, $clause); } - $qb->addSelect('composition.numberOfChildren AS childrennumber_aggregator'); + $qb + ->addSelect('composition_children.numberOfChildren AS childrennumber_aggregator') + ->addGroupBy('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') - ) - ); - - 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); + $qb->setParameter('ondate_composition_children', $data['on_date'], Types::DATE_MUTABLE); } public function applyOn(): string @@ -93,12 +78,11 @@ class ChildrenNumberAggregator implements AggregatorInterface return 'Number of children'; } - return $this->translator->trans( - 'household_composition.numberOfChildren children in household', - [ - 'numberOfChildren' => $value, - ] - ); + if (null === $value) { + return ''; + } + + return $value; }; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php index fdb18e951..7e10ea429 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php @@ -18,6 +18,7 @@ use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Repository\Household\HouseholdCompositionTypeRepository; use DateTime; use Doctrine\DBAL\Types\Types; +use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; @@ -44,39 +45,23 @@ class CompositionAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('composition', $qb->getAllAliases(), true)) { - $qb->join('household.compositions', 'composition'); + if (!in_array('composition_type', $qb->getAllAliases(), true)) { + $clause = $qb->expr()->andX( + $qb->expr()->lte('composition_type.startDate', ':ondate_composition_type'), + $qb->expr()->orX( + $qb->expr()->gt('composition_type.endDate', ':ondate_composition_type'), + $qb->expr()->isNull('composition_type.endDate') + ) + ); + + $qb->leftJoin('household.compositions', 'composition_type', Expr\Join::WITH, $clause); } - $qb->addSelect('IDENTITY(composition.householdCompositionType) AS composition_aggregator'); + $qb + ->addSelect('IDENTITY(composition_type.householdCompositionType) AS composition_aggregator') + ->addGroupBy('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); + $qb->setParameter('ondate_composition_type', $data['on_date'], Types::DATE_MUTABLE); } public function applyOn(): string @@ -98,6 +83,10 @@ class CompositionAggregator implements AggregatorInterface return 'Composition'; } + if (null === $value) { + return ''; + } + $c = $this->typeRepository->find($value); return $this->translatableStringHelper->localize( diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php index 18ddbaea1..106084e4f 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php @@ -106,8 +106,7 @@ class CountHousehold implements ExportInterface, GroupedExportInterface $qb->join('member.household', 'household'); } - - $qb->select('COUNT(DISTINCT member.household) AS export_result'); + $qb->select('COUNT(DISTINCT household.id) AS export_result'); return $qb; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php index 3d00ed36b..1ca78d669 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php @@ -18,6 +18,7 @@ use Chill\PersonBundle\Entity\Household\HouseholdCompositionType; use Chill\PersonBundle\Export\Declarations; use DateTime; use Doctrine\DBAL\Types\Types; +use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; @@ -41,32 +42,24 @@ class CompositionFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('composition', $qb->getAllAliases(), true)) { - $qb->join('household.compositions', 'composition'); + if (!in_array('composition_type_filter', $qb->getAllAliases(), true)) { + $clause = + $qb->expr()->andX( + $qb->expr()->lte('composition_type_filter.startDate', ':ondate_composition_type_filter'), + $qb->expr()->orX( + $qb->expr()->gt('composition_type_filter.endDate', ':ondate_composition_type_filter'), + $qb->expr()->isNull('composition_type_filter.endDate') + ) + ); + + $qb->join('household.compositions', 'composition', Expr\Join::WITH, $clause); } - $where = $qb->getDQLPart('where'); + $whereClause = $qb->expr()->in('composition_type_filter.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); - } else { - $where = $qb->expr()->andX($clause); - } - - $qb->add('where', $where); + $qb->andWhere($whereClause); $qb->setParameter('compositions', $data['accepted_composition']); - $qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE); + $qb->setParameter('ondate_composition_type_filter', $data['on_date'], Types::DATE_MUTABLE); } public function applyOn(): string From e3764f6f91c85114f3bd70518f7480ccc6bd6d8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 26 Sep 2022 17:02:28 +0200 Subject: [PATCH 091/120] [export][person] Fixed: use left join on association between works and goal/results --- .../ActionTypeAggregator.php | 2 +- .../GoalResultAggregator.php | 21 +++++++------------ .../ResultAggregator.php | 2 +- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php index 37d50bf51..fc96b9e6b 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php @@ -40,7 +40,7 @@ final class ActionTypeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acpwsocialaction', $qb->getAllAliases(), true)) { - $qb->join('acpw.socialAction', 'acpwsocialaction'); + $qb->leftJoin('acpw.socialAction', 'acpwsocialaction'); } $qb->addSelect('acpwsocialaction.id as action_type_aggregator'); diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalResultAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalResultAggregator.php index aeced53d7..48075c38b 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalResultAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalResultAggregator.php @@ -35,6 +35,10 @@ class GoalResultAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { return function ($value) use ($key): string { + if (null === $value) { + return ''; + } + switch ($key) { case 'goal_aggregator': @@ -107,25 +111,16 @@ class GoalResultAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('goal', $qb->getAllAliases(), true)) { - $qb->join('acpw.goals', 'goal'); + $qb->leftJoin('acpw.goals', 'goal'); } if (!in_array('goalresult', $qb->getAllAliases(), true)) { - $qb->join('goal.results', 'goalresult'); + $qb->leftJoin('goal.results', 'goalresult'); } $qb->addSelect('IDENTITY(goal.goal) as goal_aggregator'); $qb->addSelect('goalresult.id as result_aggregator'); - - $groupBy = $qb->getDQLPart('groupBy'); - - if (!empty($groupBy)) { - $qb->addGroupBy('goal_aggregator'); - } else { - $qb->groupBy('goal_aggregator'); - } - - $qb->addGroupBy('result_aggregator'); + $qb->addGroupBy('goal_aggregator')->addGroupBy('result_aggregator'); } /** @@ -135,4 +130,4 @@ class GoalResultAggregator implements AggregatorInterface { return Declarations::SOCIAL_WORK_ACTION_TYPE; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php index 054b0ab55..4b190e0a5 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php @@ -38,7 +38,7 @@ final class ResultAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('result', $qb->getAllAliases(), true)) { - $qb->join('acpw.results', 'result'); + $qb->leftJoin('acpw.results', 'result'); } $qb->addSelect('result.id AS acpw_result_aggregator'); From 78e00b8eba9036c6d46c7359ad2dad09a4ba9ad6 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Mon, 26 Sep 2022 17:40:58 +0200 Subject: [PATCH 092/120] exports: improve translations --- src/Bundle/ChillPersonBundle/translations/messages.fr.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 557a6a24b..c94b4963d 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -339,8 +339,8 @@ Fields to include in export: Champs à inclure dans l'export Address valid at this date: Addresse valide à cette date List duplicates: Liste des doublons Create a list of duplicate people: Créer la liste des personnes détectées comme doublons. -Count people participating in an accompanying course by various parameters.: Nombre de personnes concernées par un parcours -Count people participating in an accompanying course: Nombre de personnes concernés par un parcours +Count people participating in an accompanying course: Nombre de personnes concernées par un parcours +Count people participating in an accompanying course by various parameters.: Compte le nombre de personnes concernées par un parcours Exports of accompanying courses: Exports des parcours d'accompagnement Count accompanying courses: Nombre de parcours From 9f2ecff63ed811983a438a2de4c0265dffaf715c Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Mon, 26 Sep 2022 18:52:00 +0200 Subject: [PATCH 093/120] exports: ResidentialAddressAt ThirdParty/User Filter, improve query (issue637 et issue638) --- .../ResidentialAddressAtThirdpartyFilter.php | 11 +++++++++-- .../ResidentialAddressAtUserFilter.php | 19 +++++++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php index c96fd680f..2770ae82f 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php @@ -56,10 +56,17 @@ class ResidentialAddressAtThirdpartyFilter implements FilterInterface } $where = $qb->getDQLPart('where'); + $clause = $qb->expr()->andX( $qb->expr()->isNotNull('resaddr.hostThirdParty'), - $qb->expr()->between(':date', 'resaddr.startDate', 'resaddr.endDate'), - $qb->expr()->in('tpartycat.id', ':type') + $qb->expr()->in('tpartycat.id', ':type'), + $qb->expr()->orX( + $qb->expr()->between(':date', 'resaddr.startDate', 'resaddr.endDate'), + $qb->expr()->andX( + $qb->expr()->lte('resaddr.startDate', ':date'), + $qb->expr()->isNull('resaddr.endDate') + ) + ) ); if ($where instanceof Expr\Andx) { diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php index 482312143..7c07121e1 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\PersonFilters; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Entity\Person\ResidentialAddress; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\Query\Expr; @@ -36,7 +37,17 @@ class ResidentialAddressAtUserFilter implements FilterInterface } $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->isNotNull('resaddr.hostPerson'); + + $clause = $qb->expr()->andX( + $qb->expr()->isNotNull('resaddr.hostPerson'), + $qb->expr()->orX( + $qb->expr()->between(':date', 'resaddr.startDate', 'resaddr.endDate'), + $qb->expr()->andX( + $qb->expr()->lte('resaddr.startDate', ':date'), + $qb->expr()->isNull('resaddr.endDate') + ) + ) + ); if ($where instanceof Andx) { $where->add($clause); @@ -44,6 +55,7 @@ class ResidentialAddressAtUserFilter implements FilterInterface $where = $qb->expr()->andX($clause); } + $qb->setParameter('date', $data['date_calc']); $qb->add('where', $where); } @@ -54,7 +66,10 @@ class ResidentialAddressAtUserFilter implements FilterInterface public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder) { - // No form needed unless validity date should be added ( not specified in doc as a parameter ). + $builder->add('date_calc', ChillDateType::class, [ + 'label' => 'Date during which residential address was valid', + 'data' => new \DateTime('now'), + ]); } public function describeAction($data, $format = 'string') From 49d2e98a1a82d8a25c1bed0a9f73b52b279fe8bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 26 Sep 2022 21:11:01 +0200 Subject: [PATCH 094/120] [person] Feature: add a person's center history The association between Person and Center is now stored in a dedicated Entity: `PersonCenterHistory`, which have a date interval (start date and endDate). The SQL counterpart is a table, with a constraint which ensure that no person might be associated with two center at the same time. For ease, a view is created to get the current center associated with the person. The dedicated migration creates also: * indexes for a rapid search for person at current date; * and populate the table from current data, setting the startdate to the person's creation date and time if any, `NOW()` unless. The `Person` entity is also updated to use the information from the PersonCenterHistory classes, but this commit does not yet delete the `Center` column. --- .../ChillPersonBundle/Entity/Person.php | 64 +++++++- .../Entity/Person/PersonCenterCurrent.php | 112 ++++++++++++++ .../Entity/Person/PersonCenterHistory.php | 142 ++++++++++++++++++ .../Person/PersonCenterHistoryInterface.php | 10 ++ .../Person/PersonCenterHistoryRepository.php | 48 ++++++ .../Tests/Entity/PersonTest.php | 26 ++++ .../migrations/Version20220926154347.php | 69 +++++++++ 7 files changed, 468 insertions(+), 3 deletions(-) create mode 100644 src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterCurrent.php create mode 100644 src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterHistory.php create mode 100644 src/Bundle/ChillPersonBundle/Repository/Person/PersonCenterHistoryInterface.php create mode 100644 src/Bundle/ChillPersonBundle/Repository/Person/PersonCenterHistoryRepository.php create mode 100644 src/Bundle/ChillPersonBundle/migrations/Version20220926154347.php diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index 3a197e414..5d9f6a2cb 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -27,6 +27,8 @@ use Chill\MainBundle\Validation\Constraint\PhonenumberConstraint; use Chill\PersonBundle\Entity\Household\Household; use Chill\PersonBundle\Entity\Household\HouseholdMember; use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress; +use Chill\PersonBundle\Entity\Person\PersonCenterCurrent; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Entity\Person\PersonCurrentAddress; use Chill\PersonBundle\Entity\Person\PersonResource; use Chill\PersonBundle\Validator\Constraints\Household\HouseholdMembershipSequential; @@ -180,9 +182,21 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI * The person's center. * * @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Center") + * @deprecated */ private ?Center $center = null; + /** + * @ORM\OneToMany(targetEntity=PersonCenterHistory::class, mappedBy="person") + * @var Collection|PersonCenterHistory[] + */ + private Collection $centerHistory; + + /** + * @ORM\OneToOne(targetEntity=PersonCenterCurrent::class, mappedBy="person") + */ + private ?PersonCenterCurrent $centerCurrent = null; + /** * Array where customfield's data are stored. * @@ -523,6 +537,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI $this->budgetResources = new ArrayCollection(); $this->budgetCharges = new ArrayCollection(); $this->resources = new ArrayCollection(); + $this->centerHistory = new ArrayCollection(); } /** @@ -897,7 +912,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI public function getCenter(): ?Center { - return $this->center; + return null === $this->centerCurrent ? null : $this->centerCurrent->getCenter(); } public function getCFData(): ?array @@ -1510,17 +1525,60 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI return $this; } + /** + * Associate the center with the person. The association start on 'now'. + * + * @param Center $center + * @return $this + */ public function setCenter(Center $center): self { $this->center = $center; + $modification = new DateTimeImmutable('now'); + + foreach ($this->centerHistory as $centerHistory) { + if (null === $centerHistory->getEndDate()) { + $centerHistory->setEndDate($modification); + } + } + + $this->centerHistory[] = $new = new PersonCenterHistory($this, $center, $modification); + $this->centerCurrent = new PersonCenterCurrent($new); + return $this; } /** - * @return Report + * @return Collection */ - public function setCFData(?array $cFData) + public function getCenterHistory(): Collection + { + return $this->centerHistory; + } + + /** + * @param Collection $centerHistory + * @return Person + */ + public function setCenterHistory(Collection $centerHistory): Person + { + $this->centerHistory = $centerHistory; + return $this; + } + + /** + * @return PersonCenterCurrent|null + */ + public function getCenterCurrent(): ?PersonCenterCurrent + { + return $this->centerCurrent; + } + + /** + * @return Person + */ + public function setCFData(?array $cFData): self { $this->cFData = $cFData; diff --git a/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterCurrent.php b/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterCurrent.php new file mode 100644 index 000000000..690ff025e --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterCurrent.php @@ -0,0 +1,112 @@ +person = $history->getPerson(); + $this->center = $history->getCenter(); + $this->startDate = $history->getStartDate(); + $this->endDate = $history->getEndDate(); + $this->id = $history->getId(); + } + + /** + * The id will be the same as the current @link{PersonCenterHistory::class} + * + * @return int + */ + public function getId(): int + { + return $this->id; + } + + /** + * @return Person + */ + public function getPerson(): Person + { + return $this->person; + } + + /** + * @return Center + */ + public function getCenter(): Center + { + return $this->center; + } + + /** + * @return \DateTimeImmutable + */ + public function getStartDate(): \DateTimeImmutable + { + return $this->startDate; + } + + /** + * @return \DateTimeImmutable|null + */ + public function getEndDate(): ?\DateTimeImmutable + { + return $this->endDate; + } + +} diff --git a/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterHistory.php b/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterHistory.php new file mode 100644 index 000000000..6bfb28cc6 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterHistory.php @@ -0,0 +1,142 @@ +person = $person; + $this->center = $center; + $this->startDate = $startDate; + } + + + /** + * @return int|null + */ + public function getId(): ?int + { + return $this->id; + } + + /** + * @return Person|null + */ + public function getPerson(): ?Person + { + return $this->person; + } + + /** + * @param Person|null $person + */ + public function setPerson(?Person $person): self + { + $this->person = $person; + + return $this; + } + + /** + * @return Center|null + */ + public function getCenter(): ?Center + { + return $this->center; + } + + /** + * @param Center|null $center + */ + public function setCenter(?Center $center): self + { + $this->center = $center; + + return $this; + } + + /** + * @return \DateTimeImmutable|null + */ + public function getStartDate(): ?\DateTimeImmutable + { + return $this->startDate; + } + + /** + * @param \DateTimeImmutable|null $startDate + */ + public function setStartDate(?\DateTimeImmutable $startDate): self + { + $this->startDate = $startDate; + return $this; + } + + /** + * @return \DateTimeImmutable|null + */ + public function getEndDate(): ?\DateTimeImmutable + { + return $this->endDate; + } + + /** + * @param \DateTimeImmutable|null $endDate + */ + public function setEndDate(?\DateTimeImmutable $endDate): self + { + $this->endDate = $endDate; + + return $this; + } +} diff --git a/src/Bundle/ChillPersonBundle/Repository/Person/PersonCenterHistoryInterface.php b/src/Bundle/ChillPersonBundle/Repository/Person/PersonCenterHistoryInterface.php new file mode 100644 index 000000000..089add06a --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Repository/Person/PersonCenterHistoryInterface.php @@ -0,0 +1,10 @@ +repository = $em->getRepository($this->getClassName()); + } + + public function find($id): ?PersonCenterHistory + { + return $this->repository->find($id); + } + + /** + * @return array|PersonCenterHistory[] + */ + public function findAll(): array + { + return $this->repository->findAll(); + } + + /** + * @return array|PersonCenterHistory[] + */ + 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): ?PersonCenterHistory + { + return $this->repository->findOneBy($criteria); + } + + public function getClassName(): string + { + return PersonCenterHistory::class; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Entity/PersonTest.php b/src/Bundle/ChillPersonBundle/Tests/Entity/PersonTest.php index 18149c824..4633c3fcb 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Entity/PersonTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Entity/PersonTest.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Entity; +use Chill\MainBundle\Entity\Center; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\Household\Household; use Chill\PersonBundle\Entity\Household\HouseholdMember; @@ -191,4 +192,29 @@ final class PersonTest extends \PHPUnit\Framework\TestCase $this->assertEquals($r['result'], Person::ERROR_ADDIND_PERIOD_AFTER_AN_OPEN_PERIOD); } + + public function testSetCenter() + { + $person = new Person(); + $centerA = new Center(); + $centerB = new Center(); + + $this->assertCount(0, $person->getCenterHistory()); + $this->assertNull($person->getCenter()); + $this->assertNull($person->getCenterCurrent()); + + $person->setCenter($centerA); + + $this->assertCount(1, $person->getCenterHistory()); + $this->assertSame($centerA, $person->getCenter()); + $this->assertInstanceOf(Person\PersonCenterCurrent::class, $person->getCenterCurrent()); + $this->assertSame($centerA, $person->getCenterCurrent()->getCenter()); + + $person->setCenter($centerB); + + $this->assertCount(2, $person->getCenterHistory()); + $this->assertSame($centerB, $person->getCenter()); + $this->assertInstanceOf(Person\PersonCenterCurrent::class, $person->getCenterCurrent()); + $this->assertSame($centerB, $person->getCenterCurrent()->getCenter()); + } } diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20220926154347.php b/src/Bundle/ChillPersonBundle/migrations/Version20220926154347.php new file mode 100644 index 000000000..b308c22e3 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/migrations/Version20220926154347.php @@ -0,0 +1,69 @@ +addSql('CREATE SEQUENCE chill_person_person_center_history_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); + $this->addSql('CREATE TABLE chill_person_person_center_history ( + id INT NOT NULL, person_id INT DEFAULT NULL, center_id INT DEFAULT NULL, + startDate DATE NOT NULL, endDate DATE DEFAULT NULL, createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, + updatedAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, createdBy_id INT DEFAULT NULL, + updatedBy_id INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_CACA67FA217BBB47 ON chill_person_person_center_history (person_id)'); + $this->addSql('CREATE INDEX IDX_CACA67FA5932F377 ON chill_person_person_center_history (center_id)'); + $this->addSql('CREATE INDEX IDX_CACA67FA3174800F ON chill_person_person_center_history (createdBy_id)'); + $this->addSql('CREATE INDEX IDX_CACA67FA65FF1AEC ON chill_person_person_center_history (updatedBy_id)'); + $this->addSql('COMMENT ON COLUMN chill_person_person_center_history.startDate IS \'(DC2Type:date_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_person_person_center_history.endDate IS \'(DC2Type:date_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_person_person_center_history.createdAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_person_person_center_history.updatedAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER TABLE chill_person_person_center_history ADD CONSTRAINT FK_CACA67FA217BBB47 FOREIGN KEY (person_id) REFERENCES chill_person_person (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_person_person_center_history ADD CONSTRAINT FK_CACA67FA5932F377 FOREIGN KEY (center_id) REFERENCES centers (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_person_person_center_history ADD CONSTRAINT FK_CACA67FA3174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_person_person_center_history ADD CONSTRAINT FK_CACA67FA65FF1AEC FOREIGN KEY (updatedBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + + // check consistency of history on database side + $this->addSql('ALTER TABLE chill_person_person_center_history ADD CHECK (startdate <= enddate)'); + $this->addSql('ALTER TABLE chill_person_person_center_history ADD CONSTRAINT ' . + 'chill_internal_person_person_center_history_not_overlaps EXCLUDE USING GIST( + -- extension btree_gist required to include comparaison with integer + person_id WITH =, + daterange(startdate, enddate, \'[)\') WITH && + ) + INITIALLY DEFERRED'); + + // create index on search by person and date + $this->addSql('CREATE INDEX chill_internal_person_person_center_history_by_date ON chill_person_person_center_history (person_id DESC, startdate DESC, enddate DESC NULLS FIRST)'); + + // create a view to get the current center on a person + $this->addSql( + 'CREATE VIEW view_chill_person_person_center_history_current AS + SELECT id, person_id, center_id, startDate, endDate, createdAt, updatedAt, createdBy_id, updatedBy_id + FROM chill_person_person_center_history WHERE startDate <= NOW() AND (enddate IS NULL OR enddate > NOW())' + ); + + $this->addSql('INSERT INTO chill_person_person_center_history (id, person_id, center_id, startdate) + SELECT nextval(\'chill_person_person_center_history_id_seq\'), id, center_id, COALESCE(createdat, NOW()) + FROM chill_person_person WHERE center_id IS NOT NULL'); + } + + public function down(Schema $schema): void + { + $this->addSql('DROP VIEW view_chill_person_person_center_history_current'); + $this->addSql('DROP SEQUENCE chill_person_person_center_history_id_seq CASCADE'); + $this->addSql('DROP TABLE chill_person_person_center_history'); + } +} From 451f7f4230d907928966c05fc6e62fca96d9fd72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 27 Sep 2022 09:41:41 +0200 Subject: [PATCH 095/120] [person][Search] Feature: use the current center history when searching for person --- .../Repository/PersonACLAwareRepository.php | 8 +- .../PersonACLAwareRepositoryTest.php | 79 +++++++++++++++++++ 2 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 src/Bundle/ChillPersonBundle/Tests/Repository/PersonACLAwareRepositoryTest.php diff --git a/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php b/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php index d5dc3cd7d..96662b66c 100644 --- a/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php @@ -16,6 +16,7 @@ use Chill\MainBundle\Repository\CountryRepository; use Chill\MainBundle\Search\ParsingException; use Chill\MainBundle\Search\SearchApiQuery; use Chill\MainBundle\Security\Authorization\AuthorizationHelper; +use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Security\Authorization\PersonVoter; use DateTimeInterface; @@ -34,7 +35,7 @@ use function implode; final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterface { - private AuthorizationHelper $authorizationHelper; + private AuthorizationHelperInterface $authorizationHelper; private CountryRepository $countryRepository; @@ -46,7 +47,7 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac Security $security, EntityManagerInterface $em, CountryRepository $countryRepository, - AuthorizationHelper $authorizationHelper + AuthorizationHelperInterface $authorizationHelper ) { $this->security = $security; $this->em = $em; @@ -310,9 +311,10 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac } return $query + ->setFromClause($query->getFromClause() . ' JOIN view_chill_person_person_center_history_current vcppchc ON vcppchc.person_id = person.id', $query->getFromParams()) ->andWhereClause( strtr( - 'person.center_id IN ({{ center_ids }})', + 'vcppchc.center_id IN ({{ center_ids }})', [ '{{ center_ids }}' => implode( ', ', diff --git a/src/Bundle/ChillPersonBundle/Tests/Repository/PersonACLAwareRepositoryTest.php b/src/Bundle/ChillPersonBundle/Tests/Repository/PersonACLAwareRepositoryTest.php new file mode 100644 index 000000000..4540e3716 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Repository/PersonACLAwareRepositoryTest.php @@ -0,0 +1,79 @@ +entityManager = self::$container->get(EntityManagerInterface::class); + $this->countryRepository = self::$container->get(CountryRepository::class); + $this->centerRepository = self::$container->get(CenterRepositoryInterface::class); + + } + + public function testCountByCriteria() + { + $user = new User(); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableCenters(Argument::exact($user), Argument::exact(PersonVoter::SEE)) + ->willReturn($this->centerRepository->findAll()); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $repository = new PersonACLAwareRepository($security->reveal(), $this->entityManager, $this->countryRepository, + $authorizationHelper->reveal()); + + $number = $repository->countBySearchCriteria('diallo'); + + $this->assertGreaterThan(0, $number); + } + + public function testFindByCriteria() + { + $user = new User(); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableCenters(Argument::exact($user), Argument::exact(PersonVoter::SEE)) + ->willReturn($this->centerRepository->findAll()); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $repository = new PersonACLAwareRepository($security->reveal(), $this->entityManager, $this->countryRepository, + $authorizationHelper->reveal()); + + $results = $repository->findBySearchCriteria(0, 5, false, 'diallo'); + + $this->assertGreaterThan(0, count($results)); + $this->assertContainsOnlyInstancesOf(Person::class, $results); + foreach ($results as $person) { + $this->assertStringContainsString('diallo', strtolower($person->getFirstName() . ' ' . $person->getLastName())); + } + } +} From 1386ae66debf1a90f2cb65a068e945583addc6b1 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Tue, 27 Sep 2022 15:35:45 +0200 Subject: [PATCH 096/120] centerDispatchResolver added in construct - was missing --- src/Bundle/ChillDocStoreBundle/Form/PersonDocumentType.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Bundle/ChillDocStoreBundle/Form/PersonDocumentType.php b/src/Bundle/ChillDocStoreBundle/Form/PersonDocumentType.php index 3df9f261f..f7abd93dd 100644 --- a/src/Bundle/ChillDocStoreBundle/Form/PersonDocumentType.php +++ b/src/Bundle/ChillDocStoreBundle/Form/PersonDocumentType.php @@ -41,11 +41,13 @@ class PersonDocumentType extends AbstractType public function __construct( TranslatableStringHelperInterface $translatableStringHelper, ScopeResolverDispatcher $scopeResolverDispatcher, - ParameterBagInterface $parameterBag + ParameterBagInterface $parameterBag, + CenterResolverDispatcher $centerResolverDispatcher ) { $this->translatableStringHelper = $translatableStringHelper; $this->scopeResolverDispatcher = $scopeResolverDispatcher; $this->parameterBag = $parameterBag; + $this->centerResolverDispatcher = $centerResolverDispatcher; } public function buildForm(FormBuilderInterface $builder, array $options) From f39b0ee0029324fd2a8614b6aac61187c4c7ba23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 27 Sep 2022 16:09:09 +0200 Subject: [PATCH 097/120] [task] Feature: use the resolution of center with person's center history --- .../SingleTaskAclAwareRepository.php | 32 ++- .../SingleTaskACLAwareRepositoryTest.php | 211 ++++++++++++++++++ 2 files changed, 231 insertions(+), 12 deletions(-) create mode 100644 src/Bundle/ChillTaskBundle/Tests/Repository/SingleTaskACLAwareRepositoryTest.php diff --git a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php index d2dd7fc18..dca83eb71 100644 --- a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php +++ b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php @@ -13,6 +13,7 @@ namespace Chill\TaskBundle\Repository; use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface; +use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\Person; use Chill\TaskBundle\Entity\SingleTask; @@ -31,14 +32,14 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository { private AuthorizationHelperInterface $authorizationHelper; - private CenterResolverDispatcherInterface $centerResolverDispatcher; + private CenterResolverManagerInterface $centerResolverDispatcher; private EntityManagerInterface $em; private Security $security; public function __construct( - CenterResolverDispatcherInterface $centerResolverDispatcher, + CenterResolverManagerInterface $centerResolverDispatcher, EntityManagerInterface $em, Security $security, AuthorizationHelperInterface $authorizationHelper @@ -304,14 +305,18 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository QueryBuilder $qb, $entity ): QueryBuilder { - $scopes = $this->authorizationHelper->getReachableScopes( - $this->security->getUser(), - TaskVoter::SHOW, - $this->centerResolverDispatcher->resolveCenter($entity) - ); + foreach ($this->centerResolverDispatcher->resolveCenters($entity) as $center) { + $scopes = $this->authorizationHelper->getReachableScopes( + $this->security->getUser(), + TaskVoter::SHOW, + $center + ); - return $qb->andWhere($qb->expr()->in('t.circle', ':scopes')) - ->setParameter('scopes', $scopes); + $qb->andWhere($qb->expr()->in('t.circle', ':scopes')) + ->setParameter('scopes', $scopes); + } + + return $qb; } private function addACLGlobal( @@ -329,7 +334,10 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository $qb->leftJoin('t.person', 'person') ->leftJoin('t.course', 'course') ->leftJoin('course.participations', 'participation') - ->leftJoin('participation.person', 'person_p'); + ->leftJoin('participation.person', 'person_p') + ->leftJoin('person.centerCurrent', 'center_current_person') + ->leftJoin('person_p.centerCurrent', 'center_current_participation') + ; $qb->distinct(true); $k = 0; @@ -344,8 +352,8 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository $and = $qb->expr()->andX( $qb->expr()->orX( - $qb->expr()->eq('person.center', ':center_' . $k), - $qb->expr()->eq('person_p.center', ':center_' . $k) + $qb->expr()->eq('center_current_person.center', ':center_' . $k), + $qb->expr()->eq('center_current_participation.center', ':center_' . $k) ), $qb->expr()->in('t.circle', ':scopes_' . $k) ); diff --git a/src/Bundle/ChillTaskBundle/Tests/Repository/SingleTaskACLAwareRepositoryTest.php b/src/Bundle/ChillTaskBundle/Tests/Repository/SingleTaskACLAwareRepositoryTest.php new file mode 100644 index 000000000..f4c54b6cc --- /dev/null +++ b/src/Bundle/ChillTaskBundle/Tests/Repository/SingleTaskACLAwareRepositoryTest.php @@ -0,0 +1,211 @@ +em = self::$container->get(EntityManagerInterface::class); + $this->userRepository = self::$container->get(UserRepository::class); + $this->centerRepository = self::$container->get(CenterRepositoryInterface::class); + $this->scopeRepository = self::$container->get(ScopeRepository::class); + $this->personRepository = self::$container->get(PersonRepository::class); + } + + public function testCountByPerson(): void + { + $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); + $user = new User(); + $scopes = $this->scopeRepository->findAll(); + $person = $this->getRandomPerson($this->em); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); + $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any()) + ->willReturn([$centerA]); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA)) + ->willReturn($scopes); + + $repository = new SingleTaskAclAwareRepository( + $centerResolverDispatcher->reveal(), + $this->em, + $security->reveal(), + $authorizationHelper->reveal() + ); + + $nb = $repository->countByPerson($person, null, []); + + $this->assertGreaterThanOrEqual(0, $nb); + } + + public function testFindByPerson(): void + { + $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); + $user = new User(); + $scopes = $this->scopeRepository->findAll(); + $person = $this->getRandomPerson($this->em); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); + $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any()) + ->willReturn([$centerA]); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA)) + ->willReturn($scopes); + + $repository = new SingleTaskAclAwareRepository( + $centerResolverDispatcher->reveal(), + $this->em, + $security->reveal(), + $authorizationHelper->reveal() + ); + + $tasks = $repository->findByPerson($person, null, []); + + $this->assertGreaterThanOrEqual(0, count($tasks)); + } + + public function testFindByAllViewable(): void + { + $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); + $user = new User(); + $scopes = $this->scopeRepository->findAll(); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); + $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any()) + ->willReturn([$centerA]); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableCenters(Argument::exact($user), Argument::exact(TaskVoter::SHOW)) + ->willReturn([$centerA]); + $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA)) + ->willReturn($scopes); + + $repository = new SingleTaskAclAwareRepository( + $centerResolverDispatcher->reveal(), + $this->em, + $security->reveal(), + $authorizationHelper->reveal() + ); + + $tasks = $repository->findByAllViewable(null, []); + + $this->assertGreaterThanOrEqual(0, count($tasks)); + } + + public function testCountByAllViewable(): void + { + $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); + $user = new User(); + $scopes = $this->scopeRepository->findAll(); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); + $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any()) + ->willReturn([$centerA]); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableCenters(Argument::exact($user), Argument::exact(TaskVoter::SHOW)) + ->willReturn([$centerA]); + $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA)) + ->willReturn($scopes); + + $repository = new SingleTaskAclAwareRepository( + $centerResolverDispatcher->reveal(), + $this->em, + $security->reveal(), + $authorizationHelper->reveal() + ); + + $nb = $repository->countByAllViewable(null, []); + + $this->assertGreaterThanOrEqual(0, $nb); + } + + public function testFindByCourse(): void + { + $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); + $user = new User(); + $scopes = $this->scopeRepository->findAll(); + /** @var Person $person */ + $person = $this->em->createQuery( + 'SELECT p FROM '.Person::class.' p JOIN p.centerCurrent cc + WHERE SIZE(p.accompanyingPeriodParticipations) > 0 + AND cc.center = :center' + ) + ->setParameter('center', $centerA) + ->setMaxResults(1) + ->getSingleResult() + ; + $period = $person->getAccompanyingPeriodParticipations()->first()->getAccompanyingPeriod(); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); + $centerResolverDispatcher->resolveCenters(Argument::type(AccompanyingPeriod::class), Argument::any()) + ->willReturn([$centerA]); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::any()) + ->willReturn($scopes); + + $repository = new SingleTaskAclAwareRepository( + $centerResolverDispatcher->reveal(), + $this->em, + $security->reveal(), + $authorizationHelper->reveal() + ); + + $tasks = $repository->findByCourse($period); + + $this->assertGreaterThanOrEqual(0, count($tasks)); + } +} \ No newline at end of file From 32f252149bd43a034eda338715692357477cb1b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 28 Sep 2022 15:43:31 +0200 Subject: [PATCH 098/120] [export] Feature: rely on person's center history to build export's query --- .../LinkedToACP/AvgActivityDuration.php | 18 +++++++++ .../LinkedToACP/AvgActivityVisitDuration.php | 18 +++++++++ .../Export/LinkedToACP/CountActivity.php | 24 ++++++++++-- .../LinkedToACP/SumActivityDuration.php | 24 ++++++++++-- .../LinkedToACP/SumActivityVisitDuration.php | 24 ++++++++++-- .../Export/LinkedToPerson/CountActivity.php | 21 +++++++--- .../LinkedToPerson/StatActivityDuration.php | 21 ++++++++-- .../Export/Export/CountAccompanyingCourse.php | 20 +++++++++- .../Export/Export/CountEvaluation.php | 18 +++++++++ .../Export/Export/CountHousehold.php | 39 ++++++++++++------- .../Export/Export/CountPerson.php | 11 ++++-- .../CountPersonWithAccompanyingCourse.php | 11 ++++++ .../Export/Export/CountSocialWorkActions.php | 25 +++++++++--- .../Export/StatAccompanyingCourseDuration.php | 24 +++++++++--- 14 files changed, 247 insertions(+), 51 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php index 3fc975147..f45adc0b3 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php @@ -17,6 +17,8 @@ use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; @@ -83,6 +85,10 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + $qb = $this->repository->createQueryBuilder('activity'); $qb @@ -90,6 +96,18 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface ->select('AVG(activity.durationTime) as export_avg_activity_duration') ->andWhere($qb->expr()->isNotNull('activity.durationTime')); + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part + JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers) + ; + return $qb; } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php index 69533a09d..661f169dd 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php @@ -17,6 +17,8 @@ use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; @@ -84,6 +86,10 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + $qb = $this->repository->createQueryBuilder('activity'); $qb @@ -92,6 +98,18 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac ->andWhere($qb->expr()->isNotNull('activity.travelTime')) ; + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part + JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers) + ; + return $qb; } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php index 2162b8b58..46d2f3c33 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php @@ -17,6 +17,8 @@ use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; @@ -84,11 +86,25 @@ class CountActivity implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { - $qb = $this->repository->createQueryBuilder('activity'); + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); - if (!in_array('acp', $qb->getAllAliases(), true)) { - $qb->join('activity.accompanyingPeriod', 'acp'); - } + $qb = $this->repository + ->createQueryBuilder('activity') + ->join('activity.accompanyingPeriod', 'acp'); + + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part + JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers) + ; $qb->select('COUNT(DISTINCT activity.id) as export_count_activity'); diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php index e9dbaff29..405b54885 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php @@ -17,6 +17,8 @@ use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; @@ -84,15 +86,29 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { - $qb = $this->repository->createQueryBuilder('activity'); + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); - if (!in_array('acp', $qb->getAllAliases(), true)) { - $qb->join('activity.accompanyingPeriod', 'acp'); - } + $qb = $this->repository + ->createQueryBuilder('activity') + ->join('activity.accompanyingPeriod', 'acp'); $qb->select('SUM(activity.durationTime) as export_sum_activity_duration') ->andWhere($qb->expr()->isNotNull('activity.durationTime')); + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part + JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers) + ; + return $qb; } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php index d41994562..40020cb4b 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php @@ -17,6 +17,8 @@ use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; @@ -84,15 +86,29 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { - $qb = $this->repository->createQueryBuilder('activity'); + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); - if (!in_array('acp', $qb->getAllAliases(), true)) { - $qb->join('activity.accompanyingPeriod', 'acp'); - } + $qb = $this->repository + ->createQueryBuilder('activity') + ->join('activity.accompanyingPeriod', 'acp'); $qb->select('SUM(activity.travelTime) as export_sum_activity_visit_duration') ->andWhere($qb->expr()->isNotNull('activity.travelTime')); + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part + JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers) + ; + return $qb; } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php index f12507df8..116fa4b6a 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php @@ -84,16 +84,25 @@ class CountActivity implements ExportInterface, GroupedExportInterface { $centers = array_map(static fn ($el) => $el['center'], $acl); - $qb = $this->activityRepository->createQueryBuilder('activity'); - - if (!in_array('person', $qb->getAllAliases(), true)) { - $qb->join('activity.person', 'person'); - } + $qb = $this->activityRepository + ->createQueryBuilder('activity') + ->join('activity.person', 'person') + ->join('person.centerHistory', 'centerHistory') + ; $qb->select('COUNT(activity.id) as export_count_activity'); $qb - ->where($qb->expr()->in('person.center', ':centers')) + ->where( + $qb->expr()->andX( + $qb->expr()->lte('centerHistory.startDate', 'activity.date'), + $qb->expr()->orX( + $qb->expr()->isNull('centerHistory.endDate'), + $qb->expr()->gt('centerHistory.endDate', 'activity.date') + ) + ) + ) + ->andWhere($qb->expr()->in('centerHistory.center', ':centers')) ->setParameter('centers', $centers); return $qb; diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php index 9013b4648..f1228842f 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php @@ -119,11 +119,24 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface $select = 'SUM(activity.durationTime) AS export_stat_activity'; } - return $qb->select($select) + $qb->select($select) ->join('activity.person', 'person') - ->join('actperson.center', 'actcenter') - ->where($qb->expr()->in('actcenter', ':centers')) - ->setParameter(':centers', $centers); + ->join('person.centerHistory', 'centerHistory'); + + $qb + ->where( + $qb->expr()->andX( + $qb->expr()->lte('centerHistory.startDate', 'activity.date'), + $qb->expr()->orX( + $qb->expr()->isNull('centerHistory.endDate'), + $qb->expr()->gt('centerHistory.endDate', 'activity.date') + ) + ) + ) + ->andWhere($qb->expr()->in('centerHistory.center', ':centers')) + ->setParameter('centers', $centers); + + return $qb; } public function requiredRole(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php index 11a10436e..02682c3a9 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php @@ -15,6 +15,8 @@ use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Doctrine\ORM\EntityManagerInterface; @@ -90,9 +92,25 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + $qb = $this->repository->createQueryBuilder('acp'); - $qb->select('COUNT(acp.id) AS export_result'); + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part + JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers) + ; + + $qb->select('COUNT(DISTINCT acp.id) AS export_result'); return $qb; } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php b/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php index 634b351cf..7ff5349f6 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php @@ -15,6 +15,8 @@ use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Doctrine\ORM\EntityManagerInterface; @@ -88,6 +90,10 @@ class CountEvaluation implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + $qb = $this->repository->createQueryBuilder('acp'); if (!in_array('acpw', $qb->getAllAliases(), true)) { @@ -98,6 +104,18 @@ class CountEvaluation implements ExportInterface, GroupedExportInterface $qb->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval'); } + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part + JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers) + ; + $qb->select('COUNT(DISTINCT workeval.id) AS export_result'); return $qb; diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php index 106084e4f..e1c7adf88 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php @@ -15,6 +15,9 @@ use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Household\HouseholdMember; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\HouseholdVoter; use Doctrine\ORM\EntityManagerInterface; @@ -88,23 +91,29 @@ class CountHousehold implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { - $qb = $this->repository->createQueryBuilder('acp'); + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); - if (!in_array('acppart', $qb->getAllAliases(), true)) { - $qb->join('acp.participations', 'acppart'); - } + $qb = $this->repository + ->createQueryBuilder('acp') + ->join('acp.participations', 'acppart') + // little optimization: we remove joins and make a direct join between participations and household members + ->join(HouseholdMember::class, 'member', Query\Expr\Join::WITH, 'IDENTITY(acppart.person) = IDENTITY(member.person)') + ->join('member.household', 'household') + ; - if (!in_array('partperson', $qb->getAllAliases(), true)) { - $qb->join('acppart.person', 'partperson'); - } - - if (!in_array('member', $qb->getAllAliases(), true)) { - $qb->join('partperson.householdParticipations', 'member'); - } - - if (!in_array('household', $qb->getAllAliases(), true)) { - $qb->join('member.household', 'household'); - } + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part + JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers) + ; $qb->select('COUNT(DISTINCT household.id) AS export_result'); diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php index 18cefc6bb..3b2e47bdd 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php @@ -14,9 +14,11 @@ namespace Chill\PersonBundle\Export\Export; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Repository\PersonRepository; use Chill\PersonBundle\Security\Authorization\PersonVoter; +use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query; use Doctrine\ORM\QueryBuilder; use LogicException; @@ -100,9 +102,12 @@ class CountPerson implements ExportInterface, GroupedExportInterface $qb = $this->personRepository->createQueryBuilder('person'); $qb->select('COUNT(person.id) AS export_result') - ->join('person.center', 'center') - ->andWhere('center IN (:authorized_centers)') - ->setParameter('authorized_centers', $centers); + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.PersonCenterHistory::class.' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)' + ) + ) + ->setParameter('authorized_centers', $centers); return $qb; } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php b/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php index 057611032..fa28e2abe 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php @@ -15,6 +15,7 @@ use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Doctrine\ORM\EntityManagerInterface; @@ -88,6 +89,10 @@ class CountPersonWithAccompanyingCourse implements ExportInterface, GroupedExpor public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + $qb = $this->repository->createQueryBuilder('acp'); if (!in_array('acppart', $qb->getAllAliases(), true)) { @@ -98,6 +103,12 @@ class CountPersonWithAccompanyingCourse implements ExportInterface, GroupedExpor $qb->join('acppart.person', 'person'); } + $qb->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.PersonCenterHistory::class.' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)' + ) + )->setParameter('authorized_centers', $centers); + $qb->select('COUNT(DISTINCT person.id) AS export_result'); return $qb; diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php b/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php index 2ad6e9f1b..4ba071d3b 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php @@ -15,6 +15,8 @@ use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Doctrine\ORM\EntityManagerInterface; @@ -90,13 +92,26 @@ class CountSocialWorkActions implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder { - $qb = $this->repository->createQueryBuilder('acp'); + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); - if (!in_array('acpw', $qb->getAllAliases(), true)) { - $qb->join('acp.works', 'acpw'); - } + $qb = $this->repository->createQueryBuilder('acp') + ->join('acp.works', 'acpw'); - $qb->select('COUNT(acpw.id) as export_result'); + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part + JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers) + ; + + $qb->select('COUNT(DISTINCT acpw.id) as export_result'); return $qb; } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php b/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php index b805005be..3b2036797 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php @@ -16,6 +16,8 @@ use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; +use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use DateTime; @@ -94,15 +96,27 @@ class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportIn public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + $qb = $this->repository->createQueryBuilder('acp'); + $qb + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part + JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('authorized_centers', $centers) + ; + $qb ->select('AVG( - ( CASE - WHEN acp.closingDate IS NOT NULL - THEN acp.closingDate - ELSE :force_closingDate - END ) - acp.openingDate + COALESCE(acp.closingDate, :force_closingDate) - acp.openingDate ) AS export_result') ->setParameter('force_closingDate', $data['closingdate']); From a91b35298a6a187ff7926b0a182b5b4bdcfbce55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 28 Sep 2022 15:51:11 +0200 Subject: [PATCH 099/120] Do use the old name for the table, as it should exists when migrations are executed sequentially --- .../ChillEventBundle/migrations/Version20160318111334.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bundle/ChillEventBundle/migrations/Version20160318111334.php b/src/Bundle/ChillEventBundle/migrations/Version20160318111334.php index e879f1d29..241003abf 100644 --- a/src/Bundle/ChillEventBundle/migrations/Version20160318111334.php +++ b/src/Bundle/ChillEventBundle/migrations/Version20160318111334.php @@ -126,7 +126,7 @@ class Version20160318111334 extends AbstractMigration $this->addSql('ALTER TABLE chill_event_participation ' . 'ADD CONSTRAINT FK_4E7768AC217BBB47 ' . 'FOREIGN KEY (person_id) ' - . 'REFERENCES chill_person_person(id) ' + . 'REFERENCES Person (id) ' . 'NOT DEFERRABLE INITIALLY IMMEDIATE'); $this->addSql('ALTER TABLE chill_event_participation ' . 'ADD CONSTRAINT FK_4E7768ACD60322AC ' From 230948469273886869ce0f0fe3ef789a865b5266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 28 Sep 2022 17:21:39 +0200 Subject: [PATCH 100/120] Fixed: cascade persist the center history when a person is created --- src/Bundle/ChillPersonBundle/Entity/Person.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index 5d9f6a2cb..bf6a283c1 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -187,7 +187,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI private ?Center $center = null; /** - * @ORM\OneToMany(targetEntity=PersonCenterHistory::class, mappedBy="person") + * @ORM\OneToMany(targetEntity=PersonCenterHistory::class, mappedBy="person", cascade={"persist"}) * @var Collection|PersonCenterHistory[] */ private Collection $centerHistory; From ac39baa5f53ddfe0ef7649cee4ac6b9a5e64ec89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 29 Sep 2022 18:21:14 +0200 Subject: [PATCH 101/120] Fixed: avoid the requirement to store person current center, which is a readonly entity --- .../ChillPersonBundle/Entity/Person.php | 47 +++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index bf6a283c1..dc311ad07 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -910,9 +910,43 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI return $this->budgetResources; } + private function getCurrentCenterHistory(): ?PersonCenterHistory + { + if (0 === $this->centerHistory->count()) { + return null; + } + + $criteria = Criteria::create(); + $now = new DateTimeImmutable('now'); + $criteria->where(Criteria::expr()->lte('startDate', $now)) + ->andWhere(Criteria::expr()->orX( + Criteria::expr()->isNull('endDate'), + Criteria::expr()->gt('endDate', $now) + )); + + $histories = $this->centerHistory->matching($criteria); + + switch ($histories->count()) { + case 0: + return null; + case 1: + return $histories->first(); + default: + throw new \UnexpectedValueException('It should not contains more than one center at a time'); + } + } + public function getCenter(): ?Center { - return null === $this->centerCurrent ? null : $this->centerCurrent->getCenter(); + if (null !== $this->centerCurrent) { + return $this->centerCurrent->getCenter(); + } + + if (null === $currentCenterHistory = $this->getCurrentCenterHistory()) { + return null; + } + + return $currentCenterHistory->getCenter(); } public function getCFData(): ?array @@ -1544,7 +1578,6 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI } $this->centerHistory[] = $new = new PersonCenterHistory($this, $center, $modification); - $this->centerCurrent = new PersonCenterCurrent($new); return $this; } @@ -1572,7 +1605,15 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI */ public function getCenterCurrent(): ?PersonCenterCurrent { - return $this->centerCurrent; + if (null !== $this->centerCurrent) { + return $this->centerCurrent; + } + + if (null === $currentCenterHistory = $this->getCurrentCenterHistory()) { + return null; + } + + return new PersonCenterCurrent($currentCenterHistory); } /** From 8d1160d09364e951cd392beba6effe7567c40051 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Thu, 29 Sep 2022 18:44:36 +0200 Subject: [PATCH 102/120] delete duplicate AggregatorTest --- .../ActivityReasonAggregatorTest.php | 82 ------------------- .../ActivityReasonAggregatorTest.php | 34 +++++--- 2 files changed, 23 insertions(+), 93 deletions(-) delete mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityReasonAggregatorTest.php diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityReasonAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityReasonAggregatorTest.php deleted file mode 100644 index 79c0924f7..000000000 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityReasonAggregatorTest.php +++ /dev/null @@ -1,82 +0,0 @@ -getContainer(); - - $this->aggregator = $container->get('chill.activity.export.reason_aggregator'); - - // add a fake request with a default locale (used in translatable string) - $prophet = new \Prophecy\Prophet(); - $request = $prophet->prophesize(); - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - - $container->get('request_stack') - ->push($request->reveal()); - } - - public function getAggregator() - { - return $this->aggregator; - } - - public function getFormData() - { - return [ - ['level' => 'reasons'], - ['level' => 'categories'], - ]; - } - - public function getQueryBuilders() - { - if (null === self::$kernel) { - self::bootKernel(); - } - - $em = self::$kernel->getContainer() - ->get('doctrine.orm.entity_manager'); - - return [ - $em->createQueryBuilder() - ->select('count(activity.id)') - ->from('ChillActivityBundle:Activity', 'activity'), - $em->createQueryBuilder() - ->select('count(activity.id)') - ->from('ChillActivityBundle:Activity', 'activity') - ->join('activity.reasons', 'reasons'), - $em->createQueryBuilder() - ->select('count(activity.id)') - ->from('ChillActivityBundle:Activity', 'activity') - ->join('activity.reasons', 'reasons') - ->join('reasons.category', 'category'), - ]; - } -} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/PersonAggregators/ActivityReasonAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/PersonAggregators/ActivityReasonAggregatorTest.php index 6de364ae7..f89c20e31 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/PersonAggregators/ActivityReasonAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/PersonAggregators/ActivityReasonAggregatorTest.php @@ -15,9 +15,12 @@ use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityReasonAggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Doctrine\ORM\EntityManagerInterface; +use Prophecy\PhpUnit\ProphecyTrait; final class ActivityReasonAggregatorTest extends AbstractAggregatorTest { + use ProphecyTrait; + private ActivityReasonAggregator $aggregator; protected function setUp(): void @@ -25,6 +28,14 @@ final class ActivityReasonAggregatorTest extends AbstractAggregatorTest self::bootKernel(); $this->aggregator = self::$container->get('chill.activity.export.reason_aggregator'); + + $request = $this->prophesize() + ->willExtend(\Symfony\Component\HttpFoundation\Request::class); + + $request->getLocale()->willReturn('fr'); + + self::$container->get('request_stack') + ->push($request->reveal()); } public function getAggregator() @@ -35,12 +46,8 @@ final class ActivityReasonAggregatorTest extends AbstractAggregatorTest public function getFormData(): array { return [ - [ - 'level' => 'reasons', - ], - [ - 'level' => 'categories', - ] + ['level' => 'reasons'], + ['level' => 'categories'], ]; } @@ -55,11 +62,16 @@ final class ActivityReasonAggregatorTest extends AbstractAggregatorTest return [ $em->createQueryBuilder() ->select('count(activity.id)') - ->from(Activity::class, 'activity') - ->join('activity.person', 'actperson') - ->innerJoin('activity.reasons', 'actreasons') - ->join('actreasons.category', 'actreasoncat') - , + ->from('ChillActivityBundle:Activity', 'activity'), + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from('ChillActivityBundle:Activity', 'activity') + ->join('activity.reasons', 'actreasons'), + $em->createQueryBuilder() + ->select('count(activity.id)') + ->from('ChillActivityBundle:Activity', 'activity') + ->join('activity.reasons', 'actreasons') + ->join('actreasons.category', 'actreasoncat'), ]; } } From 2b8fe462eafdad45f128f31c571fc58ab7a21fab Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Thu, 29 Sep 2022 19:10:37 +0200 Subject: [PATCH 103/120] repair Activity Aggregator Tests --- .../Aggregator/ActivityTypeAggregatorTest.php | 27 +++++++--------- .../Aggregator/ActivityUserAggregatorTest.php | 31 +++++++------------ 2 files changed, 22 insertions(+), 36 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityTypeAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityTypeAggregatorTest.php index bd88a7d3b..435d1043e 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityTypeAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityTypeAggregatorTest.php @@ -11,8 +11,10 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Tests\Export\Aggregator; -use Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityTypeAggregator; +use Chill\ActivityBundle\Export\Aggregator\ActivityTypeAggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Doctrine\ORM\EntityManagerInterface; +use Prophecy\PhpUnit\ProphecyTrait; /** * Add tests for ActivityTypeAggregator. @@ -22,23 +24,22 @@ use Chill\MainBundle\Test\Export\AbstractAggregatorTest; */ final class ActivityTypeAggregatorTest extends AbstractAggregatorTest { + use ProphecyTrait; + private ActivityTypeAggregator $aggregator; protected function setUp(): void { self::bootKernel(); - $container = self::$kernel->getContainer(); + $this->aggregator = self::$container->get('chill.activity.export.type_aggregator'); - $this->aggregator = $container->get('chill.activity.export.type_aggregator'); + $request = $this->prophesize() + ->willExtend(\Symfony\Component\HttpFoundation\Request::class); - // add a fake request with a default locale (used in translatable string) - $prophet = new \Prophecy\Prophet(); - $request = $prophet->prophesize(); - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); $request->getLocale()->willReturn('fr'); - $container->get('request_stack') + self::$container->get('request_stack') ->push($request->reveal()); } @@ -60,8 +61,7 @@ final class ActivityTypeAggregatorTest extends AbstractAggregatorTest self::bootKernel(); } - $em = self::$kernel->getContainer() - ->get('doctrine.orm.entity_manager'); + $em = self::$container->get(EntityManagerInterface::class); return [ $em->createQueryBuilder() @@ -70,12 +70,7 @@ final class ActivityTypeAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(activity.id)') ->from('ChillActivityBundle:Activity', 'activity') - ->join('activity.reasons', 'reasons'), - $em->createQueryBuilder() - ->select('count(activity.id)') - ->from('ChillActivityBundle:Activity', 'activity') - ->join('activity.reasons', 'reasons') - ->join('reasons.category', 'category'), + ->join('activity.activityType', 'acttype'), ]; } } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityUserAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityUserAggregatorTest.php index 0e620040f..ea76a362c 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityUserAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ActivityUserAggregatorTest.php @@ -13,6 +13,8 @@ namespace Chill\ActivityBundle\Tests\Export\Aggregator; use Chill\ActivityBundle\Export\Aggregator\ActivityUserAggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Doctrine\ORM\EntityManagerInterface; +use Prophecy\PhpUnit\ProphecyTrait; /** * Add tests for ActivityUsernAggregator. @@ -22,23 +24,22 @@ use Chill\MainBundle\Test\Export\AbstractAggregatorTest; */ final class ActivityUserAggregatorTest extends AbstractAggregatorTest { + use ProphecyTrait; + private ActivityUserAggregator $aggregator; protected function setUp(): void { self::bootKernel(); - $container = self::$kernel->getContainer(); + $this->aggregator = self::$container->get('chill.activity.export.user_aggregator'); - $this->aggregator = $container->get('chill.activity.export.user_aggregator'); + $request = $this->prophesize() + ->willExtend(\Symfony\Component\HttpFoundation\Request::class); - // add a fake request with a default locale (used in translatable string) - $prophet = new \Prophecy\Prophet(); - $request = $prophet->prophesize(); - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); $request->getLocale()->willReturn('fr'); - $container->get('request_stack') + self::$container->get('request_stack') ->push($request->reveal()); } @@ -47,35 +48,25 @@ final class ActivityUserAggregatorTest extends AbstractAggregatorTest return $this->aggregator; } - public function getFormData() + public function getFormData(): array { return [ [], ]; } - public function getQueryBuilders() + public function getQueryBuilders(): array { if (null === self::$kernel) { self::bootKernel(); } - $em = self::$kernel->getContainer() - ->get('doctrine.orm.entity_manager'); + $em = self::$container->get(EntityManagerInterface::class); return [ $em->createQueryBuilder() ->select('count(activity.id)') ->from('ChillActivityBundle:Activity', 'activity'), - $em->createQueryBuilder() - ->select('count(activity.id)') - ->from('ChillActivityBundle:Activity', 'activity') - ->join('activity.reasons', 'reasons'), - $em->createQueryBuilder() - ->select('count(activity.id)') - ->from('ChillActivityBundle:Activity', 'activity') - ->join('activity.reasons', 'reasons') - ->join('reasons.category', 'category'), ]; } } From f5fb721ddd78017d2a168511e07b968343323589 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Thu, 29 Sep 2022 20:00:45 +0200 Subject: [PATCH 104/120] activity export export tests --- .../Export/LinkedToACP/CountActivityTest.php | 51 +++++++++++++++++++ .../CountActivityTest.php | 17 +++---- .../{ => LinkedToPerson}/ListActivityTest.php | 23 ++++----- .../StatActivityDurationTest.php} | 19 +++---- 4 files changed, 74 insertions(+), 36 deletions(-) create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/CountActivityTest.php rename src/Bundle/ChillActivityBundle/Tests/Export/Export/{ => LinkedToPerson}/CountActivityTest.php (62%) rename src/Bundle/ChillActivityBundle/Tests/Export/Export/{ => LinkedToPerson}/ListActivityTest.php (65%) rename src/Bundle/ChillActivityBundle/Tests/Export/Export/{StatActivityDurationSumTest.php => LinkedToPerson/StatActivityDurationTest.php} (55%) diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/CountActivityTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/CountActivityTest.php new file mode 100644 index 000000000..78f8556c1 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/CountActivityTest.php @@ -0,0 +1,51 @@ +export = self::$container->get('chill.activity.export.count_activity_linked_to_acp'); + } + + public function getExport() + { + return $this->export; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getModifiersCombination(): array + { + return [ + ['activity'], + ['activity', 'accompanying_period'], + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Export/CountActivityTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/CountActivityTest.php similarity index 62% rename from src/Bundle/ChillActivityBundle/Tests/Export/Export/CountActivityTest.php rename to src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/CountActivityTest.php index cbf562dfa..f94ef3414 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Export/CountActivityTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/CountActivityTest.php @@ -9,8 +9,9 @@ declare(strict_types=1); -namespace Chill\ActivityBundle\Tests\Export\Export; +namespace Chill\ActivityBundle\Tests\Export\Export\LinkedToPerson; +use Chill\ActivityBundle\Export\Export\LinkedToPerson\CountActivity; use Chill\MainBundle\Test\Export\AbstractExportTest; /** @@ -19,19 +20,13 @@ use Chill\MainBundle\Test\Export\AbstractExportTest; */ final class CountActivityTest extends AbstractExportTest { - /** - * @var - */ - private $export; + private CountActivity $export; protected function setUp(): void { self::bootKernel(); - /** @var \Symfony\Component\DependencyInjection\ContainerInterface $container */ - $container = self::$kernel->getContainer(); - - $this->export = $container->get('chill.activity.export.count_activity'); + $this->export = self::$container->get('chill.activity.export.count_activity_linked_to_person'); } public function getExport() @@ -39,14 +34,14 @@ final class CountActivityTest extends AbstractExportTest return $this->export; } - public function getFormData() + public function getFormData(): array { return [ [], ]; } - public function getModifiersCombination() + public function getModifiersCombination(): array { return [ ['activity'], diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Export/ListActivityTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/ListActivityTest.php similarity index 65% rename from src/Bundle/ChillActivityBundle/Tests/Export/Export/ListActivityTest.php rename to src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/ListActivityTest.php index 46d23b023..a613bc083 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Export/ListActivityTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/ListActivityTest.php @@ -9,9 +9,11 @@ declare(strict_types=1); -namespace Chill\ActivityBundle\Tests\Export\Export; +namespace Chill\ActivityBundle\Tests\Export\Export\LinkedToPerson; +use Chill\ActivityBundle\Export\Export\LinkedToPerson\ListActivity; use Chill\MainBundle\Test\Export\AbstractExportTest; +use Prophecy\PhpUnit\ProphecyTrait; /** * @internal @@ -19,27 +21,22 @@ use Chill\MainBundle\Test\Export\AbstractExportTest; */ final class ListActivityTest extends AbstractExportTest { - /** - * @var \Chill\ActivityBundle\Export\Export\ListActivity - */ - private $export; + use ProphecyTrait; + + private ListActivity $export; protected function setUp(): void { self::bootKernel(); - /** @var \Symfony\Component\DependencyInjection\ContainerInterface $container */ - $container = self::$kernel->getContainer(); + $this->export = self::$container->get('chill.activity.export.list_activity_linked_to_person'); - $this->export = $container->get('chill.activity.export.list_activity'); + $request = $this->prophesize() + ->willExtend(\Symfony\Component\HttpFoundation\Request::class); - // add a fake request with a default locale (used in translatable string) - $prophet = new \Prophecy\Prophet(); - $request = $prophet->prophesize(); - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); $request->getLocale()->willReturn('fr'); - $container->get('request_stack') + self::$container->get('request_stack') ->push($request->reveal()); } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Export/StatActivityDurationSumTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/StatActivityDurationTest.php similarity index 55% rename from src/Bundle/ChillActivityBundle/Tests/Export/Export/StatActivityDurationSumTest.php rename to src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/StatActivityDurationTest.php index c69a31b5a..ae9e4f583 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Export/StatActivityDurationSumTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/StatActivityDurationTest.php @@ -9,8 +9,9 @@ declare(strict_types=1); -namespace Chill\ActivityBundle\Tests\Export\Export; +namespace Chill\ActivityBundle\Tests\Export\Export\LinkedToPerson; +use Chill\ActivityBundle\Export\Export\LinkedToPerson\StatActivityDuration; use Chill\MainBundle\Test\Export\AbstractExportTest; /** @@ -19,21 +20,15 @@ use Chill\MainBundle\Test\Export\AbstractExportTest; * @internal * @coversNothing */ -final class StatActivityDurationSumTest extends AbstractExportTest +final class StatActivityDurationTest extends AbstractExportTest { - /** - * @var \Chill\ActivityBundle\Export\Export\StatActivityDuration - */ - private $export; + private StatActivityDuration $export; protected function setUp(): void { self::bootKernel(); - /** @var \Symfony\Component\DependencyInjection\ContainerInterface $container */ - $container = self::$kernel->getContainer(); - - $this->export = $container->get('chill.activity.export.sum_activity_duration'); + $this->export = self::$container->get('chill.activity.export.sum_activity_duration_linked_to_person'); } public function getExport() @@ -41,14 +36,14 @@ final class StatActivityDurationSumTest extends AbstractExportTest return $this->export; } - public function getFormData() + public function getFormData(): array { return [ [], ]; } - public function getModifiersCombination() + public function getModifiersCombination(): array { return [ ['activity'], From 6bddd320fa5ec2103c4e0a944c63f5b5b567f194 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Thu, 29 Sep 2022 20:13:30 +0200 Subject: [PATCH 105/120] Add Activity Export/Export missing tests --- .../LinkedToACP/AvgActivityDurationTest.php | 51 +++++++++++++++++++ .../AvgActivityVisitDurationTest.php | 51 +++++++++++++++++++ .../LinkedToACP/SumActivityDurationTest.php | 51 +++++++++++++++++++ .../SumActivityVisitDurationTest.php | 51 +++++++++++++++++++ 4 files changed, 204 insertions(+) create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/AvgActivityDurationTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/AvgActivityVisitDurationTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/SumActivityDurationTest.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/SumActivityVisitDurationTest.php diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/AvgActivityDurationTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/AvgActivityDurationTest.php new file mode 100644 index 000000000..b175c4f15 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/AvgActivityDurationTest.php @@ -0,0 +1,51 @@ +export = self::$container->get('chill.activity.export.avg_activity_duration_linked_to_acp'); + } + + public function getExport() + { + return $this->export; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getModifiersCombination(): array + { + return [ + ['activity'], + ['activity', 'accompanying_period'], + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/AvgActivityVisitDurationTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/AvgActivityVisitDurationTest.php new file mode 100644 index 000000000..5d0e11be3 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/AvgActivityVisitDurationTest.php @@ -0,0 +1,51 @@ +export = self::$container->get('chill.activity.export.avg_activity_visit_duration_linked_to_acp'); + } + + public function getExport() + { + return $this->export; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getModifiersCombination(): array + { + return [ + ['activity'], + ['activity', 'accompanying_period'], + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/SumActivityDurationTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/SumActivityDurationTest.php new file mode 100644 index 000000000..939ca2a88 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/SumActivityDurationTest.php @@ -0,0 +1,51 @@ +export = self::$container->get('chill.activity.export.sum_activity_duration_linked_to_acp'); + } + + public function getExport() + { + return $this->export; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getModifiersCombination(): array + { + return [ + ['activity'], + ['activity', 'accompanying_period'], + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/SumActivityVisitDurationTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/SumActivityVisitDurationTest.php new file mode 100644 index 000000000..7c0f79bc5 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToACP/SumActivityVisitDurationTest.php @@ -0,0 +1,51 @@ +export = self::$container->get('chill.activity.export.sum_activity_visit_duration_linked_to_acp'); + } + + public function getExport() + { + return $this->export; + } + + public function getFormData(): array + { + return [ + [], + ]; + } + + public function getModifiersCombination(): array + { + return [ + ['activity'], + ['activity', 'accompanying_period'], + ]; + } +} From b7c29038155e59a25bf123b4a91fd1961e6c9a09 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Thu, 29 Sep 2022 20:25:36 +0200 Subject: [PATCH 106/120] keep prophecy when test get 'request_stack' --- .../Filter/ActivityReasonFilterTest.php | 16 ++++++++-------- ...sonHavingActivityBetweenDateFilterTest.php | 12 ++++-------- .../Tests/Export/Export/ListPersonTest.php | 19 +++++++++---------- .../RequestorFilterTest.php | 1 - 4 files changed, 21 insertions(+), 27 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php index 72f052f54..d45ecfbb2 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php @@ -14,6 +14,7 @@ namespace Chill\ActivityBundle\Tests\Export\Filter; use Chill\ActivityBundle\Export\Filter\PersonFilters\ActivityReasonFilter; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Doctrine\Common\Collections\ArrayCollection; +use Prophecy\PhpUnit\ProphecyTrait; /** * @internal @@ -21,23 +22,22 @@ use Doctrine\Common\Collections\ArrayCollection; */ final class ActivityReasonFilterTest extends AbstractFilterTest { + use ProphecyTrait; + private ActivityReasonFilter $filter; protected function setUp(): void { self::bootKernel(); - $container = self::$kernel->getContainer(); + $this->filter = self::$container->get('chill.activity.export.reason_filter'); - $this->filter = $container->get('chill.activity.export.reason_filter'); - - // add a fake request with a default locale (used in translatable string) - $prophet = new \Prophecy\Prophet(); - $request = $prophet->prophesize(); - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request = $this->prophesize() + ->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); - $container->get('request_stack') + self::$container->get('request_stack') ->push($request->reveal()); } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonHavingActivityBetweenDateFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonHavingActivityBetweenDateFilterTest.php index 507f03323..0e8199352 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonHavingActivityBetweenDateFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonHavingActivityBetweenDateFilterTest.php @@ -28,18 +28,14 @@ final class PersonHavingActivityBetweenDateFilterTest extends AbstractFilterTest { self::bootKernel(); - $container = self::$kernel->getContainer(); + $this->filter = self::$container->get('chill.activity.export.person_having_an_activity_between_date_filter'); - $this->filter = $container->get('chill.activity.export.' - . 'person_having_an_activity_between_date_filter'); + $request = $this->prophesize() + ->willExtend(\Symfony\Component\HttpFoundation\Request::class); - // add a fake request with a default locale (used in translatable string) - $prophet = new \Prophecy\Prophet(); - $request = $prophet->prophesize(); - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); $request->getLocale()->willReturn('fr'); - $container->get('request_stack') + self::$container->get('request_stack') ->push($request->reveal()); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListPersonTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListPersonTest.php index f3408a064..d3564c0c6 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListPersonTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListPersonTest.php @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Tests\Export\Export; use Chill\MainBundle\Test\Export\AbstractExportTest; use Chill\PersonBundle\Export\Export\ListPerson; use DateTime; +use Prophecy\PhpUnit\ProphecyTrait; /** * Test the export "ListPerson". @@ -23,10 +24,9 @@ use DateTime; */ final class ListPersonTest extends AbstractExportTest { - /** - * @var ListPerson - */ - private $export; + use ProphecyTrait; + + private ListPerson $export; protected function setUp(): void { @@ -34,10 +34,9 @@ final class ListPersonTest extends AbstractExportTest $this->export = self::$container->get('chill.person.export.list_person'); - // add a fake request with a default locale (used in translatable string) - $prophet = new \Prophecy\Prophet(); - $request = $prophet->prophesize(); - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request = $this->prophesize() + ->willExtend(\Symfony\Component\HttpFoundation\Request::class); + $request->getLocale()->willReturn('fr'); self::$container->get('request_stack') @@ -49,7 +48,7 @@ final class ListPersonTest extends AbstractExportTest return $this->export; } - public function getFormData() + public function getFormData(): array { return [ ['fields' => ['id', 'firstName', 'lastName']], @@ -65,7 +64,7 @@ final class ListPersonTest extends AbstractExportTest ]; } - public function getModifiersCombination() + public function getModifiersCombination(): array { return [ ['person'], diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/RequestorFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/RequestorFilterTest.php index d936f09ae..f9a184021 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/RequestorFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/RequestorFilterTest.php @@ -26,7 +26,6 @@ final class RequestorFilterTest extends AbstractFilterTest protected function setUp(): void { - //parent::setUp(); self::bootKernel(); // add a fake request with a default locale (used in translatable string) From e737f207359eaefb334b1b460a1d3a1612e1bbc5 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Thu, 29 Sep 2022 20:41:13 +0200 Subject: [PATCH 107/120] batch removing unused $request prophesise in Tests setup() --- .../Export/Filter/ACPFilters/ActivityTypeFilterTest.php | 6 ------ .../Export/Filter/ACPFilters/BySocialActionFilterTest.php | 6 ------ .../Export/Filter/ACPFilters/BySocialIssueFilterTest.php | 6 ------ .../Tests/Export/Filter/ACPFilters/ByUserFilterTest.php | 6 ------ .../Tests/Export/Filter/ACPFilters/EmergencyFilterTest.php | 6 ------ .../Export/Filter/ACPFilters/LocationTypeFilterTest.php | 6 ------ .../Export/Filter/ACPFilters/SentReceivedFilterTest.php | 6 ------ .../Tests/Export/Filter/ACPFilters/UserFilterTest.php | 6 ------ .../Tests/Export/Filter/ACPFilters/UserScopeFilterTest.php | 6 ------ .../Tests/Export/Filter/ActivityDateFilterTest.php | 6 ------ .../Tests/Export/Filter/ActivityTypeFilterTest.php | 6 ------ .../Filter/PersonFilters/ActivityReasonFilterTest.php | 6 ------ .../PersonHavingActivityBetweenDateFilterTest.php | 6 ------ .../AccompanyingCourseFilters/ActiveOnDateFilterTest.php | 6 ------ .../ActiveOneDayBetweenDatesFilterTest.php | 6 ------ .../AdministrativeLocationFilterTest.php | 6 ------ .../AccompanyingCourseFilters/ClosingMotiveFilterTest.php | 6 ------ .../AccompanyingCourseFilters/ConfidentialFilterTest.php | 6 ------ .../AccompanyingCourseFilters/CurrentUserJobFilterTest.php | 6 ------ .../CurrentUserScopeFilterTest.php | 6 ------ .../AccompanyingCourseFilters/EmergencyFilterTest.php | 6 ------ .../AccompanyingCourseFilters/EvaluationFilterTest.php | 6 ------ .../GeographicalUnitStatFilterTest.php | 6 ------ .../AccompanyingCourseFilters/IntensityFilterTest.php | 6 ------ .../OpenBetweenDatesFilterTest.php | 6 ------ .../Filter/AccompanyingCourseFilters/OriginFilterTest.php | 6 ------ .../Filter/AccompanyingCourseFilters/ReferrerFilterTest.php | 6 ------ .../AccompanyingCourseFilters/RequestorFilterTest.php | 6 ------ .../AccompanyingCourseFilters/SocialActionFilterTest.php | 6 ------ .../AccompanyingCourseFilters/SocialIssueFilterTest.php | 6 ------ .../Filter/AccompanyingCourseFilters/StepFilterTest.php | 6 ------ .../Filter/EvaluationFilters/EvaluationTypeFilterTest.php | 6 ------ .../Export/Filter/EvaluationFilters/MaxDateFilterTest.php | 6 ------ .../Filter/HouseholdFilters/CompositionFilterTest.php | 6 ------ .../Tests/Export/Filter/PersonFilters/AgeFilterTest.php | 6 ------ .../Export/Filter/PersonFilters/DeadOrAliveFilterTest.php | 6 ------ .../Export/Filter/PersonFilters/DeathdateFilterTest.php | 6 ------ .../Tests/Export/Filter/PersonFilters/GenderFilterTest.php | 6 ------ .../Export/Filter/PersonFilters/MaritalStatusFilterTest.php | 6 ------ .../Export/Filter/PersonFilters/NationalityFilterTest.php | 6 ------ .../ResidentialAddressAtThirdpartyFilterTest.php | 6 ------ .../PersonFilters/ResidentialAddressAtUserFilterTest.php | 6 ------ .../Tests/Export/Filter/SocialWorkFilters/JobFilterTest.php | 6 ------ .../Export/Filter/SocialWorkFilters/ReferrerFilterTest.php | 6 ------ .../Export/Filter/SocialWorkFilters/ScopeFilterTest.php | 6 ------ .../Filter/SocialWorkFilters/SocialWorkTypeFilterTest.php | 6 ------ .../Tests/Export/Filter/ReportDateFilterTest.php | 6 ------ 47 files changed, 282 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php index 10f16247e..e38d4cf5c 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php @@ -31,12 +31,6 @@ final class ActivityTypeFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.activity.export.filter_activitytype'); } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialActionFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialActionFilterTest.php index 1480e3569..d8091d068 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialActionFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialActionFilterTest.php @@ -29,12 +29,6 @@ final class BySocialActionFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.activity.export.bysocialaction_filter'); } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialIssueFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialIssueFilterTest.php index 6af3ea97c..61e5d4715 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialIssueFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialIssueFilterTest.php @@ -29,12 +29,6 @@ final class BySocialIssueFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.activity.export.bysocialissue_filter'); } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php index 93810433d..aaf852b14 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php @@ -29,12 +29,6 @@ final class ByUserFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.activity.export.byuser_filter'); } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/EmergencyFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/EmergencyFilterTest.php index d90523bed..07810efbe 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/EmergencyFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/EmergencyFilterTest.php @@ -28,12 +28,6 @@ final class EmergencyFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.activity.export.emergency_filter'); } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/LocationTypeFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/LocationTypeFilterTest.php index dcac884b9..6888e6cf7 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/LocationTypeFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/LocationTypeFilterTest.php @@ -29,12 +29,6 @@ final class LocationTypeFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.activity.export.locationtype_filter'); } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/SentReceivedFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/SentReceivedFilterTest.php index b40443ee4..d9a24dc27 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/SentReceivedFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/SentReceivedFilterTest.php @@ -28,12 +28,6 @@ final class SentReceivedFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.activity.export.sentreceived_filter'); } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserFilterTest.php index cc1a1b81a..838973caa 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserFilterTest.php @@ -29,12 +29,6 @@ final class UserFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.activity.export.user_filter'); } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserScopeFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserScopeFilterTest.php index 31ff8ca57..6b1040036 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserScopeFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserScopeFilterTest.php @@ -29,12 +29,6 @@ final class UserScopeFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.activity.export.userscope_filter'); } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityDateFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityDateFilterTest.php index b65d3808d..e95fd7c20 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityDateFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityDateFilterTest.php @@ -28,12 +28,6 @@ final class ActivityDateFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.activity.export.date_filter'); } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityTypeFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityTypeFilterTest.php index 12ace7c67..1bee8532e 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityTypeFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityTypeFilterTest.php @@ -29,12 +29,6 @@ final class ActivityTypeFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.activity.export.type_filter'); } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/ActivityReasonFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/ActivityReasonFilterTest.php index e6b14b44c..fc7dbbd00 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/ActivityReasonFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/ActivityReasonFilterTest.php @@ -29,12 +29,6 @@ final class ActivityReasonFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.activity.export.reason_filter'); } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilterTest.php index 5cad68da4..42cf9d884 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilterTest.php @@ -29,12 +29,6 @@ final class PersonHavingActivityBetweenDateFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.activity.export.person_having_an_activity_between_date_filter'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOnDateFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOnDateFilterTest.php index 3a8160cd2..fa03315d2 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOnDateFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOnDateFilterTest.php @@ -30,12 +30,6 @@ final class ActiveOnDateFilterTest extends AbstractFilterTest //parent::setUp(); self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_activeondate'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilterTest.php index 30957879e..44f55e69f 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilterTest.php @@ -30,12 +30,6 @@ final class ActiveOneDayBetweenDatesFilterTest extends AbstractFilterTest //parent::setUp(); self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_activeonedaybetweendates'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/AdministrativeLocationFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/AdministrativeLocationFilterTest.php index c09c50fbc..3bd7518f2 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/AdministrativeLocationFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/AdministrativeLocationFilterTest.php @@ -30,12 +30,6 @@ final class AdministrativeLocationFilterTest extends AbstractFilterTest //parent::setUp(); self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_administrative_location'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ClosingMotiveFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ClosingMotiveFilterTest.php index 8ff47ac6c..02e0de4d2 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ClosingMotiveFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ClosingMotiveFilterTest.php @@ -28,12 +28,6 @@ final class ClosingMotiveFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_closingmotive'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ConfidentialFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ConfidentialFilterTest.php index db469bd49..c3e185e1f 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ConfidentialFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ConfidentialFilterTest.php @@ -27,12 +27,6 @@ final class ConfidentialFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_confidential'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilterTest.php index ab2c703b6..0507333ab 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilterTest.php @@ -27,12 +27,6 @@ final class CurrentUserJobFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_userjob'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilterTest.php index d4f4712bc..12c3ce96f 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilterTest.php @@ -27,12 +27,6 @@ final class CurrentUserScopeFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_userscope'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/EmergencyFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/EmergencyFilterTest.php index 5a7c1228f..7f5fe9f32 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/EmergencyFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/EmergencyFilterTest.php @@ -27,12 +27,6 @@ final class EmergencyFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_emergency'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/EvaluationFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/EvaluationFilterTest.php index c3c25870d..b1436f345 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/EvaluationFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/EvaluationFilterTest.php @@ -30,12 +30,6 @@ final class EvaluationFilterTest extends AbstractFilterTest //parent::setUp(); self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_evaluation'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilterTest.php index 8baaf5df7..247cb870b 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilterTest.php @@ -30,12 +30,6 @@ final class GeographicalUnitStatFilterTest extends AbstractFilterTest //parent::setUp(); self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_geographicalunitstat'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/IntensityFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/IntensityFilterTest.php index 6319b2acf..2f4c0caf8 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/IntensityFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/IntensityFilterTest.php @@ -27,12 +27,6 @@ final class IntensityFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_intensity'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilterTest.php index 5334c17f8..a402b1f6a 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilterTest.php @@ -30,12 +30,6 @@ final class OpenBetweenDatesFilterTest extends AbstractFilterTest //parent::setUp(); self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_openbetweendates'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/OriginFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/OriginFilterTest.php index 745e62ca6..3182d0063 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/OriginFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/OriginFilterTest.php @@ -28,12 +28,6 @@ final class OriginFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_origin'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ReferrerFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ReferrerFilterTest.php index 8d2c8ef5b..71cfd44da 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ReferrerFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ReferrerFilterTest.php @@ -29,12 +29,6 @@ final class ReferrerFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_referrer'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/RequestorFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/RequestorFilterTest.php index f9a184021..318bccf0a 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/RequestorFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/RequestorFilterTest.php @@ -28,12 +28,6 @@ final class RequestorFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_requestor'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialActionFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialActionFilterTest.php index 39321dad1..7b9a8b63e 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialActionFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialActionFilterTest.php @@ -30,12 +30,6 @@ final class SocialActionFilterTest extends AbstractFilterTest //parent::setUp(); self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_socialaction'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialIssueFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialIssueFilterTest.php index a7815d3ed..9b7eec446 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialIssueFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialIssueFilterTest.php @@ -28,12 +28,6 @@ final class SocialIssueFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_socialissue'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/StepFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/StepFilterTest.php index e9353b461..0dce61dc3 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/StepFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/StepFilterTest.php @@ -27,12 +27,6 @@ final class StepFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_step'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/EvaluationTypeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/EvaluationTypeFilterTest.php index 8ba2b46c1..81c764c99 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/EvaluationTypeFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/EvaluationTypeFilterTest.php @@ -29,12 +29,6 @@ final class EvaluationTypeFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_evaluationtype'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/MaxDateFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/MaxDateFilterTest.php index 5c9533850..be4b3580a 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/MaxDateFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/MaxDateFilterTest.php @@ -29,12 +29,6 @@ final class MaxDateFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_maxdate'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/HouseholdFilters/CompositionFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/HouseholdFilters/CompositionFilterTest.php index 6892c0afc..e71949958 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/HouseholdFilters/CompositionFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/HouseholdFilters/CompositionFilterTest.php @@ -29,12 +29,6 @@ final class CompositionFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_household_composition'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AgeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AgeFilterTest.php index 30ca43dfd..30c3ed86d 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AgeFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AgeFilterTest.php @@ -28,12 +28,6 @@ final class AgeFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_age'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeadOrAliveFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeadOrAliveFilterTest.php index ea2fa62cb..0cbb22be7 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeadOrAliveFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeadOrAliveFilterTest.php @@ -28,12 +28,6 @@ final class DeadOrAliveFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_dead_or_alive'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeathdateFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeathdateFilterTest.php index cb711af43..17742dcd6 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeathdateFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeathdateFilterTest.php @@ -28,12 +28,6 @@ final class DeathdateFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_deathdate'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/GenderFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/GenderFilterTest.php index 8ce02a653..9ebf99b6d 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/GenderFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/GenderFilterTest.php @@ -27,12 +27,6 @@ final class GenderFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $prophet = new \Prophecy\Prophet(); - $request = $prophet->prophesize(); - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_gender'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/MaritalStatusFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/MaritalStatusFilterTest.php index 8ce747f63..40fa814b1 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/MaritalStatusFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/MaritalStatusFilterTest.php @@ -29,12 +29,6 @@ final class MaritalStatusFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_marital_status'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/NationalityFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/NationalityFilterTest.php index 4ea2c44cc..6dd1a944e 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/NationalityFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/NationalityFilterTest.php @@ -29,12 +29,6 @@ final class NationalityFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_nationality'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilterTest.php index 8bd07f75c..8444f6865 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilterTest.php @@ -31,12 +31,6 @@ final class ResidentialAddressAtThirdpartyFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_residential_address_at_thirdparty'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtUserFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtUserFilterTest.php index 6a2cde4b0..ba3ededca 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtUserFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtUserFilterTest.php @@ -31,12 +31,6 @@ final class ResidentialAddressAtUserFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_residential_address_at_user'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/JobFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/JobFilterTest.php index 5116af979..76462ebe5 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/JobFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/JobFilterTest.php @@ -27,12 +27,6 @@ final class JobFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_job'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ReferrerFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ReferrerFilterTest.php index c5cc9c523..dd4a17c72 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ReferrerFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ReferrerFilterTest.php @@ -29,12 +29,6 @@ final class ReferrerFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_treatingagent'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ScopeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ScopeFilterTest.php index b12eb4e5b..2eb1301ca 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ScopeFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ScopeFilterTest.php @@ -27,12 +27,6 @@ final class ScopeFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_scope'); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/SocialWorkTypeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/SocialWorkTypeFilterTest.php index 2e3b392ae..b9def05cd 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/SocialWorkTypeFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/SocialWorkTypeFilterTest.php @@ -31,12 +31,6 @@ final class SocialWorkTypeFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.person.export.filter_social_work_type'); } diff --git a/src/Bundle/ChillReportBundle/Tests/Export/Filter/ReportDateFilterTest.php b/src/Bundle/ChillReportBundle/Tests/Export/Filter/ReportDateFilterTest.php index 27c107f9d..54291a02f 100644 --- a/src/Bundle/ChillReportBundle/Tests/Export/Filter/ReportDateFilterTest.php +++ b/src/Bundle/ChillReportBundle/Tests/Export/Filter/ReportDateFilterTest.php @@ -28,12 +28,6 @@ final class ReportDateFilterTest extends AbstractFilterTest { self::bootKernel(); - // add a fake request with a default locale (used in translatable string) - $request = $this->prophesize(); - - $request->willExtend(\Symfony\Component\HttpFoundation\Request::class); - $request->getLocale()->willReturn('fr'); - $this->filter = self::$container->get('chill.report.export.filter_date'); } From 99b261b1d76e10e32564a32e52d2b656a0a0e8a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 30 Sep 2022 17:31:30 +0200 Subject: [PATCH 108/120] [person][relations] Fixed: GET request had a body, this prevented to load files --- .../Resources/public/vuejs/VisGraph/api.js | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/api.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/api.js index 01c335436..afa3d933e 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/api.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/api.js @@ -5,8 +5,10 @@ import {makeFetch} from 'ChillMainAssets/lib/api/apiMethods.js'; * @function getFetch * @param url * @returns {Promise} + * @deprecated use makeFetch instead */ const getFetch = (url) => { + console.error('deprecated method'); return makeFetch('GET', url, null) } @@ -51,8 +53,10 @@ const getHouseholdByPerson = (person) => { if (person.current_household_id === null) { throw 'Currently the person has not household!' } - return getFetch( - `/api/1.0/person/household/${person.current_household_id}.json`) + return makeFetch( + 'GET', + `/api/1.0/person/household/${person.current_household_id}.json` + ); } /** @@ -62,8 +66,10 @@ const getHouseholdByPerson = (person) => { */ const getCoursesByPerson = (person) => { //console.log('getCoursesByPerson', person._id) - return getFetch( - `/api/1.0/person/accompanying-course/by-person/${person._id}.json`) + return makeFetch( + 'GET', + `/api/1.0/person/accompanying-course/by-person/${person._id}.json` + ); } /** @@ -73,8 +79,10 @@ const getCoursesByPerson = (person) => { */ const getRelationshipsByPerson = (person) => { //console.log('getRelationshipsByPerson', person.id) - return getFetch( - `/api/1.0/relations/relationship/by-person/${person._id}.json`) + return makeFetch( + 'GET', + `/api/1.0/relations/relationship/by-person/${person._id}.json` + ); } /** @@ -82,7 +90,7 @@ const getRelationshipsByPerson = (person) => { * @returns {Promise} */ const getRelationsList = () => { - return getFetch(`/api/1.0/relations/relation.json`) + return makeFetch('GET', `/api/1.0/relations/relation.json`); } /** From 994160f28a91b637b634caf277393903acd92b7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 3 Oct 2022 13:50:10 +0200 Subject: [PATCH 109/120] Feature: add a proper entity for geographical unit layer --- .../Entity/GeographicalUnit.php | 17 ++++-- .../Entity/GeographicalUnitLayer.php | 56 ++++++++++++++++++ .../migrations/Version20221003112151.php | 58 +++++++++++++++++++ 3 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Entity/GeographicalUnitLayer.php create mode 100644 src/Bundle/ChillMainBundle/migrations/Version20221003112151.php diff --git a/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php b/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php index 9e119e30d..fac5d9127 100644 --- a/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php +++ b/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php @@ -15,14 +15,14 @@ use Doctrine\ORM\Mapping as ORM; /** * @ORM\Table(name="chill_main_geographical_unit") - * @ORM\Entity + * @ORM\Entity(readOnly=true) */ class GeographicalUnit { /** * @ORM\Column(type="text", nullable=true) */ - private $geom; + private string $geom; /** * @ORM\Id @@ -32,14 +32,19 @@ class GeographicalUnit private ?int $id = null; /** - * @ORM\Column(type="string", length=255, nullable=true) + * @ORM\Column(type="text", nullable=false, options={"default": ""}) */ - private $layerName; + private string $unitName; /** - * @ORM\Column(type="string", length=255, nullable=true) + * @ORM\Column(type="text", nullable=false, options={"default": ""}) */ - private $unitName; + private string $unitRefId; + + /** + * @ORM\ManyToOne(targetEntity=GeographicalUnitLayer::class) + */ + private ?GeographicalUnitLayer $layer; public function getId(): ?int { diff --git a/src/Bundle/ChillMainBundle/Entity/GeographicalUnitLayer.php b/src/Bundle/ChillMainBundle/Entity/GeographicalUnitLayer.php new file mode 100644 index 000000000..76ac06e9c --- /dev/null +++ b/src/Bundle/ChillMainBundle/Entity/GeographicalUnitLayer.php @@ -0,0 +1,56 @@ +id; + } + + /** + * @return array + */ + public function getName(): array + { + return $this->name; + } + + /** + * @param array $name + * @return GeographicalUnitLayer + */ + public function setName(array $name): GeographicalUnitLayer + { + $this->name = $name; + return $this; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/migrations/Version20221003112151.php b/src/Bundle/ChillMainBundle/migrations/Version20221003112151.php new file mode 100644 index 000000000..36b1834ea --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20221003112151.php @@ -0,0 +1,58 @@ +addSql('CREATE SEQUENCE chill_main_geographical_unit_layer_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); + $this->addSql('CREATE TABLE chill_main_geographical_unit_layer (id INT NOT NULL, name JSONB DEFAULT \'[]\'::jsonb NOT NULL, refid TEXT DEFAULT \'\' NOT NULL, PRIMARY KEY(id))'); + $this->addSql("COMMENT ON COLUMN chill_main_geographical_unit_layer.name IS '(DC2Type:json)';"); + + $this->addSql('INSERT INTO chill_main_geographical_unit_layer (id, name, refid) + SELECT DISTINCT nextval(\'chill_main_geographical_unit_layer_id_seq\'), jsonb_build_object(\'fr\', layername), layername FROM chill_main_geographical_unit'); + + $this->addSql('ALTER TABLE chill_main_geographical_unit ADD layer_id INT DEFAULT NULL'); + + $this->addSql('UPDATE chill_main_geographical_unit SET layer_id = layer.id FROM chill_main_geographical_unit_layer AS layer WHERE layer.refid = chill_main_geographical_unit.layername'); + + $this->addSql('ALTER TABLE chill_main_geographical_unit ADD unitRefId TEXT DEFAULT \'\' NOT NULL'); + $this->addSql('ALTER TABLE chill_main_geographical_unit DROP layername'); + $this->addSql("COMMENT ON COLUMN chill_main_geographical_unit.geom IS '(DC2Type:text)';"); + $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitname TYPE TEXT'); + $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitname SET DEFAULT \'\''); + $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitname SET NOT NULL'); + $this->addSql('ALTER TABLE chill_main_geographical_unit ADD CONSTRAINT FK_360A2B2FEA6EFDCD FOREIGN KEY (layer_id) REFERENCES chill_main_geographical_unit_layer (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX IDX_360A2B2FEA6EFDCD ON chill_main_geographical_unit (layer_id)'); + + } + + public function down(Schema $schema): void + { + $this->throwIrreversibleMigrationException(); + + /* for memory + $this->addSql('ALTER TABLE chill_main_geographical_unit DROP CONSTRAINT FK_360A2B2FEA6EFDCD'); + $this->addSql('DROP SEQUENCE chill_main_geographical_unit_layer_id_seq CASCADE'); + $this->addSql('DROP TABLE chill_main_geographical_unit_layer'); + $this->addSql('ALTER TABLE chill_main_geographical_unit ADD layername VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE chill_main_geographical_unit DROP layer_id'); + $this->addSql('ALTER TABLE chill_main_geographical_unit DROP unitRefId'); + $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER geom TYPE VARCHAR(255)'); + $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitName TYPE VARCHAR(255)'); + $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitName DROP DEFAULT'); + $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitName DROP NOT NULL'); + */ + } +} From 9c3ac72426be727352277b37c072de907f802b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 3 Oct 2022 15:45:42 +0200 Subject: [PATCH 110/120] Feature: Create a base importer for geographical units and add index --- .../Entity/GeographicalUnit.php | 4 +- .../Entity/GeographicalUnitLayer.php | 5 +- .../Import/GeographicalUnitBaseImporter.php | 234 ++++++++++++++++++ .../GeographicalUnitBaseImporterTest.php | 89 +++++++ .../migrations/Version20221003132620.php | 30 +++ 5 files changed, 359 insertions(+), 3 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Service/Import/GeographicalUnitBaseImporter.php create mode 100644 src/Bundle/ChillMainBundle/Tests/Services/Import/GeographicalUnitBaseImporterTest.php create mode 100644 src/Bundle/ChillMainBundle/migrations/Version20221003132620.php diff --git a/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php b/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php index fac5d9127..45a7fc54e 100644 --- a/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php +++ b/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php @@ -14,7 +14,9 @@ namespace Chill\MainBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** - * @ORM\Table(name="chill_main_geographical_unit") + * @ORM\Table(name="chill_main_geographical_unit", uniqueConstraints={ + * @ORM\UniqueConstraint(name="geographical_unit_refid", columns={"unitRefId"}) + * }) * @ORM\Entity(readOnly=true) */ class GeographicalUnit diff --git a/src/Bundle/ChillMainBundle/Entity/GeographicalUnitLayer.php b/src/Bundle/ChillMainBundle/Entity/GeographicalUnitLayer.php index 76ac06e9c..54bde03ec 100644 --- a/src/Bundle/ChillMainBundle/Entity/GeographicalUnitLayer.php +++ b/src/Bundle/ChillMainBundle/Entity/GeographicalUnitLayer.php @@ -5,12 +5,13 @@ namespace Chill\MainBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** - * @ORM\Table(name="chill_main_geographical_unit_layer") + * @ORM\Table(name="chill_main_geographical_unit_layer", uniqueConstraints={ + * @ORM\UniqueConstraint(name="geographical_unit_layer_refid", columns={"refId"}) + * }) * @ORM\Entity */ class GeographicalUnitLayer { - /** * @ORM\Id * @ORM\GeneratedValue diff --git a/src/Bundle/ChillMainBundle/Service/Import/GeographicalUnitBaseImporter.php b/src/Bundle/ChillMainBundle/Service/Import/GeographicalUnitBaseImporter.php new file mode 100644 index 000000000..9df865a28 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Service/Import/GeographicalUnitBaseImporter.php @@ -0,0 +1,234 @@ + + */ + private array $cachingStatements = []; + + private Connection $defaultConnection; + + private bool $isInitialized = false; + + private LoggerInterface $logger; + + private array $waitingForInsert = []; + + public function __construct(Connection $defaultConnection, LoggerInterface $logger) + { + $this->defaultConnection = $defaultConnection; + $this->logger = $logger; + } + + public function finalize(): void + { + $this->doInsertPending(); + + $this->prepareForFinalize(); + + $this->updateGeographicalUnitTable(); + + $this->deleteTemporaryTable(); + + $this->isInitialized = false; + } + + public function importUnit( + string $layerKey, + array $layerName, + string $unitName, + string $unitKey, + string $geomAsWKT, + int $srid = null + ): void { + $this->initialize(); + + $this->waitingForInsert[] = [ + 'layerKey' => $layerKey, + 'layerName' => $layerName, + 'unitName' => $unitName, + 'unitKey' => $unitKey, + 'geomAsWKT' => $geomAsWKT, + 'srid' => $srid + ]; + + if (100 <= count($this->waitingForInsert)) { + $this->doInsertPending(); + } + } + + private function createTemporaryTable(): void + { + $this->defaultConnection->executeStatement("CREATE TEMPORARY TABLE geographical_unit_temp ( + layerKey TEXT DEFAULT '' NOT NULL, + layerName JSONB DEFAULT '[]'::jsonb NOT NULL, + unitName TEXT default '' NOT NULL, + unitKey TEXT default '' NOT NULL, + geom GEOMETRY(MULTIPOLYGON, 4326) + )"); + + $this->defaultConnection->executeStatement('SET work_mem TO \'50MB\''); + } + + private function deleteTemporaryTable(): void + { + $this->defaultConnection->executeStatement('DROP TABLE IF EXISTS geographical_unit_temp'); + } + + private function doInsertPending(): void + { + $forNumber = count($this->waitingForInsert); + + if (0 === $forNumber) { + return; + } + + if (!array_key_exists($forNumber, $this->cachingStatements)) { + $sql = strtr(self::INSERT, [ + '{{ values }}' => implode( + ', ', + array_fill(0, $forNumber, self::VALUE) + ), + ]); + + $this->logger->debug(self::LOG_PREFIX . ' generated sql for insert', [ + 'sql' => $sql, + 'forNumber' => $forNumber, + ]); + + $this->cachingStatements[$forNumber] = $this->defaultConnection->prepare($sql); + } + + $this->logger->debug(self::LOG_PREFIX . ' inserting pending addresses', [ + 'number' => $forNumber, + 'first' => $this->waitingForInsert[0] ?? null, + ]); + + $statement = $this->cachingStatements[$forNumber]; + try { + $i = 0; + foreach ($this->waitingForInsert as $insert) { + $statement->bindValue(++$i, $insert['layerKey'], Types::STRING); + $statement->bindValue(++$i, $insert['layerName'], Types::JSON); + $statement->bindValue(++$i, $insert['unitName'], Types::STRING); + $statement->bindValue(++$i, $insert['unitKey'], Types::STRING); + $statement->bindValue(++$i, $insert['geomAsWKT'], Types::STRING); + $statement->bindValue(++$i, $insert['srid'], Types::INTEGER); + } + + $affected = $statement->executeStatement(); + + if ($affected === 0) { + throw new \RuntimeException('no row affected'); + } + } catch (Exception $e) { + throw $e; + } finally { + $this->waitingForInsert = []; + } + } + + private function initialize(): void + { + if ($this->isInitialized) { + return; + } + + $this->deleteTemporaryTable(); + $this->createTemporaryTable(); + $this->isInitialized = true; + } + + private function prepareForFinalize(): void + { + $this->defaultConnection->executeStatement( + 'CREATE INDEX idx_ref_add_temp ON geographical_unit_temp (unitKey)' + ); + } + + private function updateGeographicalUnitTable(): void + { + $this->defaultConnection->transactional( + function() { + // 0) create new layers + $this->defaultConnection->executeStatement( + " + WITH unique_layers AS ( + SELECT DISTINCT layerKey, layerName FROM geographical_unit_temp + ) + INSERT INTO chill_main_geographical_unit_layer (id, name, refid) + SELECT + nextval('chill_main_geographical_unit_layer_id_seq'), + layerName, + layerKey + FROM unique_layers + ON CONFLICT (refid) + DO UPDATE SET name=EXCLUDED.name + "); + + //1) Add new units + $this->logger->info(self::LOG_PREFIX . 'upsert new units'); + $affected = $this->defaultConnection->executeStatement("INSERT INTO chill_main_geographical_unit + (id, geom, unitname, layer_id, unitrefid) + SELECT + nextval('chill_main_geographical_unit_id_seq'), + geom, + unitName, + layer.id, + unitKey + FROM geographical_unit_temp JOIN chill_main_geographical_unit_layer AS layer ON layer.refid = layerKey + ON CONFLICT (unitrefid) + DO UPDATE + SET geom = EXCLUDED.geom, unitname = EXCLUDED.unitname + "); + $this->logger->info(self::LOG_PREFIX . 'units upserted', ['upserted' => $affected]); + + //3) Delete units + $this->logger->info(self::LOG_PREFIX . 'soft delete adresses'); + $affected = $this->defaultConnection->executeStatement('DELETE FROM chill_main_geographical_unit + WHERE + unitrefid NOT IN (SELECT distinct unitKey FROM geographical_unit_temp) + '); + $this->logger->info(self::LOG_PREFIX . 'addresses deleted', ['deleted' => $affected]); + } + ); + } +} diff --git a/src/Bundle/ChillMainBundle/Tests/Services/Import/GeographicalUnitBaseImporterTest.php b/src/Bundle/ChillMainBundle/Tests/Services/Import/GeographicalUnitBaseImporterTest.php new file mode 100644 index 000000000..bc66c3691 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Services/Import/GeographicalUnitBaseImporterTest.php @@ -0,0 +1,89 @@ +connection = self::$container->get(Connection::class); + $this->entityManager = self::$container->get(EntityManagerInterface::class); + } + + public function testImportUnit(): void + { + $importer = new GeographicalUnitBaseImporter( + $this->connection, + new NullLogger() + ); + + $importer->importUnit( + 'test', + ['fr' => 'Test Layer'], + 'Layer one', + 'layer_one', + 'MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))', + 3812 + ); + + $importer->finalize(); + + $unit = $this->connection->executeQuery(" + SELECT unitname, unitrefid, cmgul.refid AS layerrefid, cmgul.name AS layername, ST_AsText(ST_snapToGrid(ST_Transform(u.geom, 3812), 1)) AS geom + FROM chill_main_geographical_unit u JOIN chill_main_geographical_unit_layer cmgul on u.layer_id = cmgul.id + WHERE u.unitrefid = ?", ['layer_one']); + + $results = $unit->fetchAssociative(); + + $this->assertEquals($results['unitrefid'], 'layer_one'); + $this->assertEquals($results['unitname'], 'Layer one'); + $this->assertEquals(json_decode($results['layername'], true), ['fr' => 'Test Layer']); + $this->assertEquals($results['layerrefid'], 'test'); + $this->assertEquals($results['geom'], 'MULTIPOLYGON(((30 20,45 40,10 40,30 20)),((15 5,40 10,10 20,5 10,15 5)))'); + + $importer = new GeographicalUnitBaseImporter( + $this->connection, + new NullLogger() + ); + + $importer->importUnit( + 'test', + ['fr' => 'Test Layer fixed'], + 'Layer one fixed', + 'layer_one', + 'MULTIPOLYGON (((130 120, 45 40, 10 40, 130 120)),((0 0, 15 5, 40 10, 10 20, 0 0)))', + 3812 + ); + + $importer->finalize(); + + $unit = $this->connection->executeQuery(" + SELECT unitname, unitrefid, cmgul.refid AS layerrefid, cmgul.name AS layername, ST_AsText(ST_snapToGrid(ST_Transform(u.geom, 3812), 1)) AS geom + FROM chill_main_geographical_unit u JOIN chill_main_geographical_unit_layer cmgul on u.layer_id = cmgul.id + WHERE u.unitrefid = ?", ['layer_one']); + + $results = $unit->fetchAssociative(); + + $this->assertEquals($results['unitrefid'], 'layer_one'); + $this->assertEquals($results['unitname'], 'Layer one fixed'); + $this->assertEquals(json_decode($results['layername'], true), ['fr' => 'Test Layer fixed']); + $this->assertEquals($results['layerrefid'], 'test'); + $this->assertEquals($results['geom'], 'MULTIPOLYGON(((130 120,45 40,10 40,130 120)),((0 0,15 5,40 10,10 20,0 0)))'); + + } + +} \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/migrations/Version20221003132620.php b/src/Bundle/ChillMainBundle/migrations/Version20221003132620.php new file mode 100644 index 000000000..39b01a0bc --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20221003132620.php @@ -0,0 +1,30 @@ +addSql('CREATE UNIQUE INDEX geographical_unit_layer_refid ON chill_main_geographical_unit_layer (refId)'); + $this->addSql('CREATE UNIQUE INDEX geographical_unit_refid ON chill_main_geographical_unit (unitRefId)'); + $this->addSql('CREATE INDEX chill_internal_geographical_unit_layer_geom_idx ON chill_main_geographical_unit USING GIST (geom)'); + } + + public function down(Schema $schema): void + { + $this->addSql('DROP INDEX geographical_unit_layer_refid'); + $this->addSql('DROP INDEX geographical_unit_refid'); + $this->addSql('DROP INDEX chill_internal_geographical_unit_layer_geom_idx'); + } +} From 65f6712a15d516d59304cb03e7e912ffd330303e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 3 Oct 2022 17:19:11 +0200 Subject: [PATCH 111/120] Fixed: take layer into account for unicity of geographical unit's keys --- .../Entity/GeographicalUnit.php | 2 +- .../Import/GeographicalUnitBaseImporter.php | 20 +++++++++---------- .../migrations/Version20221003132620.php | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php b/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php index 45a7fc54e..b4c4388b6 100644 --- a/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php +++ b/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php @@ -15,7 +15,7 @@ use Doctrine\ORM\Mapping as ORM; /** * @ORM\Table(name="chill_main_geographical_unit", uniqueConstraints={ - * @ORM\UniqueConstraint(name="geographical_unit_refid", columns={"unitRefId"}) + * @ORM\UniqueConstraint(name="geographical_unit_refid", columns={"layer_id", "unitRefId"}) * }) * @ORM\Entity(readOnly=true) */ diff --git a/src/Bundle/ChillMainBundle/Service/Import/GeographicalUnitBaseImporter.php b/src/Bundle/ChillMainBundle/Service/Import/GeographicalUnitBaseImporter.php index 9df865a28..539e69028 100644 --- a/src/Bundle/ChillMainBundle/Service/Import/GeographicalUnitBaseImporter.php +++ b/src/Bundle/ChillMainBundle/Service/Import/GeographicalUnitBaseImporter.php @@ -137,11 +137,6 @@ final class GeographicalUnitBaseImporter $this->cachingStatements[$forNumber] = $this->defaultConnection->prepare($sql); } - $this->logger->debug(self::LOG_PREFIX . ' inserting pending addresses', [ - 'number' => $forNumber, - 'first' => $this->waitingForInsert[0] ?? null, - ]); - $statement = $this->cachingStatements[$forNumber]; try { $i = 0; @@ -215,7 +210,7 @@ final class GeographicalUnitBaseImporter layer.id, unitKey FROM geographical_unit_temp JOIN chill_main_geographical_unit_layer AS layer ON layer.refid = layerKey - ON CONFLICT (unitrefid) + ON CONFLICT (layer_id, unitrefid) DO UPDATE SET geom = EXCLUDED.geom, unitname = EXCLUDED.unitname "); @@ -223,10 +218,15 @@ final class GeographicalUnitBaseImporter //3) Delete units $this->logger->info(self::LOG_PREFIX . 'soft delete adresses'); - $affected = $this->defaultConnection->executeStatement('DELETE FROM chill_main_geographical_unit - WHERE - unitrefid NOT IN (SELECT distinct unitKey FROM geographical_unit_temp) - '); + $affected = $this->defaultConnection->executeStatement('WITH to_delete AS ( + SELECT cmgu.id + FROM chill_main_geographical_unit AS cmgu + JOIN chill_main_geographical_unit_layer AS cmgul ON cmgul.id = cmgu.layer_id + JOIN geographical_unit_temp AS gut ON cmgul.refid = gut.layerKey AND cmgu.unitrefid = gut.unitKey + ) + DELETE FROM chill_main_geographical_unit + WHERE id NOT IN (SELECT id FROM to_delete) + '); $this->logger->info(self::LOG_PREFIX . 'addresses deleted', ['deleted' => $affected]); } ); diff --git a/src/Bundle/ChillMainBundle/migrations/Version20221003132620.php b/src/Bundle/ChillMainBundle/migrations/Version20221003132620.php index 39b01a0bc..e07885dce 100644 --- a/src/Bundle/ChillMainBundle/migrations/Version20221003132620.php +++ b/src/Bundle/ChillMainBundle/migrations/Version20221003132620.php @@ -17,8 +17,8 @@ final class Version20221003132620 extends AbstractMigration public function up(Schema $schema): void { $this->addSql('CREATE UNIQUE INDEX geographical_unit_layer_refid ON chill_main_geographical_unit_layer (refId)'); - $this->addSql('CREATE UNIQUE INDEX geographical_unit_refid ON chill_main_geographical_unit (unitRefId)'); - $this->addSql('CREATE INDEX chill_internal_geographical_unit_layer_geom_idx ON chill_main_geographical_unit USING GIST (geom)'); + $this->addSql('CREATE UNIQUE INDEX geographical_unit_refid ON chill_main_geographical_unit (layer_id, unitRefId)'); + $this->addSql('CREATE INDEX chill_internal_geographical_unit_layer_geom_idx ON chill_main_geographical_unit USING GIST (layer_id, geom)'); } public function down(Schema $schema): void From 52435f5331660efe8fa09a1f3476e4c8cfee1ecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 3 Oct 2022 18:20:13 +0200 Subject: [PATCH 112/120] Feature: aggregator for accompanying course by geographical level --- .../Entity/GeographicalUnit.php | 2 +- .../Entity/GeographicalUnitLayer.php | 28 ++++ .../GeographicalUnitLayerLayerRepository.php | 59 ++++++++ ...ographicalUnitLayerRepositoryInterface.php | 14 ++ .../GeographicalUnitStatAggregator.php | 143 +++++++++++++++--- .../translations/messages.fr.yml | 5 + 6 files changed, 228 insertions(+), 23 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Repository/GeographicalUnitLayerLayerRepository.php create mode 100644 src/Bundle/ChillMainBundle/Repository/GeographicalUnitLayerRepositoryInterface.php diff --git a/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php b/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php index b4c4388b6..e601ab147 100644 --- a/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php +++ b/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php @@ -44,7 +44,7 @@ class GeographicalUnit private string $unitRefId; /** - * @ORM\ManyToOne(targetEntity=GeographicalUnitLayer::class) + * @ORM\ManyToOne(targetEntity=GeographicalUnitLayer::class, inversedBy="units") */ private ?GeographicalUnitLayer $layer; diff --git a/src/Bundle/ChillMainBundle/Entity/GeographicalUnitLayer.php b/src/Bundle/ChillMainBundle/Entity/GeographicalUnitLayer.php index 54bde03ec..162d6ccf4 100644 --- a/src/Bundle/ChillMainBundle/Entity/GeographicalUnitLayer.php +++ b/src/Bundle/ChillMainBundle/Entity/GeographicalUnitLayer.php @@ -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; + } } \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Repository/GeographicalUnitLayerLayerRepository.php b/src/Bundle/ChillMainBundle/Repository/GeographicalUnitLayerLayerRepository.php new file mode 100644 index 000000000..7ef99ebac --- /dev/null +++ b/src/Bundle/ChillMainBundle/Repository/GeographicalUnitLayerLayerRepository.php @@ -0,0 +1,59 @@ +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(); + } +} \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Repository/GeographicalUnitLayerRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/GeographicalUnitLayerRepositoryInterface.php new file mode 100644 index 000000000..e7c5abdd9 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Repository/GeographicalUnitLayerRepositoryInterface.php @@ -0,0 +1,14 @@ +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; } -} \ 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 c94b4963d..9e3ddf6a5 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -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 From fc567868c16c2d08df76a36f61e82bc0d73e157b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 4 Oct 2022 15:07:07 +0200 Subject: [PATCH 113/120] [export][person] Feature: allow to filter accompanying period by geographical unit --- .../Entity/GeographicalUnit.php | 30 +++-- .../Repository/GeographicalUnitRepository.php | 57 +++++++++ .../GeographicalUnitRepositoryInterface.php | 10 ++ .../GeographicalUnitStatFilter.php | 116 +++++++++++------- 4 files changed, 164 insertions(+), 49 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepository.php create mode 100644 src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepositoryInterface.php diff --git a/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php b/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php index e601ab147..8c704a166 100644 --- a/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php +++ b/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php @@ -53,20 +53,36 @@ class GeographicalUnit return $this->id; } - public function getLayerName(): ?string - { - return $this->layerName; - } - public function getUnitName(): ?string { 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; } diff --git a/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepository.php b/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepository.php new file mode 100644 index 000000000..ed259840b --- /dev/null +++ b/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepository.php @@ -0,0 +1,57 @@ +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; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepositoryInterface.php new file mode 100644 index 000000000..10d07893b --- /dev/null +++ b/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepositoryInterface.php @@ -0,0 +1,10 @@ +em = $em; + $this->geographicalUnitRepository = $geographicalUnitRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + public function addRole(): ?string { return null; @@ -42,33 +58,32 @@ class GeographicalUnitStatFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('location', $qb->getAllAliases(), true)) { - $qb->join('acp.administrativeLocation', 'location'); - } + $subQueryDql = + '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->join('location.address', 'address'); - } - - 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']); + $qb + ->andWhere($qb->expr()->exists($subQueryDql)) + ->setParameter('acp_geog_filter_date', $data['date_calc']) + ->setParameter('acp_geog_filter_units', $data['units']) + ; } public function applyOn(): string @@ -79,23 +94,40 @@ class GeographicalUnitStatFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { $builder - ->add('date', ChillDateType::class, [ - 'data' => new DateTime(), + ->add('date_calc', ChillDateType::class, [ + '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, - 'choice_label' => static function (GeographicalUnit $u) { - return $u->getUnitName(); + 'choices' => $this->geographicalUnitRepository->findAll(), + 'choice_label' => function(GeographicalUnit $item) { + return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName(); }, + 'attr' => [ + 'class' => 'select2', + ], 'multiple' => true, - 'expanded' => true, ]); } public function describeAction($data, $format = 'string'): array { - return ['Filtered by geographic unit: only %date%', [ - '%date%' => $data['date']->format('d-m-Y'), + return ['Filtered by geographic unit: computed at %date%, only in %units%', [ + '%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'] + ) + ) ]]; } From 432acc0ace569cc249313700f16c02ff4b957940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 4 Oct 2022 22:17:16 +0200 Subject: [PATCH 114/120] [export][person] Feature: add filter and aggregator by geographical unit on person --- .../Entity/GeographicalUnit.php | 7 + .../Repository/GeographicalUnitRepository.php | 4 +- .../GeographicalUnitAggregator.php | 163 ++++++++++++++++++ .../PersonFilters/GeographicalUnitFilter.php | 134 ++++++++++++++ .../config/services/exports_person.yaml | 13 ++ .../translations/messages+intl-icu.fr.yaml | 5 + .../translations/messages.fr.yml | 2 + 7 files changed, 327 insertions(+), 1 deletion(-) create mode 100644 src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GeographicalUnitAggregator.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/GeographicalUnitFilter.php diff --git a/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php b/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php index 8c704a166..49414db27 100644 --- a/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php +++ b/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php @@ -53,6 +53,13 @@ class GeographicalUnit return $this->id; } + protected function setId(int $id): self + { + $this->id = $id; + + return $this; + } + public function getUnitName(): ?string { return $this->unitName; diff --git a/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepository.php b/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepository.php index ed259840b..3f6c09ee6 100644 --- a/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepository.php @@ -3,6 +3,8 @@ namespace Chill\MainBundle\Repository; use Chill\MainBundle\Entity\GeographicalUnit; +use Chill\MainBundle\Entity\GeographicalUnitDTO; +use Chill\MainBundle\Entity\GeographicalUnitLayer; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; @@ -33,7 +35,7 @@ class GeographicalUnitRepository implements GeographicalUnitRepositoryInterface { return $this->repository ->createQueryBuilder('gu') - ->addSelect('PARTIAL gu.{id,unitName,unitRefId,layer}') + ->select('PARTIAL gu.{id,unitName,unitRefId,layer}') ->addOrderBy('IDENTITY(gu.layer)') ->addOrderBy(('gu.unitName')) ->getQuery() diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GeographicalUnitAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GeographicalUnitAggregator.php new file mode 100644 index 000000000..816666e2f --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GeographicalUnitAggregator.php @@ -0,0 +1,163 @@ +geographicalUnitLayerRepository = $geographicalUnitLayerRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + + /** + * @inheritDoc + */ + public function getLabels($key, array $values, $data) + { + switch ($key) { + case 'geog_unit_name': + return function ($value): string { + if ('_header' === $value) { + return 'acp_geog_agg_unitname'; + } + + if (null === $value) { + return ''; + } + + return $value; + }; + + case 'geog_unit_key': + return function ($value): string { + if ('_header' === $value) { + return 'acp_geog_agg_unitrefid'; + } + + if (null === $value) { + return ''; + } + + return $value; + }; + + default: + throw new \LogicException('key not supported'); + } + } + + /** + * @inheritDoc + */ + public function getQueryKeys($data) + { + return ['geog_unit_name', 'geog_unit_key']; + } + + /** + * @inheritDoc + */ + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('date_calc', ChillDateType::class, [ + 'label' => 'Address valid at this 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()); + }, + ]); + } + + /** + * @inheritDoc + */ + public function getTitle() + { + return 'Group people by geographical unit based on his address'; + } + + /** + * @inheritDoc + */ + public function addRole(): ?string + { + return null; + } + + /** + * @inheritDoc + */ + public function alterQuery(QueryBuilder $qb, $data): void + { + $qb + ->leftJoin('person.householdAddresses', 'person_geog_agg_current_household_address') + ->leftJoin('person_geog_agg_current_household_address.address', 'person_geog_agg_address') + ->leftJoin(GeographicalUnit::class, 'person_geog_agg_geog_unit', Join::WITH, 'ST_CONTAINS(person_geog_agg_geog_unit.geom, person_geog_agg_address.point) = TRUE') + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('person_geog_agg_current_household_address'), + $qb->expr()->andX( + $qb->expr()->lte('person_geog_agg_current_household_address.validFrom', ':person_geog_agg_date'), + $qb->expr()->orX( + $qb->expr()->isNull('person_geog_agg_current_household_address.validTo'), + $qb->expr()->gt('person_geog_agg_current_household_address.validTo', ':person_geog_agg_date') + ) + ) + ) + ) + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('person_geog_agg_geog_unit'), + $qb->expr()->in('person_geog_agg_geog_unit.layer', ':person_geog_agg_layers') + ) + ) + ->setParameter('person_geog_agg_date', $data['date_calc']) + ->setParameter('person_geog_agg_layers', $data['level']) + ->addSelect('person_geog_agg_geog_unit.unitName AS geog_unit_name') + ->addSelect('person_geog_agg_geog_unit.unitRefId AS geog_unit_key') + ->addGroupBy('geog_unit_name') + ->addGroupBy('geog_unit_key') + ; + } + + /** + * @inheritDoc + */ + public function applyOn() + { + return Declarations::PERSON_TYPE; + } + + public static function getDefaultAlias(): string + { + return 'person_geog_agg'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/GeographicalUnitFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/GeographicalUnitFilter.php new file mode 100644 index 000000000..e95aff798 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/GeographicalUnitFilter.php @@ -0,0 +1,134 @@ +geographicalUnitRepository = $geographicalUnitRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + + /** + * @inheritDoc + */ + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('date_calc', ChillDateType::class, [ + 'label' => 'Compute geographical location at date', + 'required' => true, + 'data' => new \DateTimeImmutable('today'), + 'input' => 'datetime_immutable', + ]) + ->add('units', EntityType::class, [ + 'label' => 'Geographical unit', + 'placeholder' => 'Select a geographical unit', + 'class' => GeographicalUnit::class, + 'choices' => $this->geographicalUnitRepository->findAll(), + 'choice_label' => function(GeographicalUnit $item) { + return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName(); + }, + 'attr' => [ + 'class' => 'select2', + ], + 'multiple' => true, + ]); + } + + /** + * @inheritDoc + */ + public function getTitle(): string + { + return 'Filter by person\'s geographical unit (based on address)'; + } + + /** + * @inheritDoc + */ + public function describeAction($data, $format = 'string') + { + return [ + 'exports.by_person.Filtered by person\'s geographical unit (based on address) computed at datecalc, only units', + [ + 'datecalc' => $data['date_calc']->format('Y-m-d'), + 'units' => implode( + ', ', + array_map( + function (GeographicalUnit $item) { + return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName(); + }, + $data['units']->toArray() + ) + ) + ] + ]; + } + + /** + * @inheritDoc + */ + public function addRole(): ?string + { + return null; + } + + /** + * @inheritDoc + */ + public function alterQuery(QueryBuilder $qb, $data) + { + $subQuery = + 'SELECT 1 + FROM '.PersonHouseholdAddress::class.' person_filter_geog_person_household_address + JOIN person_filter_geog_person_household_address.address person_filter_geog_address + JOIN '.GeographicalUnit::class.' person_filter_geog_unit + WITH ST_CONTAINS(person_filter_geog_unit.geom, person_filter_geog_address.point) = TRUE + WHERE + person_filter_geog_person_household_address.validFrom <= :person_filter_geog_date + AND + (person_filter_geog_person_household_address.validTo IS NULL + OR person_filter_geog_person_household_address.validTo > :person_filter_geog_date) + AND + person_filter_geog_unit IN (:person_filter_geog_units) + AND + person_filter_geog_person_household_address.person = person + '; + + $qb + ->andWhere( + $qb->expr()->exists($subQuery) + ) + ->setParameter('person_filter_geog_date', $data['date_calc']) + ->setParameter('person_filter_geog_units', $data['units']) + ; + } + + /** + * @inheritDoc + */ + public function applyOn() + { + return Declarations::PERSON_TYPE; + } +} diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml index fa1c8df8d..ffffaf175 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml @@ -90,6 +90,12 @@ services: tags: - { name: chill.export_filter, alias: person_marital_status_filter } + Chill\PersonBundle\Export\Filter\PersonFilters\GeographicalUnitFilter: + autowire: true + autoconfigure: true + tags: + - { name: chill.export_filter, alias: person_geog_filter } + ## Aggregators chill.person.export.aggregator_nationality: class: Chill\PersonBundle\Export\Aggregator\PersonAggregators\NationalityAggregator @@ -132,3 +138,10 @@ services: autoconfigure: true tags: - { name: chill.export_aggregator, alias: person_household_position_aggregator } + + Chill\PersonBundle\Export\Aggregator\PersonAggregators\GeographicalUnitAggregator: + autowire: true + autoconfigure: true + tags: + - { name: chill.export_aggregator, alias: person_geog_aggregator } + diff --git a/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml b/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml index 50cb9f0e6..091b9232f 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml +++ b/src/Bundle/ChillPersonBundle/translations/messages+intl-icu.fr.yaml @@ -130,3 +130,8 @@ periods: many {Masquer # parcours clôturés ou anciens parcours} other {Masquer # parcours clôturés ou anciens parcours} } + +exports: + by_person: + Filtered by person\'s geographical unit (based on address) computed at date, only units: + "Filtré par zone géographique sur base de l'adresse, calculé à {datecalc, date, short}, seulement les zones suivantes: {units}" diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 9e3ddf6a5..12a193ac2 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -462,6 +462,8 @@ 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 +Group people by geographical unit based on his address: Grouper les personnes par zone géographique (sur base de l'adresse) +Filter by person's geographical unit (based on address): Filter les personnes par zone géographique (sur base de l'adresse) Filter by socialaction: Filtrer les parcours par action d'accompagnement Accepted socialactions: Actions d'accompagnement From 58a1af0c78d1c64831c082f0ad2316f513007661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 5 Oct 2022 09:58:18 +0200 Subject: [PATCH 115/120] installation instructions --- docs/source/installation/index.rst | 113 +++++++++++++++++++---------- 1 file changed, 73 insertions(+), 40 deletions(-) diff --git a/docs/source/installation/index.rst b/docs/source/installation/index.rst index 8d84b795f..9c44d81dd 100644 --- a/docs/source/installation/index.rst +++ b/docs/source/installation/index.rst @@ -21,31 +21,45 @@ Requirements - This project use `docker `_ to be run. As a developer, use `docker-compose `_ to bootstrap a dev environment in a glance. You do not need any other dependencies ; - Make is used to automate scripts. -Installation in development mode -******************************** +Installation +************ + +If you plan to run chill in production: + +1. install it locally first, and check if everything is ok on your local machine; +2. once ready, build the image from your local machine, and deploy them. + +If you want to develop some bundles, the first step is sufficient (until you deploy on production). + 1. Get the code =============== -Clone or download the chill-app project and `cd` into the main directory. +Clone or download the chill-skeleton project and `cd` into the main directory. .. code-block:: bash - git clone https://gitlab.com/Chill-Projet/chill-app.git + git clone https://gitlab.com/Chill-Projet/chill-skeleton-basic.git cd chill-app As a developer, the code will stay on your computer and will be executed in docker container. To avoid permission problem, the code should be run with the same uid/gid from your current user. This is why we get your current user id with the command ``id -u`` in each following scripts. -2. Prepare your variables -========================= +2. Prepare your variables and docker-compose +============================================ -Have a look at the variable in ``.env.dist`` and in ``app/config/parameters.yml.dist`` and check if you need to adapt them. If they do not adapt with your need, or if some are missing: +Have a look at the variable in ``.env`` and check if you need to adapt them. If they do not adapt with your need, or if some are missing: -1. copy the file as ``.env``: ``cp .env.dist .env`` +1. copy the file as ``.env.local``: ``cp .env .env.local`` 2. you may replace some variables inside ``.env`` +Prepare also you docker-compose installation, and adapt it to your needs: + +1. If you plan to deploy on dev, copy the file ``docker-compose.override.dev.template.yml`` to ``docker-compose.override.yml``. +2. adapt to your needs. + + **Note**: If you intend to use the bundle ``Chill-Doc-Store``, you will need to configure and install an openstack object storage container with temporary url middleware. You will have to configure `secret keys `_. 3. Run the bootstrap script @@ -64,6 +78,17 @@ This script will : 3. install the php dependencies 4. build assets +.. warning:: + + The script will work only if the binary ``docker-compose`` is located into your ``PATH``. If you use ``compose`` as a docker plugin, + you can simulate this binary by creating this file at (for instance), ``/usr/local/bin/docker-compose`` (and run ``chmod +x /usr/local/bin/docker-compose``): + + .. code-block:: bash + + #!/bin/bash + + /usr/bin/docker compose "$@" + .. note:: @@ -87,13 +112,20 @@ This script will : .. code-block:: bash - make migrate + # mount into to container + ./docker-php.sh + # and load fixtures + bin/console doctrine:migrations:migrate + Chill will be available at ``http://localhost:8001.`` Currently, there isn't any user or data. To add fixtures, run .. code-block:: bash - docker-compose exec --user $(id -u) php bin/console doctrine:fixtures:load --purge-with-truncate + # mount into to container + ./docker-php.sh + # and load fixtures + bin/console doctrine:fixtures:load --purge-with-truncate There are several users available: @@ -112,16 +144,18 @@ Add a Gitlab token to ensure that you get always the source code: 1. generate a gitlab token there: https://gitlab.com/oauth/token 2. run this command (in php container, at the app's root): :code:`composer config gitlab-token.gitlab.com ` -The auth token should appears now in the directory :code:`.composer`: + The auth token should appears now in the directory :code:`.composer`: -.. code-block: bash + .. code-block: bash + + $ cat .composer/auth.json + { + "gitlab-token": { + "gitlab.com": "" + } + } +3. run ``composer update`` again (into the php container) - $ cat .composer/auth.json - { - "gitlab-token": { - "gitlab.com": "" - } - } See also "how to switch branch and get new dependencies". @@ -145,7 +179,7 @@ How to execute the console ? .. code-block:: bash # if a container is running - docker-compose exec --user $(id -u) php bin/console + ./docker-php.sh # if not docker-compose run --user $(id -u) php bin/console @@ -155,7 +189,8 @@ How to create the database schema (= run migrations) ? .. code-block:: bash # if a container is running - docker-compose exec --user $(id -u) php bin/console doctrine:migrations:migrate + ./docker-php.sh + bin/console doctrine:migrations:migrate # if not docker-compose run --user $(id -u) php bin/console doctrine:migrations:migrate @@ -173,7 +208,8 @@ How to load fixtures ? (development mode only) .. code-block:: bash # if a container is running - docker-compose exec --user $(id -u) php bin/console doctrine:fixtures:load + ./docker-php.sh + bin/console doctrine:fixtures:load # if not docker-compose run --user $(id -u) php bin/console doctrine:fixtures:load @@ -183,7 +219,7 @@ How to open a terminal in the project .. code-block:: bash # if a container is running - docker-compose exec --user $(id -u) php /bin/bash + ./docker-php.sh # if not docker-compose run --user $(id -u) php /bin/bash @@ -193,21 +229,22 @@ How to run composer ? .. code-block:: bash # if a container is running - docker-compose exec --user $(id -u) php ./composer.phar + ./docker-php.sh + composer # if not - docker-compose run --user $(id -u) php ./composer.phar + docker-compose run --user $(id -u) php composer How to access to PGADMIN ? ========================== -Pgadmin is installed with docker-compose. +Pgadmin is installed with docker-compose, and is available **only if you uncomment the appropriate lines into ``docker-compose.override.yml``. You can access it at ``http://localhost:8002``. Credentials: -- login: admin@chill.social -- password: password +- login: from the variable you set into ``docker-composer.override.yml`` +- password: same :-) How to run tests ? ================== @@ -221,13 +258,15 @@ Exemple, for running test inside `main` bundle: .. code-block:: bash # mount into the php image - docker-compose run --user $(id -u) php /bin/bash + ./docker-php.sh # cd into main directory - cd vendor/chill-project/main + cd vendor/chill-project/chill-bundles # download deps - php ../../../composer.phar install + git submodule init + git submodule update + composer install # run tests - /vendor/bin/phpunit + bin/phpunit src/Bundle/path/to/your/test How to run webpack interactively ================================ @@ -253,13 +292,6 @@ In order to do that without pain, use those steps: 2. mount into the php container, and run `composer update` -Build the documentation API -=========================== - -A basic configuration of `sami `_ is embedded within the project. - -A configuration file for `phpDocumentor `_ is present. - Error `An exception has been thrown during the rendering of a template ("Asset manifest file "/var/www/app/web/build/manifest.json" does not exist.").` on first run ==================================================================================================================================================================== @@ -273,7 +305,8 @@ Currently, to run this software in production, the *state of the art* is the fol 1. Run the software locally and tweak the configuration to your needs ; 2. Build the image and store them into a private container registry. This can be done using :code:`make build-and-push-image`. - To be sure to target the correct container registry, you have to adapt the values ``IMAGE_NGINX`` and ``IMAGE_PHP`` date in the ``.env`` file. + To be sure to target the correct container registry, you have to adapt the image names into your ``docker-compose.override.yml`` file. +3. Push the image on your registry, or upload them to the destination machine using ``docker image save`` and ``docker image load``. 3. Run the image on your production server, using docker-compose or eventually docker stack. You have to customize the variable set in docker-compose. See also the :ref:`running-production-tips-and-tricks` below. @@ -305,7 +338,7 @@ It is worth having an eye on the configuration of logstash container. Design principles ***************** -Why the DB URL is set in environment, and not in parameters.yml ? +Why the DB URL is also set in environment, and not in .env file ? ================================================================= Because, at startup, a script does check the db is up and, if not, wait for a couple of seconds before running ``entrypoint.sh``. For avoiding double configuration, the configuration of the PHP app takes his configuration from environment also (and it will be standard in future releases, with symfony 4.0). From a90e87b1be8f278113b191816e88ad6619bf3101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 5 Oct 2022 10:22:30 +0200 Subject: [PATCH 116/120] more explanation for installation instructions --- docs/source/installation/index.rst | 86 ++++++++++++++++++------------ 1 file changed, 51 insertions(+), 35 deletions(-) diff --git a/docs/source/installation/index.rst b/docs/source/installation/index.rst index 9c44d81dd..00f27ed69 100644 --- a/docs/source/installation/index.rst +++ b/docs/source/installation/index.rst @@ -14,6 +14,13 @@ Installation & Usage #################### +.. toctree:: + :maxdepth: 2 + + prod.rst + prod-calendar-sms-sending.rst + msgraph-configure.rst + Requirements ************ @@ -46,6 +53,35 @@ Clone or download the chill-skeleton project and `cd` into the main directory. As a developer, the code will stay on your computer and will be executed in docker container. To avoid permission problem, the code should be run with the same uid/gid from your current user. This is why we get your current user id with the command ``id -u`` in each following scripts. +2. Prepare composer to download the sources +=========================================== + +As you are running in dev, you must configure an auth token for getting the source code. + +.. warning + + If you skip this part, the code will be downloaded from dist instead of source (with git repository). You will probably replace the source manually, but the next time you will run ```composer update```, your repository will be replaced and you might loose something. + +1. Create a personal access token from https://gitlab.com/-/profile/personal_access_tokens, with the `read_api` scope. +2. add a file called ```.composer/auth.json``` with this content: + + .. code-block:: json + + { + "gitlab-token": { + "gitlab.com": "glXXX-XXXXXXXXXXXXXXXXXXXX" + } + } + +2. Prepare your variables and environment +========================================= + +Copy ```docker-compose.override.dev.yml``` into ```docker-compose.override.yml``` + +.. code-block:: bash + + cp docker-compose.override.dev.template.yml docker-compose.override.yml + 2. Prepare your variables and docker-compose ============================================ @@ -74,7 +110,7 @@ This script can be run using `make` This script will : 1. force docker-compose to, eventually, pull the base images and build the image used by this project ; -2. run an install script to download `composer `_ ; +2. run an install script to download `composer `_ ; 3. install the php dependencies 4. build assets @@ -136,30 +172,6 @@ The password is always ``password``. Now, read `Operations` below. -Prepare for development -*********************** - -Add a Gitlab token to ensure that you get always the source code: - -1. generate a gitlab token there: https://gitlab.com/oauth/token -2. run this command (in php container, at the app's root): :code:`composer config gitlab-token.gitlab.com ` - - The auth token should appears now in the directory :code:`.composer`: - - .. code-block: bash - - $ cat .composer/auth.json - { - "gitlab-token": { - "gitlab.com": "" - } - } -3. run ``composer update`` again (into the php container) - - - -See also "how to switch branch and get new dependencies". - Operations ********** @@ -167,7 +179,7 @@ Operations Build assets ============ -run those commands: +run those commands: .. code-block:: bash @@ -180,7 +192,7 @@ How to execute the console ? # if a container is running ./docker-php.sh - # if not + # if not docker-compose run --user $(id -u) php bin/console How to create the database schema (= run migrations) ? @@ -191,7 +203,7 @@ How to create the database schema (= run migrations) ? # if a container is running ./docker-php.sh bin/console doctrine:migrations:migrate - # if not + # if not docker-compose run --user $(id -u) php bin/console doctrine:migrations:migrate @@ -210,7 +222,7 @@ How to load fixtures ? (development mode only) # if a container is running ./docker-php.sh bin/console doctrine:fixtures:load - # if not + # if not docker-compose run --user $(id -u) php bin/console doctrine:fixtures:load How to open a terminal in the project @@ -220,7 +232,7 @@ How to open a terminal in the project # if a container is running ./docker-php.sh - # if not + # if not docker-compose run --user $(id -u) php /bin/bash How to run composer ? @@ -231,7 +243,7 @@ How to run composer ? # if a container is running ./docker-php.sh composer - # if not + # if not docker-compose run --user $(id -u) php composer How to access to PGADMIN ? @@ -253,7 +265,7 @@ Tests reside inside the installed bundles. You must `cd` into that directory, do **Note**: some bundle require the fixture to be executed. See the dedicated _how-tos_. -Exemple, for running test inside `main` bundle: +Exemple, for running test inside `main` bundle: .. code-block:: bash @@ -278,10 +290,14 @@ How to switch the branch for chill-bundles, and get new dependencies During development, you will switch to new branches for chill-bundles. As long as the dependencies are equals, this does not cause any problem. But sometimes, a new branch introduces a new dependency, and you must download it. +.. warning:: + + Ensure that you have gitlab-token ready before updating your branches. See above. + In order to do that without pain, use those steps: 0. Ensuire you have a token, set -1. at the app's root, update the `composer.json` to your current branch: +1. at the app's root, update the ``composer.json`` to your current branch: .. code-block:: json @@ -290,7 +306,7 @@ In order to do that without pain, use those steps: "chill-bundles": "dev-@dev" } -2. mount into the php container, and run `composer update` +2. mount into the php container (``./docker-php.sh``), and run ``composer update`` Error `An exception has been thrown during the rendering of a template ("Asset manifest file "/var/www/app/web/build/manifest.json" does not exist.").` on first run ==================================================================================================================================================================== @@ -304,7 +320,7 @@ Currently, to run this software in production, the *state of the art* is the fol 1. Run the software locally and tweak the configuration to your needs ; 2. Build the image and store them into a private container registry. This can be done using :code:`make build-and-push-image`. - + To be sure to target the correct container registry, you have to adapt the image names into your ``docker-compose.override.yml`` file. 3. Push the image on your registry, or upload them to the destination machine using ``docker image save`` and ``docker image load``. 3. Run the image on your production server, using docker-compose or eventually docker stack. You have to customize the variable set in docker-compose. From 491570a21caca71df44dfbe7a1a2b1448aa895d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 5 Oct 2022 14:46:03 +0200 Subject: [PATCH 117/120] [export] Fixed: the scope aggregator (accompanying course) group by period's scope (and the label didn't show this) + extract ScopeRepository and create ScopeRepositoryInterface --- .../Repository/ScopeRepository.php | 7 +++-- .../Repository/ScopeRepositoryInterface.php | 28 +++++++++++++++++++ .../ScopeAggregator.php | 2 +- .../translations/messages.fr.yml | 3 +- 4 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Repository/ScopeRepositoryInterface.php diff --git a/src/Bundle/ChillMainBundle/Repository/ScopeRepository.php b/src/Bundle/ChillMainBundle/Repository/ScopeRepository.php index 5b3efe658..6336addbd 100644 --- a/src/Bundle/ChillMainBundle/Repository/ScopeRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/ScopeRepository.php @@ -14,9 +14,10 @@ namespace Chill\MainBundle\Repository; use Chill\MainBundle\Entity\Scope; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\QueryBuilder; use Doctrine\Persistence\ObjectRepository; -final class ScopeRepository implements ObjectRepository +final class ScopeRepository implements ScopeRepositoryInterface { private EntityRepository $repository; @@ -25,7 +26,7 @@ final class ScopeRepository implements ObjectRepository $this->repository = $entityManager->getRepository(Scope::class); } - public function createQueryBuilder($alias, $indexBy = null) + public function createQueryBuilder($alias, $indexBy = null): QueryBuilder { return $this->repository->createQueryBuilder($alias, $indexBy); } @@ -59,7 +60,7 @@ final class ScopeRepository implements ObjectRepository return $this->repository->findOneBy($criteria, $orderBy); } - public function getClassName() + public function getClassName(): string { return Scope::class; } diff --git a/src/Bundle/ChillMainBundle/Repository/ScopeRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/ScopeRepositoryInterface.php new file mode 100644 index 000000000..1764db8d3 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Repository/ScopeRepositoryInterface.php @@ -0,0 +1,28 @@ + Date: Wed, 5 Oct 2022 14:57:15 +0200 Subject: [PATCH 118/120] [Export] Feature: create a aggregator for referrer's main scope on aggregator --- .../ReferrerScopeAggregator.php | 133 ++++++++++++++++++ .../ReferrerScopeAggregatorTest.php | 63 +++++++++ .../services/exports_accompanying_course.yaml | 9 +- .../translations/messages.fr.yml | 8 ++ 4 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregator.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregatorTest.php diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregator.php new file mode 100644 index 000000000..3701371a7 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregator.php @@ -0,0 +1,133 @@ +scopeRepository = $scopeRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + + /** + * @inheritDoc + */ + public function getLabels($key, array $values, $data) + { + return function ($value) { + if ('_header' === $value) { + return 'export.aggregator.course.by_user_scope.Referrer\'s scope'; + } + + if (null === $value) { + return ''; + } + + $scope = $this->scopeRepository->find($value); + + if (null === $scope) { + throw new \LogicException('no scope found with this id: ' . $value); + } + + return $this->translatableStringHelper->localize($scope->getName()); + }; + } + + /** + * @inheritDoc + */ + public function getQueryKeys($data) + { + return [self::SCOPE_KEY]; + } + + /** + * @inheritDoc + */ + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('date_calc', ChillDateType::class, [ + 'input' => 'datetime_immutable', + 'data' => new \DateTimeImmutable('now'), + 'label' => 'export.aggregator.course.by_user_scope.Computation date for referrer', + 'required' => true, + ]); + } + + /** + * @inheritDoc + */ + public function getTitle() + { + return 'export.aggregator.course.by_user_scope.Group course by referrer\'s scope'; + } + + /** + * @inheritDoc + */ + public function addRole(): ?string + { + return null; + } + + /** + * @inheritDoc + */ + public function alterQuery(QueryBuilder $qb, $data) + { + $userHistory = 'acp_agg_refscope_user_history'; + $ref = 'acp_agg_refscope_user_history_ref'; + $scopeName = self::SCOPE_KEY; + $dateCalc = 'acp_agg_refscope_user_history_date_calc'; + + $qb + ->leftJoin('acp.userHistories', $userHistory) + ->leftJoin($userHistory.'.user', $ref) + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull($userHistory), + $qb->expr()->andX( + $qb->expr()->lte($userHistory.'.startDate', ':'.$dateCalc), + $qb->expr()->orX( + $qb->expr()->isNull($userHistory.'.endDate'), + $qb->expr()->lt($userHistory.'.endDate', ':'.$dateCalc) + ) + ) + ) + ) + ->setParameter($dateCalc, $data['date_calc']); + + // add groups + $qb + ->addSelect('IDENTITY('.$ref.'.mainScope) AS '.$scopeName) + ->addGroupBy($scopeName) + ; + } + + /** + * @inheritDoc + */ + public function applyOn() + { + return Declarations::ACP_TYPE; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregatorTest.php new file mode 100644 index 000000000..6069a024d --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregatorTest.php @@ -0,0 +1,63 @@ +prophesize(TranslatableStringHelperInterface::class); + $translatableStringHelper->localize(Argument::type('array'))->willReturn('localized'); + + $scopeRepository = $this->prophesize(ScopeRepositoryInterface::class); + $scopeRepository->find(Argument::type('int'))->willReturn( + (new Scope())->setName(['fr' => 'scope']) + ); + + return new ReferrerScopeAggregator( + $scopeRepository->reveal(), + $translatableStringHelper->reveal() + ); + } + + public function getFormData() + { + return [ + [ + 'date_calc' => new \DateTimeImmutable('now') + ] + ]; + } + + public function getQueryBuilders() + { + if (null === self::$kernel) { + self::bootKernel(); + } + + $em = self::$container->get(EntityManagerInterface::class); + + return [ + $em->createQueryBuilder() + ->select('count(acp.id)') + ->from(AccompanyingPeriod::class, 'acp') + ->join('acp.scopes', 'acpscope') + , + ]; + } + + +} diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml index 8a8b20cbe..874f28b72 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_course.yaml @@ -148,7 +148,7 @@ services: autowire: true autoconfigure: true tags: - - { name: chill.export_aggregator, alias: accompanyingcourse_referrer_scope_aggregator } + - { name: chill.export_aggregator, alias: accompanyingcourse_scope_aggregator } chill.person.export.aggregator_referrer_job: class: Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\JobAggregator @@ -255,3 +255,10 @@ services: tags: - { name: chill.export_aggregator, alias: accompanyingcourse_duration_aggregator } + Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\REferrerScopeAggregator: + autoconfigure: true + autowire: true + tags: + - { name: chill.export_aggregator, alias: accompanyingcourse_ref_scope_aggregator } + + diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index d8c60fca4..664498a95 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -941,3 +941,11 @@ reassign: notification: Notify referrer: Notifier le référent Notify any: Notifier d'autres utilisateurs + +export: + aggregator: + course: + by_user_scope: + Group course by referrer's scope: Grouper les parcours par service du référent + Computation date for referrer: Date à laquelle le référent était actif + Referrer's scope: Service du référent de parcours From c1d96af85f89a50c3b8a3f24f1871b9217f561e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 5 Oct 2022 15:08:53 +0200 Subject: [PATCH 119/120] fix cs --- .../BySocialActionAggregator.php | 69 +++---- .../BySocialIssueAggregator.php | 74 ++++---- .../ACPAggregators/ByThirdpartyAggregator.php | 73 ++++---- .../ACPAggregators/ByUserAggregator.php | 73 ++++---- .../ACPAggregators/DateAggregator.php | 122 +++++++------ .../ACPAggregators/LocationTypeAggregator.php | 72 ++++---- .../ACPAggregators/UserScopeAggregator.php | 72 ++++---- .../Aggregator/ActivityTypeAggregator.php | 3 +- .../Aggregator/ActivityUserAggregator.php | 4 +- .../Export/Declarations.php | 4 +- .../LinkedToACP/AvgActivityDuration.php | 37 ++-- .../LinkedToACP/AvgActivityVisitDuration.php | 37 ++-- .../Export/LinkedToACP/CountActivity.php | 17 +- .../LinkedToACP/SumActivityDuration.php | 37 ++-- .../LinkedToACP/SumActivityVisitDuration.php | 36 ++-- .../Export/LinkedToPerson/CountActivity.php | 18 +- .../Export/LinkedToPerson/ListActivity.php | 14 +- .../LinkedToPerson/StatActivityDuration.php | 12 +- .../ACPFilters/BySocialActionFilter.php | 77 ++++---- .../Filter/ACPFilters/BySocialIssueFilter.php | 75 ++++---- .../Export/Filter/ACPFilters/ByUserFilter.php | 78 ++++---- .../Filter/ACPFilters/EmergencyFilter.php | 74 ++++---- .../Filter/ACPFilters/LocationTypeFilter.php | 82 +++++---- .../Filter/ACPFilters/SentReceivedFilter.php | 67 +++---- .../Export/Filter/ACPFilters/UserFilter.php | 76 ++++---- .../Filter/ACPFilters/UserScopeFilter.php | 85 +++++---- .../Export/Filter/ActivityDateFilter.php | 3 +- .../Config/ConfigRepository.php | 4 +- .../Export/Aggregator/AgentAggregator.php | 19 +- .../Aggregator/CancelReasonAggregator.php | 72 ++++---- .../Export/Aggregator/JobAggregator.php | 72 ++++---- .../Export/Aggregator/LocationAggregator.php | 68 +++---- .../Aggregator/LocationTypeAggregator.php | 71 ++++---- .../Export/Aggregator/MonthYearAggregator.php | 70 ++++---- .../Export/Aggregator/ScopeAggregator.php | 72 ++++---- .../Export/Export/CountAppointments.php | 29 +-- .../Export/StatAppointmentAvgDuration.php | 38 ++-- .../Export/StatAppointmentSumDuration.php | 37 ++-- .../Export/Filter/AgentFilter.php | 74 ++++---- .../Export/Filter/BetweenDatesFilter.php | 68 +++---- .../Export/Filter/JobFilter.php | 57 +++--- .../Export/Filter/ScopeFilter.php | 59 +++--- .../Controller/ExportController.php | 32 ++-- .../ChillMainBundle/Doctrine/DQL/Extract.php | 15 +- .../ChillMainBundle/Doctrine/DQL/ToChar.php | 14 +- .../ChillMainBundle/Export/ExportManager.php | 4 +- .../Import/AddressReferenceBaseImporter.php | 5 +- .../AddressReferenceBaseImporterTest.php | 34 +++- .../Import/PostalCodeBaseImporterTest.php | 36 ++-- .../NotificationOnTransitionTest.php | 34 ++-- ...ntityWorkflowTransitionEventSubscriber.php | 35 ++-- .../NotificationOnTransition.php | 8 +- .../migrations/Version20220730204216.php | 17 +- .../migrations/Version20220829132409.php | 21 ++- .../HouseholdCompositionTypeController.php | 2 +- .../ChillPersonExtension.php | 1 + .../Entity/MaritalStatus.php | 1 - .../AdministrativeLocationAggregator.php | 89 ++++------ .../ClosingMotiveAggregator.php | 93 +++++----- .../ConfidentialAggregator.php | 105 +++++------ .../DurationAggregator.php | 118 ++++++------ .../EmergencyAggregator.php | 105 +++++------ .../EvaluationAggregator.php | 95 +++++----- .../IntensityAggregator.php | 99 +++++------ .../JobAggregator.php | 94 +++++----- .../OriginAggregator.php | 96 +++++----- .../ReferrerAggregator.php | 6 +- .../ScopeAggregator.php | 94 +++++----- .../SocialActionAggregator.php | 91 +++++----- .../SocialIssueAggregator.php | 92 ++++------ .../StepAggregator.php | 114 ++++++------ .../EvaluationTypeAggregator.php | 88 ++++----- .../ChildrenNumberAggregator.php | 101 +++++------ .../CompositionAggregator.php | 104 +++++------ .../HouseholdPositionAggregator.php | 6 +- .../MaritalStatusAggregator.php | 1 - .../NationalityAggregator.php | 7 +- .../ActionTypeAggregator.php | 1 - .../SocialWorkAggregators/GoalAggregator.php | 1 - .../SocialWorkAggregators/JobAggregator.php | 94 +++++----- .../ReferrerAggregator.php | 6 +- .../ResultAggregator.php | 1 - .../SocialWorkAggregators/ScopeAggregator.php | 94 +++++----- .../ChillPersonBundle/Export/Declarations.php | 12 +- .../Export/Export/CountAccompanyingCourse.php | 24 +-- .../Export/Export/CountEvaluation.php | 68 +++---- .../Export/Export/CountHousehold.php | 67 +++---- .../Export/Export/CountPerson.php | 11 +- .../CountPersonWithAccompanyingCourse.php | 69 +++---- .../Export/Export/CountSocialWorkActions.php | 27 ++- .../Export/Export/ListPerson.php | 13 +- .../Export/Export/ListPersonDuplicate.php | 10 +- .../Export/StatAccompanyingCourseDuration.php | 66 ++----- .../ActiveOnDateFilter.php | 73 ++++---- .../ActiveOneDayBetweenDatesFilter.php | 81 ++++----- .../ActivityTypeFilter.php | 95 +++++----- .../AdministrativeLocationFilter.php | 92 +++++----- .../ClosingMotiveFilter.php | 96 +++++----- .../ConfidentialFilter.php | 84 +++++---- .../CurrentUserJobFilter.php | 26 +-- .../CurrentUserScopeFilter.php | 31 ++-- .../EmergencyFilter.php | 80 +++++---- .../EvaluationFilter.php | 95 +++++----- .../GeographicalUnitStatFilter.php | 89 +++++----- .../IntensityFilter.php | 80 +++++---- .../OpenBetweenDatesFilter.php | 81 ++++----- .../OriginFilter.php | 93 +++++----- .../ReferrerFilter.php | 93 +++++----- .../RequestorFilter.php | 101 +++++------ .../SocialActionFilter.php | 84 ++++----- .../SocialIssueFilter.php | 168 +++++++++--------- .../AccompanyingCourseFilters/StepFilter.php | 66 +++---- .../EvaluationTypeFilter.php | 93 +++++----- .../EvaluationFilters/MaxDateFilter.php | 85 ++++----- .../HouseholdFilters/CompositionFilter.php | 112 ++++++------ .../Export/Filter/PersonFilters/AgeFilter.php | 8 +- .../Filter/PersonFilters/BirthdateFilter.php | 5 +- .../PersonFilters/DeadOrAliveFilter.php | 10 +- .../PersonFilters/FamilySituationFilter.php | 2 - .../ResidentialAddressAtThirdpartyFilter.php | 41 +++-- .../ResidentialAddressAtUserFilter.php | 6 +- .../Filter/SocialWorkFilters/JobFilter.php | 60 +++---- .../SocialWorkFilters/ReferrerFilter.php | 93 +++++----- .../Filter/SocialWorkFilters/ScopeFilter.php | 59 +++--- .../SocialWorkTypeFilter.php | 168 +++++++++--------- .../Form/HouseholdCompositionTypeType.php | 3 +- .../AccompanyingPeriodWorkRepository.php | 10 +- .../Repository/PersonRepository.php | 10 +- .../Authorization/AccompanyingPeriodVoter.php | 15 +- .../Export/CountAccompanyingCourseTest.php | 24 +-- .../Export/CountSocialWorkActionsTest.php | 25 +-- .../StatAccompanyingCourseDurationTest.php | 18 +- .../Export/Filter/ActiveOnDateFilterTest.php | 29 +-- .../ActiveOneDayBetweenDatesFilterTest.php | 31 ++-- .../Export/Filter/ActivityTypeFilterTest.php | 29 +-- .../AdministrativeLocationFilterTest.php | 28 +-- .../Export/Filter/ClosingMotiveFilterTest.php | 29 +-- .../Export/Filter/ConfidentialFilterTest.php | 29 +-- .../Export/Filter/EmergencyFilterTest.php | 20 ++- .../Export/Filter/EvaluationFilterTest.php | 28 +-- .../Filter/GeographicalUnitStatFilterTest.php | 31 ++-- .../Export/Filter/IntensityFilterTest.php | 29 +-- .../Tests/Export/Filter/JobFilterTest.php | 28 +-- .../Filter/OpenBetweenDatesFilterTest.php | 31 ++-- .../Tests/Export/Filter/OriginFilterTest.php | 29 +-- .../Export/Filter/ReferrerFilterTest.php | 28 +-- .../Export/Filter/RequestorFilterTest.php | 27 +-- .../Tests/Export/Filter/ScopeFilterTest.php | 29 +-- .../Export/Filter/SocialActionFilterTest.php | 28 +-- .../Export/Filter/SocialIssueFilterTest.php | 29 +-- .../Tests/Export/Filter/StepFilterTest.php | 27 +-- .../Tests/Export/Filter/UserJobFilterTest.php | 27 +-- .../Export/Filter/UserScopeFilterTest.php | 28 +-- 153 files changed, 3797 insertions(+), 3874 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialActionAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialActionAggregator.php index 269924a8a..62b160bc1 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialActionAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialActionAggregator.php @@ -1,5 +1,12 @@ actionRepository = $actionRepository; } - public function getLabels($key, array $values, $data) - { - return function($value) { - if ('_header' === $value) { - return 'Social action'; - } - - $sa = $this->actionRepository->find($value); - - return $this->actionRender->renderString($sa, []); - }; - } - - public function getQueryKeys($data): array - { - return ['socialaction_aggregator']; - } - - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - public function getTitle(): string - { - return 'Group activity by linked socialaction'; - } - public function addRole() { return null; @@ -61,7 +40,7 @@ class BySocialActionAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if(!in_array('socialaction', $qb->getAllAliases())) { + if (!in_array('socialaction', $qb->getAllAliases(), true)) { $qb->join('activity.socialActions', 'socialaction'); } @@ -80,4 +59,32 @@ class BySocialActionAggregator implements AggregatorInterface { return Declarations::ACTIVITY_ACP; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value) { + if ('_header' === $value) { + return 'Social action'; + } + + $sa = $this->actionRepository->find($value); + + return $this->actionRender->renderString($sa, []); + }; + } + + public function getQueryKeys($data): array + { + return ['socialaction_aggregator']; + } + + public function getTitle(): string + { + return 'Group activity by linked socialaction'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialIssueAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialIssueAggregator.php index 0e4b9ab8e..ff70e54b9 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialIssueAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/BySocialIssueAggregator.php @@ -1,5 +1,12 @@ issueRender = $issueRender; } - public function getLabels($key, array $values, $data) - { - return function ($value): string { - - if ($value === '_header') { - return 'Social issues'; - } - - $i = $this->issueRepository->find($value); - - return $this->issueRender->renderString($i, []); - }; - } - - public function getQueryKeys($data): array - { - return ['socialissue_aggregator']; - } - - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - public function getTitle(): string - { - return 'Group activity by linked socialissue'; - } - public function addRole() { return null; @@ -62,7 +40,7 @@ class BySocialIssueAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('socialissue', $qb->getAllAliases())) { + if (!in_array('socialissue', $qb->getAllAliases(), true)) { $qb->join('activity.socialIssues', 'socialissue'); } @@ -81,4 +59,32 @@ class BySocialIssueAggregator implements AggregatorInterface { return Declarations::ACTIVITY_ACP; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Social issues'; + } + + $i = $this->issueRepository->find($value); + + return $this->issueRender->renderString($i, []); + }; + } + + public function getQueryKeys($data): array + { + return ['socialissue_aggregator']; + } + + public function getTitle(): string + { + return 'Group activity by linked socialissue'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByThirdpartyAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByThirdpartyAggregator.php index 5a9e633ef..ce70a9ddb 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByThirdpartyAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByThirdpartyAggregator.php @@ -1,5 +1,12 @@ thirdPartyRender = $thirdPartyRender; } - public function getLabels($key, array $values, $data) - { - return function ($value): string { - if ($value === '_header') { - return 'Accepted thirdparty'; - } - - $tp = $this->thirdPartyRepository->find($value); - - return $this->thirdPartyRender->renderString($tp, []); - }; - } - - public function getQueryKeys($data): array - { - return ['thirdparty_aggregator']; - } - - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - public function getTitle(): string - { - return 'Group activity by linked thirdparties'; - } - public function addRole() { return null; @@ -61,7 +40,7 @@ class ByThirdpartyAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('thirdparty', $qb->getAllAliases())) { + if (!in_array('thirdparty', $qb->getAllAliases(), true)) { $qb->join('activity.thirdParties', 'thirdparty'); } @@ -80,4 +59,32 @@ class ByThirdpartyAggregator implements AggregatorInterface { return Declarations::ACTIVITY_ACP; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Accepted thirdparty'; + } + + $tp = $this->thirdPartyRepository->find($value); + + return $this->thirdPartyRender->renderString($tp, []); + }; + } + + public function getQueryKeys($data): array + { + return ['thirdparty_aggregator']; + } + + public function getTitle(): string + { + return 'Group activity by linked thirdparties'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByUserAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByUserAggregator.php index 95c9998b8..d57410c4b 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByUserAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByUserAggregator.php @@ -1,5 +1,12 @@ userRender = $userRender; } - public function getLabels($key, array $values, $data) - { - return function ($value): string { - if ($value === '_header') { - return 'Accepted users'; - } - - $u = $this->userRepository->find($value); - - return $this->userRender->renderString($u, []); - }; - } - - public function getQueryKeys($data): array - { - return ['users_aggregator']; - } - - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - public function getTitle(): string - { - return 'Group activity by linked users'; - } - public function addRole() { return null; @@ -61,7 +40,7 @@ class ByUserAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('user', $qb->getAllAliases())) { + if (!in_array('user', $qb->getAllAliases(), true)) { $qb->join('activity.users', 'user'); } @@ -80,4 +59,32 @@ class ByUserAggregator implements AggregatorInterface { return Declarations::ACTIVITY_ACP; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Accepted users'; + } + + $u = $this->userRepository->find($value); + + return $this->userRender->renderString($u, []); + }; + } + + public function getQueryKeys($data): array + { + return ['users_aggregator']; + } + + public function getTitle(): string + { + return 'Group activity by linked users'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php index 1c2f02d3b..3463b1c59 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php @@ -1,12 +1,19 @@ 'year', ]; - private CONST DEFAULT_CHOICE = 'year'; + private const DEFAULT_CHOICE = 'year'; private TranslatorInterface $translator; @@ -31,54 +38,6 @@ class DateAggregator implements AggregatorInterface $this->translator = $translator; } - public function getLabels($key, array $values, $data) - { - return function ($value) use ($data): string { - if ($value === '_header') { - return 'by '. $data['frequency']; - } - switch ($data['frequency']) { - case 'month': - $month = \DateTime::createFromFormat('!m', $value); - return sprintf( - "%02d (%s)", - $value, - $month->format('M') - ); - - case 'week': - //return $this->translator->trans('for week') .' '. $value ; - - case 'year': - //return $this->translator->trans('in year') .' '. $value ; - - default: - return $value; - } - }; - } - - public function getQueryKeys($data): array - { - return ['date_aggregator']; - } - - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('frequency', ChoiceType::class, [ - 'choices' => self::CHOICES, - 'multiple' => false, - 'expanded' => true, - 'empty_data' => self::DEFAULT_CHOICE, - 'data' => self::DEFAULT_CHOICE, - ]); - } - - public function getTitle(): string - { - return 'Group activity by date'; - } - public function addRole() { return null; @@ -90,13 +49,19 @@ class DateAggregator implements AggregatorInterface switch ($data['frequency']) { case 'month': - $fmt = 'MM'; break; + $fmt = 'MM'; + +break; case 'week': - $fmt = 'IW'; break; + $fmt = 'IW'; + +break; case 'year': - $fmt = 'YYYY'; $order = 'DESC'; break; + $fmt = 'YYYY'; $order = 'DESC'; + +break; default: throw new RuntimeException(sprintf("The frequency data '%s' is invalid.", $data['frequency'])); @@ -126,4 +91,53 @@ class DateAggregator implements AggregatorInterface return Declarations::ACTIVITY_ACP; } -} \ No newline at end of file + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('frequency', ChoiceType::class, [ + 'choices' => self::CHOICES, + 'multiple' => false, + 'expanded' => true, + 'empty_data' => self::DEFAULT_CHOICE, + 'data' => self::DEFAULT_CHOICE, + ]); + } + + public function getLabels($key, array $values, $data) + { + return static function ($value) use ($data): string { + if ('_header' === $value) { + return 'by ' . $data['frequency']; + } + + switch ($data['frequency']) { + case 'month': + $month = DateTime::createFromFormat('!m', $value); + + return sprintf( + '%02d (%s)', + $value, + $month->format('M') + ); + + case 'week': + //return $this->translator->trans('for week') .' '. $value ; + + case 'year': + //return $this->translator->trans('in year') .' '. $value ; + + default: + return $value; + } + }; + } + + public function getQueryKeys($data): array + { + return ['date_aggregator']; + } + + public function getTitle(): string + { + return 'Group activity by date'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/LocationTypeAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/LocationTypeAggregator.php index aa5052aaa..c5f43cc64 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/LocationTypeAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/LocationTypeAggregator.php @@ -1,5 +1,12 @@ translatableStringHelper = $translatableStringHelper; } - public function getLabels($key, array $values, $data) - { - return function ($value): string { - if ($value === '_header') { - return 'Accepted locationtype'; - } - - $lt = $this->locationTypeRepository->find($value); - - return $this->translatableStringHelper->localize( - $lt->getTitle() - ); - }; - } - - public function getQueryKeys($data): array - { - return ['locationtype_aggregator']; - } - - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - public function getTitle(): string - { - return 'Group activity by locationtype'; - } - public function addRole() { return null; @@ -63,7 +40,7 @@ class LocationTypeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('location', $qb->getAllAliases())) { + if (!in_array('location', $qb->getAllAliases(), true)) { $qb->join('activity.location', 'location'); } @@ -83,4 +60,33 @@ class LocationTypeAggregator implements AggregatorInterface return Declarations::ACTIVITY_ACP; } -} \ No newline at end of file + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Accepted locationtype'; + } + + $lt = $this->locationTypeRepository->find($value); + + return $this->translatableStringHelper->localize( + $lt->getTitle() + ); + }; + } + + public function getQueryKeys($data): array + { + return ['locationtype_aggregator']; + } + + public function getTitle(): string + { + return 'Group activity by locationtype'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/UserScopeAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/UserScopeAggregator.php index 4d6cef24a..67c2d8db1 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/UserScopeAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/UserScopeAggregator.php @@ -1,5 +1,12 @@ translatableStringHelper = $translatableStringHelper; } - public function getLabels($key, array $values, $data) - { - return function ($value): string { - if ($value === '_header') { - return 'Scope'; - } - - $s = $this->scopeRepository->find($value); - - return $this->translatableStringHelper->localize( - $s->getName() - ); - }; - } - - public function getQueryKeys($data): array - { - return ['userscope_aggregator']; - } - - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - public function getTitle(): string - { - return 'Group activity by userscope'; - } - public function addRole() { return null; @@ -63,7 +40,7 @@ class UserScopeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('user', $qb->getAllAliases())) { + if (!in_array('user', $qb->getAllAliases(), true)) { $qb->join('activity.user', 'user'); } @@ -83,4 +60,33 @@ class UserScopeAggregator implements AggregatorInterface return Declarations::ACTIVITY_ACP; } -} \ No newline at end of file + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Scope'; + } + + $s = $this->scopeRepository->find($value); + + return $this->translatableStringHelper->localize( + $s->getName() + ); + }; + } + + public function getQueryKeys($data): array + { + return ['userscope_aggregator']; + } + + public function getTitle(): string + { + return 'Group activity by userscope'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php index cedd2df8c..3637d70c6 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php @@ -21,6 +21,7 @@ use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Security\Core\Role\Role; +use function in_array; class ActivityTypeAggregator implements AggregatorInterface { @@ -45,7 +46,7 @@ class ActivityTypeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('type', $qb->getAllAliases())) { + if (!in_array('type', $qb->getAllAliases(), true)) { $qb->join('activity.activityType', 'type'); } diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUserAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUserAggregator.php index 675098c60..6d6d3a837 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUserAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUserAggregator.php @@ -25,10 +25,10 @@ class ActivityUserAggregator implements AggregatorInterface { public const KEY = 'activity_user_id'; - private UserRepository $userRepository; - private UserRender $userRender; + private UserRepository $userRepository; + public function __construct( UserRepository $userRepository, UserRender $userRender diff --git a/src/Bundle/ChillActivityBundle/Export/Declarations.php b/src/Bundle/ChillActivityBundle/Export/Declarations.php index 7a5b47028..82f01bcb2 100644 --- a/src/Bundle/ChillActivityBundle/Export/Declarations.php +++ b/src/Bundle/ChillActivityBundle/Export/Declarations.php @@ -18,7 +18,7 @@ abstract class Declarations { public const ACTIVITY = 'activity'; - public const ACTIVITY_ACP = "activity_linked_to_acp"; + public const ACTIVITY_ACP = 'activity_linked_to_acp'; - public const ACTIVITY_PERSON = "activity_linked_to_person"; + public const ACTIVITY_PERSON = 'activity_linked_to_person'; } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php index 1ae5d1830..d06116787 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php @@ -1,5 +1,14 @@ getQuery()->getResult(Query::HYDRATE_SCALAR); } + public function getTitle(): string + { + return 'Average activity linked to an accompanying period duration'; + } + public function getType(): string { return Declarations::ACTIVITY; @@ -73,8 +85,7 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { $qb = $this->repository->createQueryBuilder('activity') - ->join('activity.accompanyingPeriod', 'acp') - ; + ->join('activity.accompanyingPeriod', 'acp'); $qb->select('AVG(activity.durationTime) as export_avg_activity_duration'); @@ -94,10 +105,4 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface //PersonDeclarations::ACP_TYPE, ]; } - - public function getGroup(): string - { - return 'Exports of activities linked to an accompanying period'; - } - -} \ No newline at end of file +} diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php index b03378b16..535fe3d85 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php @@ -1,5 +1,14 @@ getQuery()->getResult(Query::HYDRATE_SCALAR); } + public function getTitle(): string + { + return 'Average activity linked to an accompanying period visit duration'; + } + public function getType(): string { return Declarations::ACTIVITY; @@ -73,8 +85,7 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { $qb = $this->repository->createQueryBuilder('activity') - ->join('activity.accompanyingPeriod', 'acp') - ; + ->join('activity.accompanyingPeriod', 'acp'); $qb->select('AVG(activity.travelTime) as export_avg_activity_visit_duration'); @@ -94,10 +105,4 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac //PersonDeclarations::ACP_TYPE, ]; } - - public function getGroup(): string - { - return 'Exports of activities linked to an accompanying period'; - } - -} \ No newline at end of file +} diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php index 55c0a1974..cefb5e0e9 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php @@ -30,7 +30,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface protected EntityRepository $repository; public function __construct( - EntityManagerInterface $em + EntityManagerInterface $em ) { $this->repository = $em->getRepository(Activity::class); } @@ -49,6 +49,11 @@ class CountActivity implements ExportInterface, GroupedExportInterface return 'Count activities linked to an accompanying period by various parameters.'; } + public function getGroup(): string + { + return 'Exports of activities linked to an accompanying period'; + } + public function getLabels($key, array $values, $data) { if ('export_count_activity' !== $key) { @@ -81,11 +86,10 @@ class CountActivity implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { $qb = $this->repository->createQueryBuilder('activity') - ->join('activity.accompanyingPeriod', 'acp') - ; + ->join('activity.accompanyingPeriod', 'acp'); $qb->select('COUNT(activity.id) as export_count_activity'); - + return $qb; } @@ -102,9 +106,4 @@ class CountActivity implements ExportInterface, GroupedExportInterface //PersonDeclarations::ACP_TYPE, ]; } - - public function getGroup(): string - { - return 'Exports of activities linked to an accompanying period'; - } } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php index 3ab192702..7ba914ee5 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php @@ -1,5 +1,14 @@ getQuery()->getResult(Query::HYDRATE_SCALAR); } + public function getTitle(): string + { + return 'Sum activity linked to an accompanying period duration'; + } + public function getType(): string { return Declarations::ACTIVITY; @@ -73,8 +85,7 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { $qb = $this->repository->createQueryBuilder('activity') - ->join('activity.accompanyingPeriod', 'acp') - ; + ->join('activity.accompanyingPeriod', 'acp'); $qb->select('SUM(activity.durationTime) as export_sum_activity_duration'); @@ -94,10 +105,4 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface //PersonDeclarations::ACP_TYPE, ]; } - - public function getGroup(): string - { - return 'Exports of activities linked to an accompanying period'; - } - -} \ No newline at end of file +} diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php index 452fc4de7..c3f7ef243 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php @@ -1,5 +1,14 @@ getQuery()->getResult(Query::HYDRATE_SCALAR); } + public function getTitle(): string + { + return 'Sum activity linked to an accompanying period visit duration'; + } + public function getType(): string { return Declarations::ACTIVITY; @@ -73,8 +85,7 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { $qb = $this->repository->createQueryBuilder('activity') - ->join('activity.accompanyingPeriod', 'acp') - ; + ->join('activity.accompanyingPeriod', 'acp'); $qb->select('SUM(activity.travelTime) as export_sum_activity_visit_duration'); @@ -94,9 +105,4 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac //PersonDeclarations::ACP_TYPE, ]; } - - public function getGroup(): string - { - return 'Exports of activities linked to an accompanying period'; - } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php index 75d3122c3..84baf657a 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php @@ -11,12 +11,12 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Export\Export\LinkedToPerson; +use Chill\ActivityBundle\Export\Declarations; use Chill\ActivityBundle\Repository\ActivityRepository; use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; -use Chill\ActivityBundle\Export\Declarations; use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\Query; use LogicException; @@ -47,6 +47,11 @@ class CountActivity implements ExportInterface, GroupedExportInterface return 'Count activities linked to a person by various parameters.'; } + public function getGroup(): string + { + return 'Exports of activities linked to a person'; + } + public function getLabels($key, array $values, $data) { if ('export_count_activity' !== $key) { @@ -81,15 +86,13 @@ class CountActivity implements ExportInterface, GroupedExportInterface $centers = array_map(static fn ($el) => $el['center'], $acl); $qb = $this->activityRepository->createQueryBuilder('activity') - ->join('activity.person', 'person') - ; + ->join('activity.person', 'person'); $qb->select('COUNT(activity.id) as export_count_activity'); $qb ->where($qb->expr()->in('person.center', ':centers')) - ->setParameter('centers', $centers) - ; + ->setParameter('centers', $centers); return $qb; } @@ -107,9 +110,4 @@ class CountActivity implements ExportInterface, GroupedExportInterface //PersonDeclarations::PERSON_TYPE, ]; } - - public function getGroup(): string - { - return 'Exports of activities linked to a person'; - } } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/ListActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/ListActivity.php index 21a68fb93..387ea8821 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/ListActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/ListActivity.php @@ -12,12 +12,14 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Export\Export\LinkedToPerson; use Chill\ActivityBundle\Entity\ActivityReason; +use Chill\ActivityBundle\Export\Declarations; use Chill\ActivityBundle\Repository\ActivityRepository; use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\MainBundle\Export\ListInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; +use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use DateTime; use Doctrine\DBAL\Exception\InvalidArgumentException; use Doctrine\ORM\EntityManagerInterface; @@ -28,8 +30,6 @@ use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Validator\Constraints\Callback; use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Contracts\Translation\TranslatorInterface; -use Chill\ActivityBundle\Export\Declarations; -use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use function array_key_exists; use function count; @@ -100,6 +100,11 @@ class ListActivity implements ListInterface, GroupedExportInterface return 'List activities linked to a person description'; } + public function getGroup(): string + { + return 'Exports of activities linked to a person'; + } + public function getLabels($key, array $values, $data) { switch ($key) { @@ -283,9 +288,4 @@ class ListActivity implements ListInterface, GroupedExportInterface //PersonDeclarations::PERSON_TYPE, ]; } - - public function getGroup(): string - { - return 'Exports of activities linked to a person'; - } } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php index 5fae246f8..ebba5ad82 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/StatActivityDuration.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Export\Export\LinkedToPerson; +use Chill\ActivityBundle\Export\Declarations; use Chill\ActivityBundle\Repository\ActivityRepository; use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter; use Chill\MainBundle\Entity\Center; @@ -18,7 +19,6 @@ use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\PersonBundle\Export\Declarations as PersonDeclarations; -use Chill\ActivityBundle\Export\Declarations; use Doctrine\ORM\Query; use LogicException; use Symfony\Component\Form\FormBuilderInterface; @@ -67,6 +67,11 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface } } + public function getGroup(): string + { + return 'Exports of activities linked to a person'; + } + public function getLabels($key, array $values, $data) { if ('export_stat_activity' !== $key) { @@ -135,9 +140,4 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface //PersonDeclarations::PERSON_TYPE, ]; } - - public function getGroup(): string - { - return 'Exports of activities linked to a person'; - } } diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/BySocialActionFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/BySocialActionFilter.php index 58d77c73e..d110a829f 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/BySocialActionFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/BySocialActionFilter.php @@ -1,17 +1,25 @@ actionRender = $actionRender; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_socialactions', EntityType::class, [ - 'class' => SocialAction::class, - 'choice_label' => function (SocialAction $sa) { - return $this->actionRender->renderString($sa, []); - }, - 'multiple' => true, - 'expanded' => true, - ]); - } - - public function getTitle(): string - { - return 'Filter activity by linked socialaction'; - } - - public function describeAction($data, $format = 'string'): array - { - $actions = []; - - foreach ($data['accepted_socialactions'] as $sa) { - $actions[] = $this->actionRender->renderString($sa, []); - } - - return ['Filtered activity by linked socialaction: only %actions%', [ - '%actions%' => implode(", ou ", $actions) - ]]; - } - public function addRole() { return null; @@ -61,7 +39,7 @@ class BySocialActionFilter implements FilterInterface { $where = $qb->getDQLPart('where'); - if (!in_array('socialaction', $qb->getAllAliases())) { + if (!in_array('socialaction', $qb->getAllAliases(), true)) { $qb->join('activity.socialActions', 'socialaction'); } @@ -77,9 +55,38 @@ class BySocialActionFilter implements FilterInterface $qb->setParameter('socialactions', $data['accepted_socialactions']); } - public function applyOn(): string + public function applyOn(): string { - return Declarations::ACTIVITY_ACP; + return Declarations::ACTIVITY_ACP; } -} \ No newline at end of file + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_socialactions', EntityType::class, [ + 'class' => SocialAction::class, + 'choice_label' => function (SocialAction $sa) { + return $this->actionRender->renderString($sa, []); + }, + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $actions = []; + + foreach ($data['accepted_socialactions'] as $sa) { + $actions[] = $this->actionRender->renderString($sa, []); + } + + return ['Filtered activity by linked socialaction: only %actions%', [ + '%actions%' => implode(', ou ', $actions), + ]]; + } + + public function getTitle(): string + { + return 'Filter activity by linked socialaction'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/BySocialIssueFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/BySocialIssueFilter.php index 052b780ff..66fa92dd4 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/BySocialIssueFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/BySocialIssueFilter.php @@ -1,17 +1,25 @@ issueRender = $issueRender; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_socialissues', EntityType::class, [ - 'class' => SocialIssue::class, - 'choice_label' => function(SocialIssue $si) { - return $this->issueRender->renderString($si, []); - }, - 'multiple' => true, - 'expanded' => true, - ]); - } - - public function getTitle(): string - { - return 'Filter activity by linked socialissue'; - } - - public function describeAction($data, $format = 'string'): array - { - $issues = []; - - foreach ($data['accepted_socialissues'] as $si) { - $issues[] = $this->issueRender->renderString($si, []); - } - - return ['Filtered activity by linked socialissue: only %issues%', [ - '%issues%' => implode(", ou ", $issues) - ]]; - } - public function addRole() { return null; @@ -61,7 +39,7 @@ class BySocialIssueFilter implements FilterInterface { $where = $qb->getDQLPart('where'); - if (!in_array('socialissue', $qb->getAllAliases())) { + if (!in_array('socialissue', $qb->getAllAliases(), true)) { $qb->join('activity.socialIssues', 'socialissue'); } @@ -77,9 +55,38 @@ class BySocialIssueFilter implements FilterInterface $qb->setParameter('socialissues', $data['accepted_socialissues']); } - public function applyOn(): string + public function applyOn(): string { return Declarations::ACTIVITY_ACP; } -} \ No newline at end of file + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_socialissues', EntityType::class, [ + 'class' => SocialIssue::class, + 'choice_label' => function (SocialIssue $si) { + return $this->issueRender->renderString($si, []); + }, + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $issues = []; + + foreach ($data['accepted_socialissues'] as $si) { + $issues[] = $this->issueRender->renderString($si, []); + } + + return ['Filtered activity by linked socialissue: only %issues%', [ + '%issues%' => implode(', ou ', $issues), + ]]; + } + + public function getTitle(): string + { + return 'Filter activity by linked socialissue'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ByUserFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ByUserFilter.php index b128b03aa..512dce874 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ByUserFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ByUserFilter.php @@ -1,17 +1,25 @@ userRender = $userRender; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_users', EntityType::class, [ - 'class' => User::class, - 'choice_label' => function (User $u) { - return $this->userRender->renderString($u, []); - }, - 'multiple' => true, - 'expanded' => true, - ]); - } - - public function getTitle(): string - { - return 'Filter activity by linked users'; - } - - public function describeAction($data, $format = 'string'): array - { - $users = []; - - foreach ($data['accepted_users'] as $u) { - $users[] = $this->userRender->renderString($u, []); - } - - return ['Filtered activity by linked users: only %users%', [ - '%users%' => implode(", ou ", $users) - ]]; - } - public function addRole() { return null; @@ -61,7 +39,7 @@ class ByUserFilter implements FilterInterface { $where = $qb->getDQLPart('where'); - if (!in_array('user', $qb->getAllAliases())) { + if (!in_array('user', $qb->getAllAliases(), true)) { $qb->join('activity.users', 'user'); } @@ -77,8 +55,38 @@ class ByUserFilter implements FilterInterface $qb->setParameter('users', $data['accepted_users']); } - public function applyOn(): string + public function applyOn(): string { - return Declarations::ACTIVITY_ACP; + return Declarations::ACTIVITY_ACP; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_users', EntityType::class, [ + 'class' => User::class, + 'choice_label' => function (User $u) { + return $this->userRender->renderString($u, []); + }, + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $users = []; + + foreach ($data['accepted_users'] as $u) { + $users[] = $this->userRender->renderString($u, []); + } + + return ['Filtered activity by linked users: only %users%', [ + '%users%' => implode(', ou ', $users), + ]]; + } + + public function getTitle(): string + { + return 'Filter activity by linked users'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/EmergencyFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/EmergencyFilter.php index d7a823891..612ef3d3d 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/EmergencyFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/EmergencyFilter.php @@ -1,11 +1,18 @@ false, ]; - private CONST DEFAULT_CHOICE = false; + private const DEFAULT_CHOICE = false; private TranslatorInterface $translator; @@ -28,35 +35,6 @@ class EmergencyFilter implements FilterInterface $this->translator = $translator; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_emergency', ChoiceType::class, [ - 'choices' => self::CHOICES, - 'multiple' => false, - 'expanded' => true, - 'empty_data' => self::DEFAULT_CHOICE, - 'data' => self::DEFAULT_CHOICE, - ]); - } - - public function getTitle(): string - { - return 'Filter activity by emergency'; - } - - public function describeAction($data, $format = 'string'): array - { - foreach (self::CHOICES as $k => $v) { - if ($v === $data['accepted_emergency']) { - $choice = $k; - } - } - - return ['Filtered activity by emergency: only %emergency%', [ - '%emergency%' => $this->translator->trans($choice) - ]]; - } - public function addRole() { return null; @@ -78,9 +56,37 @@ class EmergencyFilter implements FilterInterface $qb->setParameter('emergency', $data['accepted_emergency']); } - public function applyOn(): string + public function applyOn(): string { - return Declarations::ACTIVITY_ACP; + return Declarations::ACTIVITY_ACP; } -} \ No newline at end of file + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_emergency', ChoiceType::class, [ + 'choices' => self::CHOICES, + 'multiple' => false, + 'expanded' => true, + 'empty_data' => self::DEFAULT_CHOICE, + 'data' => self::DEFAULT_CHOICE, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + foreach (self::CHOICES as $k => $v) { + if ($v === $data['accepted_emergency']) { + $choice = $k; + } + } + + return ['Filtered activity by emergency: only %emergency%', [ + '%emergency%' => $this->translator->trans($choice), + ]]; + } + + public function getTitle(): string + { + return 'Filter activity by emergency'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/LocationTypeFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/LocationTypeFilter.php index cf29f20f2..4519dd63a 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/LocationTypeFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/LocationTypeFilter.php @@ -1,17 +1,25 @@ translatableStringHelper = $translatableStringHelper; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_locationtype', EntityType::class, [ - 'class' => LocationType::class, - 'choice_label' => function(LocationType $type) { - return $this->translatableStringHelper->localize($type->getTitle()); - }, - 'multiple' => true, - 'expanded' => true, - ]); - } - - public function getTitle(): string - { - return 'Filter activity by locationtype'; - } - - public function describeAction($data, $format = 'string'): array - { - $types = []; - - foreach ($data['accepted_locationtype'] as $type) { - $types[] = $this->translatableStringHelper->localize( - $type->getTitle() - ); - } - - return ['Filtered activity by locationtype: only %types%', [ - '%types%' => implode(", ou ", $types) - ]]; - } - public function addRole() { return null; @@ -61,7 +37,7 @@ class LocationTypeFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('location', $qb->getAllAliases())) { + if (!in_array('location', $qb->getAllAliases(), true)) { $qb->join('activity.location', 'location'); } @@ -78,8 +54,40 @@ class LocationTypeFilter implements FilterInterface $qb->setParameter('locationtype', $data['accepted_locationtype']); } - public function applyOn(): string + public function applyOn(): string { - return Declarations::ACTIVITY_ACP; + return Declarations::ACTIVITY_ACP; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_locationtype', EntityType::class, [ + 'class' => LocationType::class, + 'choice_label' => function (LocationType $type) { + return $this->translatableStringHelper->localize($type->getTitle()); + }, + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $types = []; + + foreach ($data['accepted_locationtype'] as $type) { + $types[] = $this->translatableStringHelper->localize( + $type->getTitle() + ); + } + + return ['Filtered activity by locationtype: only %types%', [ + '%types%' => implode(', ou ', $types), + ]]; + } + + public function getTitle(): string + { + return 'Filter activity by locationtype'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/SentReceivedFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/SentReceivedFilter.php index 409c974ac..7c878ac75 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/SentReceivedFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/SentReceivedFilter.php @@ -1,12 +1,19 @@ Activity::SENTRECEIVED_RECEIVED, ]; - private CONST DEFAULT_CHOICE = Activity::SENTRECEIVED_SENT; + private const DEFAULT_CHOICE = Activity::SENTRECEIVED_SENT; private TranslatorInterface $translator; @@ -29,31 +36,6 @@ class SentReceivedFilter implements FilterInterface $this->translator = $translator; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_sentreceived', ChoiceType::class, [ - 'choices' => self::CHOICES, - 'multiple' => false, - 'expanded' => true, - 'empty_data' => self::DEFAULT_CHOICE, - 'data' => self::DEFAULT_CHOICE, - ]); - } - - public function getTitle(): string - { - return 'Filter activity by sentreceived'; - } - - public function describeAction($data, $format = 'string'): array - { - $sentreceived = array_flip(self::CHOICES)[$data['accepted_sentreceived']]; - - return ['Filtered activity by sentreceived: only %sentreceived%', [ - '%sentreceived%' => $this->translator->trans($sentreceived) - ]]; - } - public function addRole() { return null; @@ -75,8 +57,33 @@ class SentReceivedFilter implements FilterInterface $qb->setParameter('sentreceived', $data['accepted_sentreceived']); } - public function applyOn(): string + public function applyOn(): string { - return Declarations::ACTIVITY_ACP; + return Declarations::ACTIVITY_ACP; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_sentreceived', ChoiceType::class, [ + 'choices' => self::CHOICES, + 'multiple' => false, + 'expanded' => true, + 'empty_data' => self::DEFAULT_CHOICE, + 'data' => self::DEFAULT_CHOICE, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $sentreceived = array_flip(self::CHOICES)[$data['accepted_sentreceived']]; + + return ['Filtered activity by sentreceived: only %sentreceived%', [ + '%sentreceived%' => $this->translator->trans($sentreceived), + ]]; + } + + public function getTitle(): string + { + return 'Filter activity by sentreceived'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/UserFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/UserFilter.php index d1455ea89..ef865b3a8 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/UserFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/UserFilter.php @@ -1,12 +1,19 @@ userRender = $userRender; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_users', EntityType::class, [ - 'class' => User::class, - 'choice_label' => function (User $u) { - return $this->userRender->renderString($u, []); - }, - 'multiple' => true, - 'expanded' => true, - 'label' => 'Creators' - ]); - } - - public function getTitle(): string - { - return 'Filter activity by user'; - } - - public function describeAction($data, $format = 'string'): array - { - $users = []; - - foreach ($data['accepted_users'] as $u) { - $users[] = $this->userRender->renderString($u, []); - } - - return ['Filtered activity by user: only %users%', [ - '%users%' => implode(", ou ", $users) - ]]; - } - public function addRole() { return null; @@ -74,9 +50,39 @@ class UserFilter implements FilterInterface $qb->setParameter('users', $data['accepted_users']); } - public function applyOn(): string + public function applyOn(): string { - return Declarations::ACTIVITY_ACP; + return Declarations::ACTIVITY_ACP; } -} \ No newline at end of file + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_users', EntityType::class, [ + 'class' => User::class, + 'choice_label' => function (User $u) { + return $this->userRender->renderString($u, []); + }, + 'multiple' => true, + 'expanded' => true, + 'label' => 'Creators', + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $users = []; + + foreach ($data['accepted_users'] as $u) { + $users[] = $this->userRender->renderString($u, []); + } + + return ['Filtered activity by user: only %users%', [ + '%users%' => implode(', ou ', $users), + ]]; + } + + public function getTitle(): string + { + return 'Filter activity by user'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/UserScopeFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/UserScopeFilter.php index 987704ab8..ae2ad2629 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/UserScopeFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/UserScopeFilter.php @@ -1,17 +1,25 @@ translatableStringHelper = $translatableStringHelper; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_userscope', EntityType::class, [ - 'class' => Scope::class, - 'choice_label' => function (Scope $s) { - return $this->translatableStringHelper->localize( - $s->getName() - ); - }, - 'multiple' => true, - 'expanded' => true - ]); - } - - public function getTitle(): string - { - return 'Filter activity by userscope'; - } - - public function describeAction($data, $format = 'string'): array - { - $scopes = []; - - foreach ($data['accepted_userscope'] as $s) { - $scopes[] = $this->translatableStringHelper->localize( - $s->getName() - ); - } - - return ['Filtered activity by userscope: only %scopes%', [ - '%scopes%' => implode(", ou ", $scopes) - ]]; - } - public function addRole() { return null; @@ -63,7 +37,7 @@ class UserScopeFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('user', $qb->getAllAliases())) { + if (!in_array('user', $qb->getAllAliases(), true)) { $qb->join('activity.user', 'user'); } @@ -81,9 +55,42 @@ class UserScopeFilter implements FilterInterface $qb->setParameter('userscope', $data['accepted_userscope']); } - public function applyOn(): string + public function applyOn(): string { - return Declarations::ACTIVITY_ACP; + return Declarations::ACTIVITY_ACP; } -} \ No newline at end of file + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_userscope', EntityType::class, [ + 'class' => Scope::class, + 'choice_label' => function (Scope $s) { + return $this->translatableStringHelper->localize( + $s->getName() + ); + }, + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $scopes = []; + + foreach ($data['accepted_userscope'] as $s) { + $scopes[] = $this->translatableStringHelper->localize( + $s->getName() + ); + } + + return ['Filtered activity by userscope: only %scopes%', [ + '%scopes%' => implode(', ou ', $scopes), + ]]; + } + + public function getTitle(): string + { + return 'Filter activity by userscope'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ActivityDateFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ActivityDateFilter.php index 4b20553af..65057cb1e 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ActivityDateFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ActivityDateFilter.php @@ -73,8 +73,7 @@ class ActivityDateFilter implements FilterInterface ->add('date_to', ChillDateType::class, [ 'label' => 'Activities before this date', 'data' => new DateTime(), - ]) - ; + ]); $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) { /** @var \Symfony\Component\Form\FormInterface $filterForm */ diff --git a/src/Bundle/ChillBudgetBundle/Config/ConfigRepository.php b/src/Bundle/ChillBudgetBundle/Config/ConfigRepository.php index e498ba5a3..6443eeaf7 100644 --- a/src/Bundle/ChillBudgetBundle/Config/ConfigRepository.php +++ b/src/Bundle/ChillBudgetBundle/Config/ConfigRepository.php @@ -70,14 +70,14 @@ class ConfigRepository private function getCharges(bool $onlyActive = false): array { return $onlyActive ? - array_filter($this->charges, function ($el) { return $el['active']; }) + array_filter($this->charges, static function ($el) { return $el['active']; }) : $this->charges; } private function getResources(bool $onlyActive = false): array { return $onlyActive ? - array_filter($this->resources, function ($el) { return $el['active']; }) + array_filter($this->resources, static function ($el) { return $el['active']; }) : $this->resources; } diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php index ed7c265ff..af96dd474 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php @@ -1,20 +1,30 @@ groupBy('agent_aggregator'); } - } public function applyOn(): string @@ -54,7 +63,7 @@ final class AgentAggregator implements AggregatorInterface // no form } - public function getLabels($key, array $values, $data): \Closure + public function getLabels($key, array $values, $data): Closure { return function ($value): string { if ('_header' === $value) { @@ -76,4 +85,4 @@ final class AgentAggregator implements AggregatorInterface { return 'Group by agent'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php index 583153a7d..2010fb90c 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php @@ -1,11 +1,21 @@ translatableStringHelper = $translatableStringHelper; } - public function getLabels($key, array $values, $data): \Closure - { - return function($value): string { - if ($value === '_header') { - return 'Cancel reason'; - } - - $j = $this->cancelReasonRepository->find($value); - - return $this->translatableStringHelper->localize( - $j->getName() - ); - }; - } - - public function getQueryKeys($data): array - { - return ['cancel_reason_aggregator']; - } - - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - public function getTitle(): string - { - return 'Group by cancel reason'; - } - public function addRole() { return null; @@ -78,4 +58,34 @@ class CancelReasonAggregator implements AggregatorInterface { return Declarations::CALENDAR_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data): Closure + { + return function ($value): string { + if ('_header' === $value) { + return 'Cancel reason'; + } + + $j = $this->cancelReasonRepository->find($value); + + return $this->translatableStringHelper->localize( + $j->getName() + ); + }; + } + + public function getQueryKeys($data): array + { + return ['cancel_reason_aggregator']; + } + + public function getTitle(): string + { + return 'Group by cancel reason'; + } +} diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php index b019cc21b..a2b300f24 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php @@ -1,11 +1,21 @@ translatableStringHelper = $translatableStringHelper; } - public function getLabels($key, array $values, $data): \Closure - { - return function($value): string { - if ($value === '_header') { - return 'Job'; - } - - $j = $this->jobRepository->find($value); - - return $this->translatableStringHelper->localize( - $j->getLabel() - ); - }; - } - - public function getQueryKeys($data): array - { - return ['job_aggregator']; - } - - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - public function getTitle(): string - { - return 'Group by agent job'; - } - public function addRole() { return null; @@ -77,4 +57,34 @@ final class JobAggregator implements AggregatorInterface { return Declarations::CALENDAR_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data): Closure + { + return function ($value): string { + if ('_header' === $value) { + return 'Job'; + } + + $j = $this->jobRepository->find($value); + + return $this->translatableStringHelper->localize( + $j->getLabel() + ); + }; + } + + public function getQueryKeys($data): array + { + return ['job_aggregator']; + } + + public function getTitle(): string + { + return 'Group by agent job'; + } +} diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php index bc921c9c7..6d5a6476d 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php @@ -1,10 +1,20 @@ locationRepository = $locationRepository; } - public function getLabels($key, array $values, $data): \Closure - { - return function($value): string { - if ($value === '_header') { - return 'Location'; - } - - $l = $this->locationRepository->find($value); - - return $l->getName(); - - }; - } - - public function getQueryKeys($data): array - { - return ['location_aggregator']; - } - - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - public function getTitle(): string - { - return 'Group by location'; - } - public function addRole(): ?Role { return null; @@ -72,4 +53,31 @@ final class LocationAggregator implements AggregatorInterface return Declarations::CALENDAR_TYPE; } -} \ No newline at end of file + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data): Closure + { + return function ($value): string { + if ('_header' === $value) { + return 'Location'; + } + + $l = $this->locationRepository->find($value); + + return $l->getName(); + }; + } + + public function getQueryKeys($data): array + { + return ['location_aggregator']; + } + + public function getTitle(): string + { + return 'Group by location'; + } +} diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php index 3b3b9105a..4ead075d5 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php @@ -1,11 +1,21 @@ translatableStringHelper = $translatableStringHelper; } - public function getLabels($key, array $values, $data): \Closure - { - return function($value): string { - if ($value === '_header') { - return 'Location type'; - } - - $j = $this->locationTypeRepository->find($value); - - return $this->translatableStringHelper->localize( - $j->getTitle() - ); - }; - } - - public function getQueryKeys($data): array - { - return ['location_type_aggregator']; - } - - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - public function getTitle(): string - { - return 'Group by location type'; - } - public function addRole() { return null; @@ -78,4 +58,33 @@ final class LocationTypeAggregator implements AggregatorInterface return Declarations::CALENDAR_TYPE; } -} \ No newline at end of file + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data): Closure + { + return function ($value): string { + if ('_header' === $value) { + return 'Location type'; + } + + $j = $this->locationTypeRepository->find($value); + + return $this->translatableStringHelper->localize( + $j->getTitle() + ); + }; + } + + public function getQueryKeys($data): array + { + return ['location_type_aggregator']; + } + + public function getTitle(): string + { + return 'Group by location type'; + } +} diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php index 71a0e75bb..42bac218d 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php @@ -1,5 +1,14 @@ translatableStringHelper = $translatableStringHelper; } - public function getLabels($key, array $values, $data): \Closure - { - return function ($value): string { - if ($value === '_header') { - return 'Scope'; - } - - $s = $this->scopeRepository->find($value); - - return $this->translatableStringHelper->localize( - $s->getName() - ); - }; - } - - public function getQueryKeys($data): array - { - return ['scope_aggregator']; - } - - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - public function getTitle(): string - { - return 'Group by agent scope'; - } - public function addRole() { return null; @@ -77,4 +57,34 @@ final class ScopeAggregator implements AggregatorInterface { return Declarations::CALENDAR_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data): Closure + { + return function ($value): string { + if ('_header' === $value) { + return 'Scope'; + } + + $s = $this->scopeRepository->find($value); + + return $this->translatableStringHelper->localize( + $s->getName() + ); + }; + } + + public function getQueryKeys($data): array + { + return ['scope_aggregator']; + } + + public function getTitle(): string + { + return 'Group by agent scope'; + } +} diff --git a/src/Bundle/ChillCalendarBundle/Export/Export/CountAppointments.php b/src/Bundle/ChillCalendarBundle/Export/Export/CountAppointments.php index e47288edc..36864e92e 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Export/CountAppointments.php +++ b/src/Bundle/ChillCalendarBundle/Export/Export/CountAppointments.php @@ -1,13 +1,23 @@ getQuery()->getResult(Query::HYDRATE_SCALAR); } + public function getTitle(): string + { + return 'Average appointment duration'; + } + public function getType(): string { return Declarations::CALENDAR_TYPE; @@ -91,13 +105,7 @@ class StatAppointmentAvgDuration implements ExportInterface, GroupedExportInterf public function supportsModifiers(): array { return [ - Declarations::CALENDAR_TYPE + Declarations::CALENDAR_TYPE, ]; } - - public function getGroup(): string - { - return 'Exports of calendar'; - } - -} \ No newline at end of file +} diff --git a/src/Bundle/ChillCalendarBundle/Export/Export/StatAppointmentSumDuration.php b/src/Bundle/ChillCalendarBundle/Export/Export/StatAppointmentSumDuration.php index 0e362d766..1960af2e4 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Export/StatAppointmentSumDuration.php +++ b/src/Bundle/ChillCalendarBundle/Export/Export/StatAppointmentSumDuration.php @@ -1,5 +1,14 @@ getQuery()->getResult(Query::HYDRATE_SCALAR); } + public function getTitle(): string + { + return 'Sum of appointment durations'; + } + public function getType(): string { return Declarations::CALENDAR_TYPE; @@ -90,13 +105,7 @@ class StatAppointmentSumDuration implements ExportInterface, GroupedExportInterf public function supportsModifiers(): array { return [ - Declarations::CALENDAR_TYPE + Declarations::CALENDAR_TYPE, ]; } - - public function getGroup(): string - { - return 'Exports of calendar'; - } - -} \ No newline at end of file +} diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php index 5c16d0728..ee4e82350 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/AgentFilter.php @@ -1,5 +1,14 @@ userRender = $userRender; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_agents', EntityType::class, [ - 'class' => User::class, - 'choice_label' => function (User $u) { - return $this->userRender->renderString($u, []); - }, - 'multiple' => true, - 'expanded' => true - ]); - - } - - public function getTitle(): string - { - return 'Filter by agent'; - } - - public function describeAction($data, $format = 'string'): array - { - $users = []; - - foreach ($data['accepted_agents'] as $r) { - $users[] = $r; - } - - return [ - 'Filtered by agent: only %agents%', [ - '%agents' => implode(", ou ", $users) - ]]; - } - public function addRole() { return null; @@ -76,4 +53,35 @@ class AgentFilter implements FilterInterface { return Declarations::CALENDAR_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_agents', EntityType::class, [ + 'class' => User::class, + 'choice_label' => function (User $u) { + return $this->userRender->renderString($u, []); + }, + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $users = []; + + foreach ($data['accepted_agents'] as $r) { + $users[] = $r; + } + + return [ + 'Filtered by agent: only %agents%', [ + '%agents' => implode(', ou ', $users), + ], ]; + } + + public function getTitle(): string + { + return 'Filter by agent'; + } +} diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/BetweenDatesFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/BetweenDatesFilter.php index a23c76d98..74b481abe 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/BetweenDatesFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/BetweenDatesFilter.php @@ -1,42 +1,26 @@ add('date_from', ChillDateType::class, [ - 'data' => new \DateTime(), - ]) - ->add('date_to', ChillDateType::class, [ - 'data' => new \DateTime(), - ]) - ; - } - - public function getTitle(): string - { - return 'Filter by appointments between certain dates'; - } - - public function describeAction($data, $format = 'string'): array - { - return ['Filtered by appointments between %dateFrom% and %dateTo%', [ - '%dateFrom%' => $data['date_from']->format('d-m-Y'), - '%dateTo%' => $data['date_to']->format('d-m-Y'), - ]]; - } - public function addRole() { return null; @@ -47,9 +31,9 @@ class BetweenDatesFilter implements FilterInterface $where = $qb->getDQLPart('where'); $clause = $qb->expr()->andX( - $qb->expr()->gte('cal.startDate', ':dateFrom'), - $qb->expr()->lte('cal.endDate', ':dateTo') - ); + $qb->expr()->gte('cal.startDate', ':dateFrom'), + $qb->expr()->lte('cal.endDate', ':dateTo') + ); if ($where instanceof Andx) { $where->add($clause); @@ -67,4 +51,28 @@ class BetweenDatesFilter implements FilterInterface { return Declarations::CALENDAR_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('date_from', ChillDateType::class, [ + 'data' => new DateTime(), + ]) + ->add('date_to', ChillDateType::class, [ + 'data' => new DateTime(), + ]); + } + + public function describeAction($data, $format = 'string'): array + { + return ['Filtered by appointments between %dateFrom% and %dateTo%', [ + '%dateFrom%' => $data['date_from']->format('d-m-Y'), + '%dateTo%' => $data['date_to']->format('d-m-Y'), + ]]; + } + + public function getTitle(): string + { + return 'Filter by appointments between certain dates'; + } +} diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php index 42e03e239..562c3281e 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php @@ -23,7 +23,6 @@ use Symfony\Contracts\Translation\TranslatorInterface; class JobFilter implements FilterInterface { - protected TranslatorInterface $translator; private TranslatableStringHelper $translatableStringHelper; @@ -36,34 +35,6 @@ class JobFilter implements FilterInterface $this->translatableStringHelper = $translatableStringHelper; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('job', EntityType::class, [ - 'class' => UserJob::class, - 'choice_label' => function (UserJob $j) { - return $this->translatableStringHelper->localize( - $j->getLabel() - ); - }, - 'multiple' => true, - 'expanded' => true - ]); - } - - public function describeAction($data, $format = 'string'): array - { - $userJobs = []; - - foreach ($data['job'] as $j) { - $userJobs[] = $this->translatableStringHelper->localize( - $j->getLabel()); - } - - return ['Filtered by agent job: only %jobs%', [ - '%jobs%' => implode(', ou ', $userJobs) - ]]; - } - public function addRole() { return null; @@ -91,6 +62,34 @@ class JobFilter implements FilterInterface return Declarations::CALENDAR_TYPE; } + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('job', EntityType::class, [ + 'class' => UserJob::class, + 'choice_label' => function (UserJob $j) { + return $this->translatableStringHelper->localize( + $j->getLabel() + ); + }, + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $userJobs = []; + + foreach ($data['job'] as $j) { + $userJobs[] = $this->translatableStringHelper->localize( + $j->getLabel() + ); + } + + return ['Filtered by agent job: only %jobs%', [ + '%jobs%' => implode(', ou ', $userJobs), + ]]; + } public function getTitle(): string { diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php index c3fc7b1e4..3ac1d0f1b 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php @@ -23,7 +23,6 @@ use Symfony\Contracts\Translation\TranslatorInterface; class ScopeFilter implements FilterInterface { - protected TranslatorInterface $translator; private TranslatableStringHelper $translatableStringHelper; @@ -36,34 +35,6 @@ class ScopeFilter implements FilterInterface $this->translatableStringHelper = $translatableStringHelper; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('scope', EntityType::class, [ - 'class' => Scope::class, - 'choice_label' => function (Scope $s) { - return $this->translatableStringHelper->localize( - $s->getName() - ); - }, - 'multiple' => true, - 'expanded' => true - ]); - } - - public function describeAction($data, $format = 'string') - { - $scopes = []; - - foreach ($data['scope'] as $s) { - $scopes[] = $this->translatableStringHelper->localize( - $s->getName()); - } - - return ['Filtered by agent scope: only %scopes%', [ - '%scopes%' => implode(', ou ', $scopes) - ]]; - } - public function addRole() { return null; @@ -91,9 +62,37 @@ class ScopeFilter implements FilterInterface return Declarations::CALENDAR_TYPE; } + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('scope', EntityType::class, [ + 'class' => Scope::class, + 'choice_label' => function (Scope $s) { + return $this->translatableStringHelper->localize( + $s->getName() + ); + }, + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string') + { + $scopes = []; + + foreach ($data['scope'] as $s) { + $scopes[] = $this->translatableStringHelper->localize( + $s->getName() + ); + } + + return ['Filtered by agent scope: only %scopes%', [ + '%scopes%' => implode(', ou ', $scopes), + ]]; + } public function getTitle() { return 'Filter by agent scope'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillMainBundle/Controller/ExportController.php b/src/Bundle/ChillMainBundle/Controller/ExportController.php index aba48474f..49d0e3959 100644 --- a/src/Bundle/ChillMainBundle/Controller/ExportController.php +++ b/src/Bundle/ChillMainBundle/Controller/ExportController.php @@ -446,7 +446,7 @@ class ExportController extends AbstractController $this->logger->notice('[export] choices for an export unserialized', [ 'key' => $key, - 'rawData' => json_encode($rawData) + 'rawData' => json_encode($rawData), ]); $alias = $rawData['alias']; @@ -531,6 +531,21 @@ class ExportController extends AbstractController ); } + private function getExportGroup($target): string + { + $exportManager = $this->exportManager; + + $groups = $exportManager->getExportsGrouped(true); + + foreach ($groups as $group => $array) { + foreach ($array as $alias => $export) { + if ($export === $target) { + return $group; + } + } + } + } + /** * get the next step. If $reverse === true, the previous step is returned. * @@ -578,19 +593,4 @@ class ExportController extends AbstractController throw new LogicException("the step {$step} is not defined."); } } - - private function getExportGroup($target): string - { - $exportManager = $this->exportManager; - - $groups = $exportManager->getExportsGrouped(true); - - foreach ($groups as $group => $array) { - foreach ($array as $alias => $export) { - if ($export === $target) { - return $group; - } - } - } - } } diff --git a/src/Bundle/ChillMainBundle/Doctrine/DQL/Extract.php b/src/Bundle/ChillMainBundle/Doctrine/DQL/Extract.php index 72ca2b461..7d8809582 100644 --- a/src/Bundle/ChillMainBundle/Doctrine/DQL/Extract.php +++ b/src/Bundle/ChillMainBundle/Doctrine/DQL/Extract.php @@ -1,10 +1,18 @@ EXTRACT(field FROM interval) @@ -50,5 +58,4 @@ class Extract extends FunctionNode $parser->match(Lexer::T_CLOSE_PARENTHESIS); } - -} \ No newline at end of file +} diff --git a/src/Bundle/ChillMainBundle/Doctrine/DQL/ToChar.php b/src/Bundle/ChillMainBundle/Doctrine/DQL/ToChar.php index c634555dc..51ff5a9dd 100644 --- a/src/Bundle/ChillMainBundle/Doctrine/DQL/ToChar.php +++ b/src/Bundle/ChillMainBundle/Doctrine/DQL/ToChar.php @@ -1,5 +1,14 @@ fmt = $parser->StringExpression(); $parser->match(Lexer::T_CLOSE_PARENTHESIS); } - -} \ No newline at end of file +} diff --git a/src/Bundle/ChillMainBundle/Export/ExportManager.php b/src/Bundle/ChillMainBundle/Export/ExportManager.php index dee69237f..31092a23b 100644 --- a/src/Bundle/ChillMainBundle/Export/ExportManager.php +++ b/src/Bundle/ChillMainBundle/Export/ExportManager.php @@ -278,16 +278,14 @@ class ExportManager $this->handleAggregators($export, $query, $data[ExportType::AGGREGATOR_KEY], $centers); $this->logger->notice('[export] will execute this qb in export', [ - 'dql' => $query->getDQL() + 'dql' => $query->getDQL(), ]); - } else { throw new UnexpectedValueException('The method `intiateQuery` should return ' . 'a `\\Doctrine\\ORM\\NativeQuery` or a `Doctrine\\ORM\\QueryBuilder` ' . 'object.'); } - $result = $export->getResult($query, $data[ExportType::EXPORT_KEY]); if (!is_iterable($result)) { diff --git a/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBaseImporter.php b/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBaseImporter.php index 6f1d218a9..474cb53de 100644 --- a/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBaseImporter.php +++ b/src/Bundle/ChillMainBundle/Service/Import/AddressReferenceBaseImporter.php @@ -16,6 +16,7 @@ use Doctrine\DBAL\Statement; use Exception; use LogicException; use Psr\Log\LoggerInterface; +use RuntimeException; use function array_key_exists; use function count; @@ -159,8 +160,8 @@ final class AddressReferenceBaseImporter try { $affected = $statement->executeStatement(array_merge(...$this->waitingForInsert)); - if ($affected === 0) { - throw new \RuntimeException('no row affected'); + if (0 === $affected) { + throw new RuntimeException('no row affected'); } } catch (Exception $e) { // in some case, we can add debug code here diff --git a/src/Bundle/ChillMainBundle/Tests/Services/Import/AddressReferenceBaseImporterTest.php b/src/Bundle/ChillMainBundle/Tests/Services/Import/AddressReferenceBaseImporterTest.php index cb23634f1..0ed1f2059 100644 --- a/src/Bundle/ChillMainBundle/Tests/Services/Import/AddressReferenceBaseImporterTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Services/Import/AddressReferenceBaseImporterTest.php @@ -1,5 +1,14 @@ setRefPostalCodeId($postalCodeId = '1234'.uniqid()) + ->setRefPostalCodeId($postalCodeId = '1234' . uniqid()) ->setPostalCodeSource('testing') ->setCode('TEST456') ->setName('testing'); @@ -54,7 +70,8 @@ class AddressReferenceBaseImporterTest extends KernelTestCase $addresses = $this->addressReferenceRepository->findByPostalCodePattern( $postalCode, - 'Rue test abcc guessed'); + 'Rue test abcc guessed' + ); $this->assertCount(1, $addresses); $this->assertEquals('Rue test abccc-guessed', $addresses[0]->getStreet()); @@ -79,12 +96,11 @@ class AddressReferenceBaseImporterTest extends KernelTestCase $addresses = $this->addressReferenceRepository->findByPostalCodePattern( $postalCode, - 'abcc guessed fixed'); + 'abcc guessed fixed' + ); $this->assertCount('1', $addresses); - $this->assertEquals( 'Rue test abccc guessed fixed', $addresses[0]->getStreet()); + $this->assertEquals('Rue test abccc guessed fixed', $addresses[0]->getStreet()); $this->assertEquals($previousAddressId, $addresses[0]->getId()); } - - -} \ No newline at end of file +} diff --git a/src/Bundle/ChillMainBundle/Tests/Services/Import/PostalCodeBaseImporterTest.php b/src/Bundle/ChillMainBundle/Tests/Services/Import/PostalCodeBaseImporterTest.php index 826e581ac..ce40d40bf 100644 --- a/src/Bundle/ChillMainBundle/Tests/Services/Import/PostalCodeBaseImporterTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Services/Import/PostalCodeBaseImporterTest.php @@ -1,5 +1,14 @@ importer->importCode( 'BE', - 'tested with pattern '. ($uniqid = uniqid()), + 'tested with pattern ' . ($uniqid = uniqid()), '12345', - $refPostalCodeId = 'test'.uniqid(), + $refPostalCodeId = 'test' . uniqid(), 'test', 50.0, 5.0, @@ -46,8 +59,8 @@ class PostalCodeBaseImporterTest extends KernelTestCase $this->importer->finalize(); $postalCodes = $this->postalCodeRepository->findByPattern( - 'with pattern '.$uniqid, - $this->countryRepository->findOneBy(['countryCode' => 'BE']) + 'with pattern ' . $uniqid, + $this->countryRepository->findOneBy(['countryCode' => 'BE']) ); $this->assertCount(1, $postalCodes); @@ -59,7 +72,7 @@ class PostalCodeBaseImporterTest extends KernelTestCase $this->importer->importCode( 'BE', - 'tested with adapted pattern '. ($uniqid = uniqid()), + 'tested with adapted pattern ' . ($uniqid = uniqid()), '12345', $refPostalCodeId, 'test', @@ -71,7 +84,7 @@ class PostalCodeBaseImporterTest extends KernelTestCase $this->importer->finalize(); $postalCodes = $this->postalCodeRepository->findByPattern( - 'with pattern '.$uniqid, + 'with pattern ' . $uniqid, $this->countryRepository->findOneBy(['countryCode' => 'BE']) ); @@ -79,7 +92,4 @@ class PostalCodeBaseImporterTest extends KernelTestCase $this->assertStringStartsWith('tested with adapted pattern', $postalCodes[0]->getName()); $this->assertEquals($previousId, $postalCodes[0]->getId()); } - - - -} \ No newline at end of file +} diff --git a/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationOnTransitionTest.php b/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationOnTransitionTest.php index 3aa2c71b1..365b01a67 100644 --- a/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationOnTransitionTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationOnTransitionTest.php @@ -1,5 +1,14 @@ setWorkflowName('workflow_name') - ->setRelatedEntityClass(\stdClass::class) - ->setRelatedEntityId(1) - ; + ->setRelatedEntityClass(stdClass::class) + ->setRelatedEntityId(1); // force an id to entityWorkflow: - $reflection = new \ReflectionClass($entityWorkflow); + $reflection = new ReflectionClass($entityWorkflow); $id = $reflection->getProperty('id'); $id->setAccessible(true); $id->setValue($entityWorkflow, 1); @@ -48,12 +62,11 @@ class NotificationOnTransitionTest extends TestCase $step = new EntityWorkflowStep(); $entityWorkflow->addStep($step); $step->addDestUser($dest) - ->setCurrentStep('to_state') - ; + ->setCurrentStep('to_state'); $em = $this->prophesize(EntityManagerInterface::class); $em->persist(Argument::type(Notification::class))->should( - function($args) use ($dest) { + static function ($args) use ($dest) { /** @var Call[] $args */ if (1 !== count($args)) { throw new FailedPredictionException('no notification sent'); @@ -68,7 +81,8 @@ class NotificationOnTransitionTest extends TestCase if (!$notification->getAddressees()->contains($dest)) { throw new FailedPredictionException('the dest is not notified'); } - }); + } + ); $engine = $this->prophesize(EngineInterface::class); $engine->render(Argument::type('string'), Argument::type('array')) diff --git a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php index 2510f9460..0f6a4799a 100644 --- a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php +++ b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php @@ -41,6 +41,24 @@ class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterfac $this->userRender = $userRender; } + public function addDests(Event $event): void + { + if (!$event->getSubject() instanceof EntityWorkflow) { + return; + } + + /** @var EntityWorkflow $entityWorkflow */ + $entityWorkflow = $event->getSubject(); + + foreach ($entityWorkflow->futureDestUsers as $user) { + $entityWorkflow->getCurrentStep()->addDestUser($user); + } + + foreach ($entityWorkflow->futureDestEmails as $email) { + $entityWorkflow->getCurrentStep()->addDestEmail($email); + } + } + public static function getSubscribedEvents(): array { return [ @@ -55,23 +73,6 @@ class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterfac ]; } - public function addDests(Event $event): void - { - if (!$event->getSubject() instanceof EntityWorkflow) { - return; - } - - /** @var EntityWorkflow $entityWorkflow */ - $entityWorkflow = $event->getSubject(); - foreach ($entityWorkflow->futureDestUsers as $user) { - $entityWorkflow->getCurrentStep()->addDestUser($user); - } - - foreach ($entityWorkflow->futureDestEmails as $email) { - $entityWorkflow->getCurrentStep()->addDestEmail($email); - } - } - public function guardEntityWorkflow(GuardEvent $event) { if (!$event->getSubject() instanceof EntityWorkflow) { diff --git a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php index 982a7024e..c56dc34ce 100644 --- a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php +++ b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php @@ -57,14 +57,15 @@ class NotificationOnTransition implements EventSubscriberInterface } /** - * Send a notification to: + * Send a notification to:. * * * the dests of the new step; * * the users which subscribed to workflow, on each step, or on final * * **Warning** take care that this method must be executed **after** the dest users are added to - * the step (@link{EntityWorkflowStep::addDestUser}). Currently, this is done during - * @link{EntityWorkflowTransitionEventSubscriber::addDests}. + * the step (@see{EntityWorkflowStep::addDestUser}). Currently, this is done during + * + * @see{EntityWorkflowTransitionEventSubscriber::addDests}. */ public function onCompletedSendNotification(Event $event): void { @@ -77,6 +78,7 @@ class NotificationOnTransition implements EventSubscriberInterface /** @var array $dests array of unique values, where keys is the object's hash */ $dests = []; + foreach (array_merge( // the subscriber to each step $entityWorkflow->getSubscriberToStep()->toArray(), diff --git a/src/Bundle/ChillMainBundle/migrations/Version20220730204216.php b/src/Bundle/ChillMainBundle/migrations/Version20220730204216.php index 4ed1b2918..e4ad98318 100644 --- a/src/Bundle/ChillMainBundle/migrations/Version20220730204216.php +++ b/src/Bundle/ChillMainBundle/migrations/Version20220730204216.php @@ -1,5 +1,12 @@ addSql('DROP INDEX chill_main_address_reference_unicity'); + } + public function getDescription(): string { return 'Add an unique constraint on addresses references'; @@ -18,9 +30,4 @@ final class Version20220730204216 extends AbstractMigration { $this->addSql('CREATE UNIQUE INDEX chill_main_address_reference_unicity ON chill_main_address_reference (refId, source)'); } - - public function down(Schema $schema): void - { - $this->addSql('DROP INDEX chill_main_address_reference_unicity'); - } } diff --git a/src/Bundle/ChillMainBundle/migrations/Version20220829132409.php b/src/Bundle/ChillMainBundle/migrations/Version20220829132409.php index 0bb09ef74..0da81d182 100644 --- a/src/Bundle/ChillMainBundle/migrations/Version20220829132409.php +++ b/src/Bundle/ChillMainBundle/migrations/Version20220829132409.php @@ -1,5 +1,12 @@ addSql('DROP SEQUENCE chill_main_geographical_unit_id_seq CASCADE'); + $this->addSql('DROP TABLE chill_main_geographical_unit'); + } + public function getDescription(): string { return 'Add new entity GeographicalUnit'; @@ -22,10 +35,4 @@ final class Version20220829132409 extends AbstractMigration $this->addSql('CREATE SEQUENCE chill_main_geographical_unit_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); $this->addSql('CREATE TABLE chill_main_geographical_unit (id INT NOT NULL, geom TEXT DEFAULT NULL, layerName VARCHAR(255) DEFAULT NULL, unitName VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id))'); } - - public function down(Schema $schema): void - { - $this->addSql('DROP SEQUENCE chill_main_geographical_unit_id_seq CASCADE'); - $this->addSql('DROP TABLE chill_main_geographical_unit'); - } } diff --git a/src/Bundle/ChillPersonBundle/Controller/HouseholdCompositionTypeController.php b/src/Bundle/ChillPersonBundle/Controller/HouseholdCompositionTypeController.php index eb1dc1ea9..e7fc5b203 100644 --- a/src/Bundle/ChillPersonBundle/Controller/HouseholdCompositionTypeController.php +++ b/src/Bundle/ChillPersonBundle/Controller/HouseholdCompositionTypeController.php @@ -23,4 +23,4 @@ class HouseholdCompositionTypeController extends CRUDController return parent::orderQuery($action, $query, $request, $paginator); } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php index efbb972c1..47b414ce2 100644 --- a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php +++ b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php @@ -95,6 +95,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac $loader->load('services/accompanyingPeriodConsistency.yaml'); $loader->load('services/exports_person.yaml'); + if ($container->getParameter('chill_person.accompanying_period') !== 'hidden') { $loader->load('services/exports_accompanying_period.yaml'); } diff --git a/src/Bundle/ChillPersonBundle/Entity/MaritalStatus.php b/src/Bundle/ChillPersonBundle/Entity/MaritalStatus.php index 649830b4a..05f47ffcf 100644 --- a/src/Bundle/ChillPersonBundle/Entity/MaritalStatus.php +++ b/src/Bundle/ChillPersonBundle/Entity/MaritalStatus.php @@ -31,7 +31,6 @@ class MaritalStatus private ?string $id; /** - * @var array * @ORM\Column(type="json") */ private array $name; diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregator.php index 8d421f369..69d1b265d 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregator.php @@ -1,8 +1,16 @@ translatableStringHelper = $translatableStringHelper; } - /** - * @inheritDoc - */ - public function getLabels($key, array $values, $data) - { - return function ($value): string { - if ('_header' === $value) { - return 'Administrative location'; - } - - $l = $this->locationRepository->find($value); - - return $l->getName() .' ('. $this->translatableStringHelper->localize($l->getLocationType()->getTitle()) . ')'; - }; - } - - /** - * @inheritDoc - */ - public function getQueryKeys($data): array - { - return ['location_aggregator']; - } - - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - /** - * @inheritDoc - */ - public function getTitle() - { - return 'Group by administrative location'; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $qb->join('acp.administrativeLocation', 'al'); @@ -90,11 +52,36 @@ class AdministrativeLocationAggregator implements AggregatorInterface } } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Administrative location'; + } + + $l = $this->locationRepository->find($value); + + return $l->getName() . ' (' . $this->translatableStringHelper->localize($l->getLocationType()->getTitle()) . ')'; + }; + } + + public function getQueryKeys($data): array + { + return ['location_aggregator']; + } + + public function getTitle() + { + return 'Group by administrative location'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregator.php index 198cacea7..d525f2f9b 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregator.php @@ -1,12 +1,20 @@ translatableStringHelper = $translatableStringHelper; } - /** - * @inheritDoc - */ - public function getLabels($key, array $values, $data) - { - return function ($value): string { - if ('_header' === $value) { - return 'Closing motive'; - } - - $cm = $this->motiveRepository->find($value); - - return $this->translatableStringHelper->localize( - $cm->getName() - ); - }; - } - - /** - * @inheritDoc - */ - public function getQueryKeys($data) - { - return ['closingmotive_aggregator']; - } - - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Group by closing motive'; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $qb->join('acp.closingMotive', 'cm'); @@ -93,11 +53,38 @@ class ClosingMotiveAggregator implements AggregatorInterface } } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Closing motive'; + } + + $cm = $this->motiveRepository->find($value); + + return $this->translatableStringHelper->localize( + $cm->getName() + ); + }; + } + + public function getQueryKeys($data) + { + return ['closingmotive_aggregator']; + } + + public function getTitle(): string + { + return 'Group by closing motive'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregator.php index d2bec5c32..534ef268a 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregator.php @@ -1,5 +1,14 @@ translator = $translator; } - /** - * @inheritDoc - */ - public function getLabels($key, array $values, $data) - { - return function ($value): string { - if ($value === '_header') { - return 'Confidentiality'; - } - switch ($value) { - - case true: - return $this->translator->trans('is confidential'); - - case false: - return $this->translator->trans('is not confidential'); - - default: - throw new LogicException(sprintf('The value %s is not valid', $value)); - } - return $value; - }; - } - - /** - * @inheritDoc - */ - public function getQueryKeys($data): array - { - return ['confidential_aggregator']; - } - - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Group by confidential'; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $qb->addSelect('acp.confidential AS confidential_aggregator'); @@ -90,11 +45,45 @@ class ConfidentialAggregator implements AggregatorInterface } } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Confidentiality'; + } + + switch ($value) { + case true: + return $this->translator->trans('is confidential'); + + case false: + return $this->translator->trans('is not confidential'); + + default: + throw new LogicException(sprintf('The value %s is not valid', $value)); + } + + return $value; + }; + } + + public function getQueryKeys($data): array + { + return ['confidential_aggregator']; + } + + public function getTitle(): string + { + return 'Group by confidential'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregator.php index 2a0215e82..ba6a1d3df 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregator.php @@ -1,14 +1,19 @@ translator = $translator; } - /** - * @inheritDoc - */ - public function getLabels($key, array $values, $data) - { - return function ($value) use ($data): string { - - if ($value === '_header') { - return $this->translator->trans('Rounded month duration'); - } - - if ($value === null) { - return $this->translator->trans('current duration'); // when closingDate is null - } - - if ($value === 0) { - return $this->translator->trans("duration 0 month"); - } - - return ''. $value . $this->translator->trans(' months'); - }; - } - - /** - * @inheritDoc - */ - public function getQueryKeys($data): array - { - return ['duration_aggregator']; - } - - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Group by duration'; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $qb // OUI - ->addSelect(' - (acp.closingDate - acp.openingDate +15) *12/365 + ->addSelect( + ' + (acp.closingDate - acp.openingDate +15) *12/365 AS duration_aggregator' ) //->addSelect('DATE_DIFF(acp.closingDate, acp.openingDate) AS duration_aggregator') @@ -124,8 +77,8 @@ class DurationAggregator implements AggregatorInterface ELSE EXTRACT(month FROM DATE_DIFF(acp.closingDate, acp.openingDate)) END ) AS duration_aggregator ') - */ - ; + */ +; $groupBy = $qb->getDQLPart('groupBy'); @@ -138,11 +91,42 @@ class DurationAggregator implements AggregatorInterface $qb->orderBy('duration_aggregator'); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return $this->translator->trans('Rounded month duration'); + } + + if (null === $value) { + return $this->translator->trans('current duration'); // when closingDate is null + } + + if (0 === $value) { + return $this->translator->trans('duration 0 month'); + } + + return '' . $value . $this->translator->trans(' months'); + }; + } + + public function getQueryKeys($data): array + { + return ['duration_aggregator']; + } + + public function getTitle(): string + { + return 'Group by duration'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregator.php index c5991e7f0..4bee25a39 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregator.php @@ -1,5 +1,14 @@ translator = $translator; } - /** - * @inheritDoc - */ - public function getLabels($key, array $values, $data) - { - return function ($value): string { - if ($value === '_header') { - return 'Emergency'; - } - switch ($value) { - - case true: - return $this->translator->trans('is emergency'); - - case false: - return $this->translator->trans('is not emergency'); - - default: - throw new LogicException(sprintf('The value %s is not valid', $value)); - } - return $value; - }; - } - - /** - * @inheritDoc - */ - public function getQueryKeys($data): array - { - return ['emergency_aggregator']; - } - - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Group by emergency'; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $qb->addSelect('acp.emergency AS emergency_aggregator'); @@ -90,11 +45,45 @@ class EmergencyAggregator implements AggregatorInterface } } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Emergency'; + } + + switch ($value) { + case true: + return $this->translator->trans('is emergency'); + + case false: + return $this->translator->trans('is not emergency'); + + default: + throw new LogicException(sprintf('The value %s is not valid', $value)); + } + + return $value; + }; + } + + public function getQueryKeys($data): array + { + return ['emergency_aggregator']; + } + + public function getTitle(): string + { + return 'Group by emergency'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregator.php index 3f98c7003..4c9fefe31 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregator.php @@ -1,5 +1,14 @@ translatableStringHelper = $translatableStringHelper; } - /** - * @inheritDoc - */ - public function getLabels($key, array $values, $data) - { - return function ($value): string { - if ('_header' === $value) { - return 'Evaluation'; - } - - $e = $this->evaluationRepository->find($value); - - return $this->translatableStringHelper->localize( - $e->getTitle() - ); - }; - } - - /** - * @inheritDoc - */ - public function getQueryKeys($data): array - { - return ['evaluation_aggregator']; - } - - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Group by evaluation'; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('acpw', $qb->getAllAliases())) { + if (!in_array('acpw', $qb->getAllAliases(), true)) { $qb->join('acp.works', 'acpw'); } $qb->join('acpw.accompanyingPeriodWorkEvaluations', 'we'); @@ -94,11 +56,38 @@ final class EvaluationAggregator implements AggregatorInterface } } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Evaluation'; + } + + $e = $this->evaluationRepository->find($value); + + return $this->translatableStringHelper->localize( + $e->getTitle() + ); + }; + } + + public function getQueryKeys($data): array + { + return ['evaluation_aggregator']; + } + + public function getTitle(): string + { + return 'Group by evaluation'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregator.php index 8da38b766..5962654c0 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregator.php @@ -1,5 +1,14 @@ translator = $translator; } - /** - * @inheritDoc - */ - public function getLabels($key, array $values, $data) - { - return function ($value): string { - if ($value === '_header') { - return 'Intensity'; - } - switch ($value) { - case 'occasional': - return $this->translator->trans('is occasional'); - case 'regular': - return $this->translator->trans('is regular'); - default: - throw new LogicException(sprintf('The value %s is not valid', $value)); - } - }; - } - - /** - * @inheritDoc - */ - public function getQueryKeys($data): array - { - return ['intensity_aggregator']; - } - - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Group by intensity'; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $qb->addSelect('acp.intensity AS intensity_aggregator'); @@ -86,11 +45,43 @@ class IntensityAggregator implements AggregatorInterface } } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Intensity'; + } + + switch ($value) { + case 'occasional': + return $this->translator->trans('is occasional'); + + case 'regular': + return $this->translator->trans('is regular'); + + default: + throw new LogicException(sprintf('The value %s is not valid', $value)); + } + }; + } + + public function getQueryKeys($data): array + { + return ['intensity_aggregator']; + } + + public function getTitle(): string + { + return 'Group by intensity'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php index 31b3fca66..c5c9c3180 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php @@ -1,18 +1,25 @@ translatableStringHelper = $translatableStringHelper; } - /** - * @inheritDoc - */ - public function getLabels($key, array $values, $data) - { - return function($value): string { - if ($value === '_header') { - return 'Job'; - } - - $j = $this->jobRepository->find($value); - - return $this->translatableStringHelper->localize( - $j->getLabel() - ); - }; - } - - /** - * @inheritDoc - */ - public function getQueryKeys($data): array - { - return ['job_aggregator']; - } - - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Group by user job'; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $qb->join('acp.job', 'j'); @@ -93,11 +52,38 @@ final class JobAggregator implements AggregatorInterface } } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Job'; + } + + $j = $this->jobRepository->find($value); + + return $this->translatableStringHelper->localize( + $j->getLabel() + ); + }; + } + + public function getQueryKeys($data): array + { + return ['job_aggregator']; + } + + public function getTitle(): string + { + return 'Group by user job'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregator.php index 2bb131435..7c4ad76fd 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregator.php @@ -1,5 +1,14 @@ translatableStringHelper = $translatableStringHelper; } - /** - * @inheritDoc - */ - public function getLabels($key, array $values, $data) - { - return function ($value): string { - if ('_header' === $value) { - return 'Origin'; - } - - $o = $this->repository->find($value); - - return $this->translatableStringHelper->localize( - $o->getLabel() - ); - }; - } - - /** - * @inheritDoc - */ - public function getQueryKeys($data): array - { - return ['origin_aggregator']; - } - - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Group by origin'; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $qb->join('acp.origin', 'o'); @@ -93,11 +54,38 @@ final class OriginAggregator implements AggregatorInterface } } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Origin'; + } + + $o = $this->repository->find($value); + + return $this->translatableStringHelper->localize( + $o->getLabel() + ); + }; + } + + public function getQueryKeys($data): array + { + return ['origin_aggregator']; + } + + public function getTitle(): string + { + return 'Group by origin'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php index 56522f2ed..323a17e26 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php @@ -15,16 +15,15 @@ use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Repository\UserRepository; use Chill\MainBundle\Templating\Entity\UserRender; use Chill\PersonBundle\Export\Declarations; -use Doctrine\ORM\Query\Expr\From; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; final class ReferrerAggregator implements AggregatorInterface { - private UserRepository $userRepository; - private UserRender $userRender; + private UserRepository $userRepository; + public function __construct( UserRepository $userRepository, UserRender $userRender @@ -51,7 +50,6 @@ final class ReferrerAggregator implements AggregatorInterface } else { $qb->groupBy('referrer_aggregator'); } - } public function applyOn(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregator.php index 5bddac42b..46187d558 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregator.php @@ -1,18 +1,25 @@ translatableStringHelper = $translatableStringHelper; } - /** - * @inheritDoc - */ - public function getLabels($key, array $values, $data) - { - return function ($value): string { - if ($value === '_header') { - return 'Scope'; - } - - $s = $this->scopeRepository->find($value); - - return $this->translatableStringHelper->localize( - $s->getName() - ); - }; - } - - /** - * @inheritDoc - */ - public function getQueryKeys($data): array - { - return ['scope_aggregator']; - } - - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Group by user scope'; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $qb->join('acp.scopes', 's'); @@ -93,11 +52,38 @@ final class ScopeAggregator implements AggregatorInterface } } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Scope'; + } + + $s = $this->scopeRepository->find($value); + + return $this->translatableStringHelper->localize( + $s->getName() + ); + }; + } + + public function getQueryKeys($data): array + { + return ['scope_aggregator']; + } + + public function getTitle(): string + { + return 'Group by user scope'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregator.php index a9257bd8e..f28cd559b 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregator.php @@ -1,5 +1,14 @@ actionRepository = $actionRepository; } - /** - * @inheritDoc - */ - public function getLabels($key, array $values, $data) - { - return function($value) { - if ('_header' === $value) { - return 'Social action'; - } - - $sa = $this->actionRepository->find($value); - - return $this->actionRender->renderString($sa, []); - }; - } - - /** - * @inheritDoc - */ - public function getQueryKeys($data): array - { - return ['socialaction_aggregator']; - } - - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Group by social action'; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('acpw', $qb->getAllAliases())) { + if (!in_array('acpw', $qb->getAllAliases(), true)) { $qb->join('acp.works', 'acpw'); } @@ -91,11 +55,36 @@ final class SocialActionAggregator implements AggregatorInterface } } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value) { + if ('_header' === $value) { + return 'Social action'; + } + + $sa = $this->actionRepository->find($value); + + return $this->actionRender->renderString($sa, []); + }; + } + + public function getQueryKeys($data): array + { + return ['socialaction_aggregator']; + } + + public function getTitle(): string + { + return 'Group by social action'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregator.php index fe2830d00..362d8e666 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregator.php @@ -1,5 +1,14 @@ issueRender = $issueRender; } - /** - * @inheritDoc - */ - public function getLabels($key, array $values, $data) - { - return function ($value): string { - - if ($value === '_header') { - return 'Social issues'; - } - - $i = $this->issueRepository->find($value); - - return $this->issueRender->renderString($i, []); - }; - } - - /** - * @inheritDoc - */ - public function getQueryKeys($data): array - { - return ['socialissue_aggregator']; - } - - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Group by social issue'; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $qb->join('acp.socialIssues', 'si'); @@ -90,11 +51,36 @@ final class SocialIssueAggregator implements AggregatorInterface } } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Social issues'; + } + + $i = $this->issueRepository->find($value); + + return $this->issueRender->renderString($i, []); + }; + } + + public function getQueryKeys($data): array + { + return ['socialissue_aggregator']; + } + + public function getTitle(): string + { + return 'Group by social issue'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php index 9b21373a8..7d3423a04 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php @@ -1,5 +1,14 @@ translator = $translator; } - /** - * @inheritDoc - */ - public function getLabels($key, array $values, $data) - { - return function ($value): string { - switch ($value) { - - case AccompanyingPeriod::STEP_DRAFT: - return $this->translator->trans('Draft'); - - case AccompanyingPeriod::STEP_CONFIRMED: - return $this->translator->trans('Confirmed'); - - case AccompanyingPeriod::STEP_CLOSED: - return $this->translator->trans('Closed'); - - case '_header': - return 'Step'; - - default: - throw new LogicException(sprintf('The value %s is not valid', $value)); - } - }; - } - - /** - * @inheritDoc - */ - public function getQueryKeys($data): array - { - return ['step_aggregator']; - } - - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('on_date', ChillDateType::class, [ - 'data' => new \DateTime(), - ]); - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Group by step'; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $qb->addSelect('acp.step AS step_aggregator'); @@ -119,14 +71,50 @@ final class StepAggregator implements AggregatorInterface //, FilterInterface $qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('on_date', ChillDateType::class, [ + 'data' => new DateTime(), + ]); + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + switch ($value) { + case AccompanyingPeriod::STEP_DRAFT: + return $this->translator->trans('Draft'); + + case AccompanyingPeriod::STEP_CONFIRMED: + return $this->translator->trans('Confirmed'); + + case AccompanyingPeriod::STEP_CLOSED: + return $this->translator->trans('Closed'); + + case '_header': + return 'Step'; + + default: + throw new LogicException(sprintf('The value %s is not valid', $value)); + } + }; + } + + public function getQueryKeys($data): array + { + return ['step_aggregator']; + } + + public function getTitle(): string + { + return 'Group by step'; + } + /* * TODO check if we need to add FilterInterface and DescribeAction Method to describe date filter ?? * @@ -138,5 +126,5 @@ final class StepAggregator implements AggregatorInterface //, FilterInterface ] ]; } - */ -} \ No newline at end of file + */ +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregator.php index e94290925..6c20ae548 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregator.php @@ -1,5 +1,14 @@ 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->addSelect('IDENTITY(eval.evaluation) AS evaluationtype_aggregator'); @@ -87,11 +50,36 @@ class EvaluationTypeAggregator implements AggregatorInterface } } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::EVAL_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Evaluation type'; + } + + $ev = $this->evaluationRepository->find($value); + + return $this->translatableStringHelper->localize($ev->getTitle()); + }; + } + + public function getQueryKeys($data): array + { + return ['evaluationtype_aggregator']; + } + + public function getTitle(): string + { + return 'Group by evaluation type'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregator.php index df50b76f0..417941a04 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregator.php @@ -1,13 +1,24 @@ 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) { - if (!in_array('composition', $qb->getAllAliases())) { + if (!in_array('composition', $qb->getAllAliases(), true)) { $qb->join('household.compositions', 'composition'); } @@ -102,11 +63,41 @@ class ChildrenNumberAggregator implements AggregatorInterface ); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::HOUSEHOLD_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('on_date', ChillDateType::class, [ + 'data' => new DateTime('now'), + ]); + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Number of children'; + } + + return $this->translator->trans( + 'household_composition.numberOfChildren children in household', + [ + 'numberOfChildren' => $value, + ] + ); + }; + } + + public function getQueryKeys($data): array + { + return ['childrennumber_aggregator']; + } + + public function getTitle(): string + { + return 'Group by number of children'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php index 2c6ab174f..429431af6 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php @@ -1,5 +1,14 @@ 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) { - if (!in_array('composition', $qb->getAllAliases())) { + if (!in_array('composition', $qb->getAllAliases(), true)) { $qb->join('household.compositions', 'composition'); } @@ -118,11 +79,40 @@ class CompositionAggregator implements AggregatorInterface $qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::HOUSEHOLD_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('on_date', ChillDateType::class, [ + 'data' => new DateTime('now'), + ]); + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Composition'; + } + + $c = $this->typeRepository->find($value); + + return $this->translatableStringHelper->localize( + $c->getLabel() + ); + }; + } + + public function getQueryKeys($data): array + { + return ['composition_aggregator']; + } + + public function getTitle(): string + { + return 'Group by composition'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php index ec4a270ac..872b3f9f5 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php @@ -18,19 +18,18 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Repository\Household\PositionRepository; use DateTime; use Doctrine\ORM\QueryBuilder; -use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Contracts\Translation\TranslatorInterface; final class HouseholdPositionAggregator implements AggregatorInterface, ExportElementValidatedInterface { - private TranslatorInterface $translator; - private PositionRepository $positionRepository; private TranslatableStringHelper $translatableStringHelper; + private TranslatorInterface $translator; + public function __construct(TranslatorInterface $translator, TranslatableStringHelper $translatableStringHelper, PositionRepository $positionRepository) { $this->translator = $translator; @@ -70,7 +69,6 @@ final class HouseholdPositionAggregator implements AggregatorInterface, ExportEl } else { $qb->groupBy('household_position_aggregator'); } - } public function applyOn() diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/MaritalStatusAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/MaritalStatusAggregator.php index 3d4c1f7cd..4a9534a6d 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/MaritalStatusAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/MaritalStatusAggregator.php @@ -18,7 +18,6 @@ use Chill\PersonBundle\Repository\MaritalStatusRepository; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; - final class MaritalStatusAggregator implements AggregatorInterface { private MaritalStatusRepository $maritalStatusRepository; diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/NationalityAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/NationalityAggregator.php index 8c3c0e9a7..fcae239cb 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/NationalityAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/NationalityAggregator.php @@ -124,19 +124,17 @@ final class NationalityAggregator implements AggregatorInterface, ExportElementV ->getQuery() ->getResult(\Doctrine\ORM\Query::HYDRATE_SCALAR); - // initialize array and add blank key for null values $labels = [ '' => $this->translator->trans('without data'), '_header' => $this->translator->trans('Nationality'), ]; - foreach ($countries as $row) { $labels[$row['c_countryCode']] = $this->translatableStringHelper->localize($row['c_name']); } } - + if ('continent' === $data['group_by_level']) { $labels = [ 'EU' => $this->translator->trans('Europe'), @@ -151,10 +149,9 @@ final class NationalityAggregator implements AggregatorInterface, ExportElementV ]; } - return function ($value) use ($labels): string { + return static function ($value) use ($labels): string { return $labels[$value]; }; - } public function getQueryKeys($data) diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php index d8f485561..3ead139ab 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php @@ -47,7 +47,6 @@ final class ActionTypeAggregator implements AggregatorInterface } else { $qb->groupBy('action_type_aggregator'); } - } public function applyOn() diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php index cb908ef3e..b39f8bee0 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php @@ -47,7 +47,6 @@ final class GoalAggregator implements AggregatorInterface } else { $qb->groupBy('goal_aggregator'); } - } public function applyOn() diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/JobAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/JobAggregator.php index ddab1f7cd..c89219f2e 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/JobAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/JobAggregator.php @@ -1,18 +1,25 @@ translatableStringHelper = $translatableStringHelper; } - /** - * @inheritDoc - */ - public function getLabels($key, array $values, $data) - { - return function($value): string { - if ($value === '_header') { - return 'Job'; - } - - $j = $this->jobRepository->find($value); - - return $this->translatableStringHelper->localize( - $j->getLabel() - ); - }; - } - - /** - * @inheritDoc - */ - public function getQueryKeys($data): array - { - return ['job_aggregator']; - } - - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Group by treating agent job'; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $qb->join('acpw.referrers', 'u'); @@ -93,11 +52,38 @@ final class JobAggregator implements AggregatorInterface } } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::SOCIAL_WORK_ACTION_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Job'; + } + + $j = $this->jobRepository->find($value); + + return $this->translatableStringHelper->localize( + $j->getLabel() + ); + }; + } + + public function getQueryKeys($data): array + { + return ['job_aggregator']; + } + + public function getTitle(): string + { + return 'Group by treating agent job'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ReferrerAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ReferrerAggregator.php index 1815ecc19..a6ae042e5 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ReferrerAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ReferrerAggregator.php @@ -15,16 +15,15 @@ use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Repository\UserRepository; use Chill\MainBundle\Templating\Entity\UserRender; use Chill\PersonBundle\Export\Declarations; -use Doctrine\ORM\Query\Expr\From; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; final class ReferrerAggregator implements AggregatorInterface { - private UserRepository $userRepository; - private UserRender $userRender; + private UserRepository $userRepository; + public function __construct( UserRepository $userRepository, UserRender $userRender @@ -51,7 +50,6 @@ final class ReferrerAggregator implements AggregatorInterface } else { $qb->groupBy('referrer_aggregator'); } - } public function applyOn(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php index fc602f973..fa750b486 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php @@ -49,7 +49,6 @@ final class ResultAggregator implements AggregatorInterface } else { $qb->groupBy('result_aggregator'); } - } public function applyOn() diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ScopeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ScopeAggregator.php index 74a73537c..0e1a7a044 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ScopeAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ScopeAggregator.php @@ -1,18 +1,25 @@ translatableStringHelper = $translatableStringHelper; } - /** - * @inheritDoc - */ - public function getLabels($key, array $values, $data) - { - return function ($value): string { - if ($value === '_header') { - return 'Scope'; - } - - $s = $this->scopeRepository->find($value); - - return $this->translatableStringHelper->localize( - $s->getName() - ); - }; - } - - /** - * @inheritDoc - */ - public function getQueryKeys($data): array - { - return ['scope_aggregator']; - } - - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Group by treating agent scope'; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $qb->join('acpw.referrers', 'u'); @@ -93,11 +52,38 @@ final class ScopeAggregator implements AggregatorInterface } } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::SOCIAL_WORK_ACTION_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value): string { + if ('_header' === $value) { + return 'Scope'; + } + + $s = $this->scopeRepository->find($value); + + return $this->translatableStringHelper->localize( + $s->getName() + ); + }; + } + + public function getQueryKeys($data): array + { + return ['scope_aggregator']; + } + + public function getTitle(): string + { + return 'Group by treating agent scope'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Declarations.php b/src/Bundle/ChillPersonBundle/Export/Declarations.php index f6c870e4d..0f4d84e3c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Declarations.php +++ b/src/Bundle/ChillPersonBundle/Export/Declarations.php @@ -16,15 +16,15 @@ namespace Chill\PersonBundle\Export; */ abstract class Declarations { - public const PERSON_IMPLIED_IN = 'person_implied_in'; - - public const PERSON_TYPE = 'person'; - public const ACP_TYPE = 'accompanying_period'; - public const SOCIAL_WORK_ACTION_TYPE = 'social_actions'; - public const EVAL_TYPE = 'evaluation'; public const HOUSEHOLD_TYPE = 'household'; + + public const PERSON_IMPLIED_IN = 'person_implied_in'; + + public const PERSON_TYPE = 'person'; + + public const SOCIAL_WORK_ACTION_TYPE = 'social_actions'; } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php index a842284f5..72ef72119 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php @@ -21,9 +21,9 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; use Doctrine\ORM\QueryBuilder; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Security\Core\Role\Role; -use LogicException; class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface { @@ -40,11 +40,6 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface // TODO: Implement buildForm() method. } - public function getTitle(): string - { - return 'Count accompanying courses'; - } - public function getAllowedFormattersTypes(): array { return [FormatterInterface::TYPE_TABULAR]; @@ -55,6 +50,11 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface return 'Count accompanying courses by various parameters'; } + public function getGroup(): string + { + return 'Exports of accompanying courses'; + } + public function getLabels($key, array $values, $data) { if ('export_result' !== $key) { @@ -79,6 +79,11 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR); } + public function getTitle(): string + { + return 'Count accompanying courses'; + } + public function getType(): string { return Declarations::ACP_TYPE; @@ -104,9 +109,4 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface Declarations::ACP_TYPE, ]; } - - public function getGroup(): string - { - return 'Exports of accompanying courses'; - } -} \ 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 1e703754e..a329afee1 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php @@ -1,5 +1,14 @@ repository = $em->getRepository(AccompanyingPeriod::class); } - /** - * @inheritDoc - */ public function buildForm(FormBuilderInterface $builder) { // TODO: Implement buildForm() method. } - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Count evaluations'; - } - - /** - * @inheritDoc - */ public function getAllowedFormattersTypes(): array { return [FormatterInterface::TYPE_TABULAR]; } - /** - * @inheritDoc - */ public function getDescription(): string { return 'Count evaluation by various parameters.'; } - /** - * @inheritDoc - */ + public function getGroup(): string + { + return 'Exports of evaluations'; + } + public function getLabels($key, array $values, $data) { if ('export_result' !== $key) { @@ -74,56 +67,42 @@ class CountEvaluation implements ExportInterface, GroupedExportInterface }; } - /** - * @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 getTitle(): string + { + return 'Count evaluations'; + } + public function getType(): string { return Declarations::EVAL_TYPE; } - /** - * @inheritDoc - */ public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { $qb = $this->repository->createQueryBuilder('acp') ->join('acp.works', 'acpw') - ->join('acpw.accompanyingPeriodWorkEvaluations', 'eval') - ; + ->join('acpw.accompanyingPeriodWorkEvaluations', 'eval'); $qb->select('COUNT(eval.id) AS export_result'); return $qb; } - /** - * @inheritDoc - */ public function requiredRole() { return new Role(AccompanyingPeriodVoter::STATS); } - /** - * @inheritDoc - */ public function supportsModifiers(): array { return [ @@ -132,9 +111,4 @@ class CountEvaluation implements ExportInterface, GroupedExportInterface //Declarations::SOCIAL_WORK_ACTION_TYPE, ]; } - - public function getGroup(): string - { - return 'Exports of evaluations'; - } -} \ 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 a43345ff3..791c42772 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php @@ -1,5 +1,14 @@ repository = $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 getGroup(): string + { + return 'Exports of households'; + } + public function getLabels($key, array $values, $data) { if ('export_result' !== $key) { @@ -73,59 +67,45 @@ class CountHousehold implements ExportInterface, GroupedExportInterface }; } - /** - * @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 getTitle(): string + { + return 'Count households'; + } + public function getType(): string { return Declarations::HOUSEHOLD_TYPE; } - /** - * @inheritDoc - */ public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { $qb = $this->repository->createQueryBuilder('acp') ->join('acp.participations', 'acppart') ->join('acppart.person', 'person') ->join('person.householdParticipations', 'householdmember') - ->join('householdmember.household', 'household') - ; + ->join('householdmember.household', 'household'); $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 [ @@ -133,9 +113,4 @@ class CountHousehold implements ExportInterface, GroupedExportInterface //Declarations::ACP_TYPE ]; } - - public function getGroup(): string - { - return 'Exports of households'; - } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php index 3ef74a69e..729e5a202 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php @@ -25,7 +25,6 @@ use Symfony\Component\Security\Core\Role\Role; class CountPerson implements ExportInterface, GroupedExportInterface { - protected PersonRepository $personRepository; public function __construct( @@ -49,6 +48,11 @@ class CountPerson implements ExportInterface, GroupedExportInterface return 'Count people by various parameters.'; } + public function getGroup(): string + { + return 'Exports of persons'; + } + public function getLabels($key, array $values, $data) { if ('export_result' !== $key) { @@ -117,9 +121,4 @@ class CountPerson implements ExportInterface, GroupedExportInterface //Declarations::ACP_TYPE ]; } - - public function getGroup(): string - { - return 'Exports of persons'; - } } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php b/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php index d271bd3da..152793f97 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php @@ -1,5 +1,14 @@ repository = $em->getRepository(AccompanyingPeriod::class); } - /** - * @inheritDoc - */ public function buildForm(FormBuilderInterface $builder) { // TODO: Implement buildForm() method. } - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Count people participating in an accompanying course'; - } - - /** - * @inheritDoc - */ public function getAllowedFormattersTypes(): array { return [FormatterInterface::TYPE_TABULAR]; } - /** - * @inheritDoc - */ public function getDescription(): string { return 'Count people participating in an accompanying course by various parameters.'; } - /** - * @inheritDoc - */ + public function getGroup(): string + { + return 'Exports of persons'; + } + public function getLabels($key, array $values, $data) { if ('export_result' !== $key) { @@ -73,66 +67,47 @@ class CountPersonWithAccompanyingCourse implements ExportInterface, GroupedExpor }; } - /** - * @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 getTitle(): string + { + return 'Count people participating in an accompanying course'; + } + public function getType(): string { return Declarations::HOUSEHOLD_TYPE; } - /** - * @inheritDoc - */ public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { $qb = $this->repository->createQueryBuilder('acp') ->join('acp.participations', 'acppart') - ->join('acppart.person', 'person') - ; + ->join('acppart.person', 'person'); $qb->select('COUNT(DISTINCT person.id) AS export_result'); return $qb; } - /** - * @inheritDoc - */ public function requiredRole() { return new Role(AccompanyingPeriodVoter::STATS); } - /** - * @inheritDoc - */ public function supportsModifiers(): array { return [ Declarations::ACP_TYPE, - Declarations::PERSON_TYPE + Declarations::PERSON_TYPE, ]; } - - public function getGroup(): string - { - return 'Exports of persons'; - } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php b/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php index cfea1f0c1..e19a40a0f 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php @@ -21,8 +21,8 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; use Doctrine\ORM\QueryBuilder; -use Symfony\Component\Form\FormBuilderInterface; use LogicException; +use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Security\Core\Role\Role; class CountSocialWorkActions implements ExportInterface, GroupedExportInterface @@ -40,11 +40,6 @@ class CountSocialWorkActions implements ExportInterface, GroupedExportInterface // No form necessary? } - public function getTitle(): string - { - return 'Count social work actions'; - } - public function getAllowedFormattersTypes(): array { return [FormatterInterface::TYPE_TABULAR]; @@ -55,6 +50,11 @@ class CountSocialWorkActions implements ExportInterface, GroupedExportInterface return 'Count social work actions by various parameters'; } + public function getGroup(): string + { + return 'Exports of social work actions'; + } + public function getLabels($key, array $values, $data) { if ('export_result' !== $key) { @@ -79,6 +79,11 @@ class CountSocialWorkActions implements ExportInterface, GroupedExportInterface return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR); } + public function getTitle(): string + { + return 'Count social work actions'; + } + public function getType(): string { return Declarations::SOCIAL_WORK_ACTION_TYPE; @@ -87,8 +92,7 @@ class CountSocialWorkActions implements ExportInterface, GroupedExportInterface public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder { $qb = $this->repository->createQueryBuilder('acp') - ->join('acp.works', 'acpw') - ; + ->join('acp.works', 'acpw'); $qb->select('COUNT(acpw.id) as export_result'); @@ -107,9 +111,4 @@ class CountSocialWorkActions implements ExportInterface, GroupedExportInterface Declarations::SOCIAL_WORK_ACTION_TYPE, ]; } - - public function getGroup(): string - { - return 'Exports of social work actions'; - } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php index f2f72aa29..5f519e647 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php @@ -136,6 +136,11 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou return 'Create a list of people according to various filters.'; } + public function getGroup(): string + { + return 'Exports of persons'; + } + public function getLabels($key, array $values, $data) { switch ($key) { @@ -426,9 +431,11 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou if ('_header' === $value) { return $this->translatableStringHelper->localize($cf->getName()); } + if (null === $value) { return ''; } + return $this->customFieldProvider ->getCustomFieldByType($cf->getType()) ->render(json_decode($value, true), $cf, 'csv'); @@ -437,6 +444,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou if ($cfType instanceof CustomFieldChoice && $cfType->isMultiple($cf)) { return function ($value) use ($cf, $cfType, $key) { $slugChoice = $this->extractInfosFromSlug($key)['additionnalInfos']['choiceSlug']; + if (null === $value) { return ''; } @@ -475,9 +483,4 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou return $uid; } - - public function getGroup(): string - { - return 'Exports of persons'; - } } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonDuplicate.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonDuplicate.php index 2c65acb84..7f8688493 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonDuplicate.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonDuplicate.php @@ -129,6 +129,11 @@ class ListPersonDuplicate implements DirectExportInterface, ExportElementValidat return 'Create a list of duplicate people'; } + public function getGroup(): string + { + return 'Exports of persons'; + } + /** * @return string */ @@ -197,9 +202,4 @@ class ListPersonDuplicate implements DirectExportInterface, ExportElementValidat return $result->fetchAllAssociative(); } - - public function getGroup(): string - { - return 'Exports of persons'; - } } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php b/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php index 417411f98..718d04643 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php @@ -18,6 +18,7 @@ use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; +use DateTime; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; @@ -27,7 +28,6 @@ use Symfony\Component\Security\Core\Role\Role; class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportInterface { - private EntityRepository $repository; public function __construct( @@ -36,44 +36,29 @@ class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportIn $this->repository = $em->getRepository(AccompanyingPeriod::class); } - /** - * @inheritDoc - */ public function buildForm(FormBuilderInterface $builder): void { $builder->add('closingdate', ChillDateType::class, [ 'label' => 'Closingdate to apply', - 'data' => new \DateTime('now'), + 'data' => new DateTime('now'), ]); } - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Accompanying courses duration'; - } - - /** - * @inheritDoc - */ public function getAllowedFormattersTypes(): array { return [FormatterInterface::TYPE_TABULAR]; } - /** - * @inheritDoc - */ public function getDescription(): string { return 'Create an average of accompanying courses duration according to various filters'; } - /** - * @inheritDoc - */ + public function getGroup(): string + { + return 'Exports of accompanying courses'; + } + public function getLabels($key, array $values, $data) { if ('export_result' !== $key) { @@ -88,33 +73,26 @@ class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportIn }; } - /** - * @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 getTitle(): string + { + return 'Accompanying courses duration'; + } + public function getType(): string { return Declarations::ACP_TYPE; } - /** - * @inheritDoc - */ public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder { $qb = $this->repository->createQueryBuilder('acp'); @@ -125,34 +103,22 @@ class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportIn WHEN acp.closingDate IS NOT NULL THEN acp.closingDate ELSE :force_closingDate - END ) - acp.openingDate + END ) - acp.openingDate ) AS export_result') - ->setParameter('force_closingDate', $data['closingdate']) - ; + ->setParameter('force_closingDate', $data['closingdate']); return $qb; } - /** - * @inheritDoc - */ public function requiredRole(): Role { return new Role(AccompanyingPeriodVoter::STATS); } - /** - * @inheritDoc - */ public function supportsModifiers(): array { return [ - Declarations::ACP_TYPE + Declarations::ACP_TYPE, ]; } - - public function getGroup(): string - { - return 'Exports of accompanying courses'; - } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActiveOnDateFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActiveOnDateFilter.php index 933350134..6b1ad6612 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActiveOnDateFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActiveOnDateFilter.php @@ -1,60 +1,32 @@ add('on_date', ChillDateType::class, [ - 'data' => new DateTime(), - ]) - ; - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Filter by active on date'; - } - - /** - * @inheritDoc - */ - public function describeAction($data, $format = 'string'): array - { - return ['Filtered by actives courses: active on %ondate%', [ - '%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'); @@ -77,11 +49,28 @@ class ActiveOnDateFilter implements FilterInterface $qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('on_date', ChillDateType::class, [ + 'data' => new DateTime(), + ]); + } + + public function describeAction($data, $format = 'string'): array + { + return ['Filtered by actives courses: active on %ondate%', [ + '%ondate%' => $data['on_date']->format('d-m-Y'), + ]]; + } + + public function getTitle(): string + { + return 'Filter by active on date'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilter.php index 2f83667c9..c37097d30 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilter.php @@ -1,64 +1,32 @@ add('date_from', ChillDateType::class, [ - 'data' => new DateTime(), - ]) - ->add('date_to', ChillDateType::class, [ - 'data' => new DateTime(), - ]) - ; - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Filter by active at least one day between dates'; - } - - /** - * @inheritDoc - */ - public function describeAction($data, $format = 'string'): array - { - return ['Filtered by actives courses: at least one day between %datefrom% and %dateto%', [ - '%datefrom%' => $data['date_from']->format('d-m-Y'), - '%dateto%' => $data['date_to']->format('d-m-Y'), - ]]; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $where = $qb->getDQLPart('where'); @@ -83,11 +51,32 @@ class ActiveOneDayBetweenDatesFilter implements FilterInterface $qb->setParameter('dateto', $data['date_to'], Types::DATE_MUTABLE); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('date_from', ChillDateType::class, [ + 'data' => new DateTime(), + ]) + ->add('date_to', ChillDateType::class, [ + 'data' => new DateTime(), + ]); + } + + public function describeAction($data, $format = 'string'): array + { + return ['Filtered by actives courses: at least one day between %datefrom% and %dateto%', [ + '%datefrom%' => $data['date_from']->format('d-m-Y'), + '%dateto%' => $data['date_to']->format('d-m-Y'), + ]]; + } + + public function getTitle(): string + { + return 'Filter by active at least one day between dates'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilter.php index 938a4e4b4..6efe29233 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActivityTypeFilter.php @@ -1,5 +1,14 @@ translatableStringHelper = $translatableStringHelper; } - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_activitytypes', EntityType::class, [ - 'class' => ActivityType::class, - 'choice_label' => function (ActivityType $aty) { - return $this->translatableStringHelper->localize($aty->getName()); - }, - 'multiple' => true, - 'expanded' => true - ]); - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Filter accompanying course by activity type'; - } - - /** - * @inheritDoc - */ - public function describeAction($data, $format = 'string'): array - { - $types = []; - - foreach ($data['accepted_activitytypes'] as $aty) { - $types[] = $this->translatableStringHelper->localize($aty->getName()); - } - - return ['Filtered by activity types: only %activitytypes%', [ - '%activitytypes%' => implode(", ou ", $types) - ]]; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { // One2many between activity and accompanyingperiod is not reversed ! @@ -83,8 +47,7 @@ class ActivityTypeFilter implements FilterInterface $qb ->join('act.accompanyingPeriod', 'acp') - ->join('act.activityType', 'aty') - ; + ->join('act.activityType', 'aty'); $where = $qb->getDQLPart('where'); $clause = $qb->expr()->in('aty.id', ':activitytypes'); @@ -97,14 +60,40 @@ class ActivityTypeFilter implements FilterInterface $qb->add('where', $where); $qb->setParameter('activitytypes', $data['accepted_activitytypes']); - } - /** - * @inheritDoc - */ public function applyOn() { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_activitytypes', EntityType::class, [ + 'class' => ActivityType::class, + 'choice_label' => function (ActivityType $aty) { + return $this->translatableStringHelper->localize($aty->getName()); + }, + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $types = []; + + foreach ($data['accepted_activitytypes'] as $aty) { + $types[] = $this->translatableStringHelper->localize($aty->getName()); + } + + return ['Filtered by activity types: only %activitytypes%', [ + '%activitytypes%' => implode(', ou ', $types), + ]]; + } + + public function getTitle(): string + { + return 'Filter accompanying course by activity type'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/AdministrativeLocationFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/AdministrativeLocationFilter.php index d3fde5b91..7bea4eb7f 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/AdministrativeLocationFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/AdministrativeLocationFilter.php @@ -1,5 +1,14 @@ translatableStringHelper = $translatableStringHelper; } - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_locations', EntityType::class, [ - 'class' => Location::class, - 'choice_label' => function (Location $l) { - return $l->getName() .' ('. $this->translatableStringHelper->localize($l->getLocationType()->getTitle()) . ')'; - }, - 'multiple' => true, - 'expanded' => true, - ]); - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Filter by administrative location'; - } - - /** - * @inheritDoc - */ - public function describeAction($data, $format = 'string'): array - { - $locations = []; - - foreach ($data['accepted_locations'] as $l) { - $locations[] = $l->getName(); - } - - return ['Filtered by administratives locations: only %locations%', [ - '%locations%' => implode(", ou ", $locations) - ]]; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $where = $qb->getDQLPart('where'); @@ -89,11 +50,38 @@ class AdministrativeLocationFilter implements FilterInterface $qb->setParameter('locations', $data['accepted_locations']); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_locations', EntityType::class, [ + 'class' => Location::class, + 'choice_label' => function (Location $l) { + return $l->getName() . ' (' . $this->translatableStringHelper->localize($l->getLocationType()->getTitle()) . ')'; + }, + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $locations = []; + + foreach ($data['accepted_locations'] as $l) { + $locations[] = $l->getName(); + } + + return ['Filtered by administratives locations: only %locations%', [ + '%locations%' => implode(', ou ', $locations), + ]]; + } + + public function getTitle(): string + { + return 'Filter by administrative location'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ClosingMotiveFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ClosingMotiveFilter.php index 23fce3f40..150a66ed1 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ClosingMotiveFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ClosingMotiveFilter.php @@ -1,21 +1,27 @@ translatableStringHelper = $translatableStringHelper; } - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_closingmotives', EntityType::class, [ - 'class' => ClosingMotive::class, - 'choice_label' => function (ClosingMotive $cm) { - return $this->translatableStringHelper->localize($cm->getName()); - }, - 'multiple' => true, - 'expanded' => true, - ]); - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Filter by closing motive'; - } - - /** - * @inheritDoc - */ - public function describeAction($data, $format = 'string'): array - { - $motives = []; - - foreach ($data['accepted_closingmotives'] as $k => $v) { - $motives[] = $this->translatableStringHelper->localize($v->getName()); - } - - return [ - 'Filtered by closingmotive: only %closingmotives%', [ - '%closingmotives%' => implode(', ou ', $motives) - ]]; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $where = $qb->getDQLPart('where'); @@ -90,11 +50,39 @@ class ClosingMotiveFilter implements FilterInterface $qb->setParameter('closingmotive', $data['accepted_closingmotives']); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_closingmotives', EntityType::class, [ + 'class' => ClosingMotive::class, + 'choice_label' => function (ClosingMotive $cm) { + return $this->translatableStringHelper->localize($cm->getName()); + }, + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $motives = []; + + foreach ($data['accepted_closingmotives'] as $k => $v) { + $motives[] = $this->translatableStringHelper->localize($v->getName()); + } + + return [ + 'Filtered by closingmotive: only %closingmotives%', [ + '%closingmotives%' => implode(', ou ', $motives), + ], ]; + } + + public function getTitle(): string + { + return 'Filter by closing motive'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ConfidentialFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ConfidentialFilter.php index 23286f941..6fedf37f0 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ConfidentialFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ConfidentialFilter.php @@ -1,5 +1,14 @@ false, 'is confidential' => true, ]; - - private CONST DEFAULT_CHOICE = false; - + + private const DEFAULT_CHOICE = false; + private TranslatorInterface $translator; - + public function __construct(TranslatorInterface $translator) { $this->translator = $translator; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_confidentials', ChoiceType::class, [ - 'choices' => self::CHOICES, - 'multiple' => false, - 'expanded' => true, - 'empty_data' => self::DEFAULT_CHOICE, - 'data' => self::DEFAULT_CHOICE, - ]); - } - - public function getTitle(): string - { - return 'Filter by confidential'; - } - - public function describeAction($data, $format = 'string'): array - { - dump($data, self::CHOICES); - - foreach (self::CHOICES as $k => $v) { - if ($v === $data['accepted_confidentials']) { - $choice = $k; - } - } - - return [ - 'Filtered by confidential: only %confidential%', [ - '%confidential%' => $this->translator->trans($choice) - ] - ]; - } - public function addRole() { return null; @@ -84,4 +60,36 @@ class ConfidentialFilter implements FilterInterface return Declarations::ACP_TYPE; } -} \ No newline at end of file + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_confidentials', ChoiceType::class, [ + 'choices' => self::CHOICES, + 'multiple' => false, + 'expanded' => true, + 'empty_data' => self::DEFAULT_CHOICE, + 'data' => self::DEFAULT_CHOICE, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + dump($data, self::CHOICES); + + foreach (self::CHOICES as $k => $v) { + if ($v === $data['accepted_confidentials']) { + $choice = $k; + } + } + + return [ + 'Filtered by confidential: only %confidential%', [ + '%confidential%' => $this->translator->trans($choice), + ], + ]; + } + + public function getTitle(): string + { + return 'Filter by confidential'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilter.php index ddebbdc6f..76ba40a59 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserJobFilter.php @@ -35,17 +35,6 @@ class CurrentUserJobFilter implements FilterInterface $this->security = $security; } - public function describeAction($data, $format = 'string') - { - return [ - 'Filtered by user job: only %job%', [ - '%job%' => $this->translatableStringHelper->localize( - $this->getUserJob()->getLabel() - ) - ], - ]; - } - public function addRole() { return null; @@ -75,16 +64,27 @@ class CurrentUserJobFilter implements FilterInterface { } + public function describeAction($data, $format = 'string') + { + return [ + 'Filtered by user job: only %job%', [ + '%job%' => $this->translatableStringHelper->localize( + $this->getUserJob()->getLabel() + ), + ], + ]; + } + public function getTitle() { return 'Filter by user job'; } - private function getUserJob():UserJob + private function getUserJob(): UserJob { /** @var User $user */ $user = $this->security->getUser(); return $user->getUserJob(); } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilter.php index e6c56f11e..37dcac27c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilter.php @@ -18,7 +18,6 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; 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; use Symfony\Component\Security\Core\Security; @@ -26,9 +25,6 @@ class CurrentUserScopeFilter implements FilterInterface { private Security $security; - /** - * @var TranslatableStringHelper - */ private TranslatableStringHelper $translatableStringHelper; public function __construct( @@ -39,17 +35,6 @@ class CurrentUserScopeFilter implements FilterInterface $this->security = $security; } - public function describeAction($data, $format = 'string') - { - return [ - 'Filtered by user main scope: only %scope%', [ - '%scope%' => $this->translatableStringHelper->localize( - $this->getUserMainScope()->getName() - ) - ] - ]; - } - public function addRole() { return null; @@ -70,7 +55,6 @@ class CurrentUserScopeFilter implements FilterInterface $qb->add('where', $where); $qb->setParameter('userscope', $this->getUserMainScope()); - } public function applyOn() @@ -82,16 +66,27 @@ class CurrentUserScopeFilter implements FilterInterface { } + public function describeAction($data, $format = 'string') + { + return [ + 'Filtered by user main scope: only %scope%', [ + '%scope%' => $this->translatableStringHelper->localize( + $this->getUserMainScope()->getName() + ), + ], + ]; + } + public function getTitle() { return 'Filter by user scope'; } - private function getUserMainScope():Scope + private function getUserMainScope(): Scope { /** @var User $user */ $user = $this->security->getUser(); return $user->getMainScope(); } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EmergencyFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EmergencyFilter.php index ed874be2b..6a755638b 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EmergencyFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EmergencyFilter.php @@ -1,5 +1,14 @@ true, 'is not emergency' => false, ]; - - private CONST DEFAULT_CHOICE = false; - + + private const DEFAULT_CHOICE = false; + private TranslatorInterface $translator; - + public function __construct(TranslatorInterface $translator) { $this->translator = $translator; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_emergency', ChoiceType::class, [ - 'choices' => self::CHOICES, - 'multiple' => false, - 'expanded' => true, - 'empty_data' => self::DEFAULT_CHOICE, - 'data' => self::DEFAULT_CHOICE, - ]); - } - - public function getTitle(): string - { - return 'Filter by emergency'; - } - - public function describeAction($data, $format = 'string'): array - { - foreach (self::CHOICES as $k => $v) { - if ($v === $data['accepted_emergency']) { - $choice = $k; - } - } - - return [ - 'Filtered by emergency: only %emergency%', [ - '%emergency%' => $this->translator->trans($choice) - ] - ]; - } - public function addRole() { return null; @@ -82,4 +60,34 @@ class EmergencyFilter implements FilterInterface return Declarations::ACP_TYPE; } -} \ No newline at end of file + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_emergency', ChoiceType::class, [ + 'choices' => self::CHOICES, + 'multiple' => false, + 'expanded' => true, + 'empty_data' => self::DEFAULT_CHOICE, + 'data' => self::DEFAULT_CHOICE, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + foreach (self::CHOICES as $k => $v) { + if ($v === $data['accepted_emergency']) { + $choice = $k; + } + } + + return [ + 'Filtered by emergency: only %emergency%', [ + '%emergency%' => $this->translator->trans($choice), + ], + ]; + } + + public function getTitle(): string + { + return 'Filter by emergency'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EvaluationFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EvaluationFilter.php index 4756f06c2..ab47c1cc7 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EvaluationFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EvaluationFilter.php @@ -1,5 +1,14 @@ translatableStringHelper = $translatableStringHelper; } - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_evaluations', EntityType::class, [ - 'class' => Evaluation::class, - 'choice_label' => function (Evaluation $ev) { - return $this->translatableStringHelper->localize($ev->getTitle()); - }, - 'multiple' => true, - 'expanded' => true, - ]); - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Filter by evaluation'; - } - - /** - * @inheritDoc - */ - public function describeAction($data, $format = 'string'): array - { - $evaluations = []; - - foreach ($data['accepted_evaluations'] as $ev) { - $evaluations[] = $this->translatableStringHelper->localize($ev->getTitle()); - } - - return ['Filtered by evaluations: only %evals%', [ - '%evals%' => implode(", ou ", $evaluations) - ]]; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $qb ->join('acp.works', 'acpw') ->join('acpw.accompanyingPeriodWorkEvaluations', 'we') - ->join('we.evaluation', 'ev') - ; + ->join('we.evaluation', 'ev'); $where = $qb->getDQLPart('where'); $clause = $qb->expr()->in('ev.id', ':evaluations'); @@ -95,11 +55,38 @@ class EvaluationFilter implements FilterInterface $qb->setParameter('evaluations', $data['accepted_evaluations']); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_evaluations', EntityType::class, [ + 'class' => Evaluation::class, + 'choice_label' => function (Evaluation $ev) { + return $this->translatableStringHelper->localize($ev->getTitle()); + }, + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $evaluations = []; + + foreach ($data['accepted_evaluations'] as $ev) { + $evaluations[] = $this->translatableStringHelper->localize($ev->getTitle()); + } + + return ['Filtered by evaluations: only %evals%', [ + '%evals%' => implode(', ou ', $evaluations), + ]]; + } + + public function getTitle(): string + { + return 'Filter by evaluation'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilter.php index 5445e20a4..5a1d74c1f 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilter.php @@ -1,11 +1,21 @@ add('date', ChillDateType::class, [ - 'data' => new \DateTime(), - ]) - ->add('accepted_loctype', EntityType::class, [ - 'class' => GeographicalUnit::class, - 'choice_label' => function (GeographicalUnit $u) { - return $u->getUnitName(); - }, - 'multiple' => true, - 'expanded' => true, - ]) - ; - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Filter by geographical unit'; - } - - /** - * @inheritDoc - */ - public function describeAction($data, $format = 'string'): array - { - return ['Filtered by geographic unit: only %date%', [ - '%date%' => $data['date']->format('d-m-Y'), - ]]; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $where = $qb->getDQLPart('where'); @@ -89,11 +55,36 @@ class GeographicalUnitStatFilter implements FilterInterface $qb->setParameter('loctype', $data['accepted_loctype']); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('date', ChillDateType::class, [ + 'data' => new DateTime(), + ]) + ->add('accepted_loctype', EntityType::class, [ + 'class' => GeographicalUnit::class, + 'choice_label' => static function (GeographicalUnit $u) { + return $u->getUnitName(); + }, + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + return ['Filtered by geographic unit: only %date%', [ + '%date%' => $data['date']->format('d-m-Y'), + ]]; + } + + public function getTitle(): string + { + return 'Filter by geographical unit'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/IntensityFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/IntensityFilter.php index c433c724f..22a1e2415 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/IntensityFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/IntensityFilter.php @@ -1,5 +1,14 @@ 'occasional', 'is regular' => 'regular', ]; - - private CONST DEFAULT_CHOICE = 'occasional'; - + + private const DEFAULT_CHOICE = 'occasional'; + private TranslatorInterface $translator; - + public function __construct(TranslatorInterface $translator) { $this->translator = $translator; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_intensities', ChoiceType::class, [ - 'choices' => self::CHOICES, - 'multiple' => false, - 'expanded' => true, - 'empty_data' => self::DEFAULT_CHOICE, - 'data' => self::DEFAULT_CHOICE, - ]); - } - - public function getTitle(): string - { - return 'Filter by intensity'; - } - - public function describeAction($data, $format = 'string'): array - { - foreach (self::CHOICES as $k => $v) { - if ($v === $data['accepted_intensities']) { - $choice = $k; - } - } - - return [ - 'Filtered by intensity: only %intensity%', [ - '%intensity%' => $this->translator->trans($choice) - ] - ]; - } - public function addRole() { return null; @@ -82,4 +60,34 @@ class IntensityFilter implements FilterInterface return Declarations::ACP_TYPE; } -} \ No newline at end of file + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_intensities', ChoiceType::class, [ + 'choices' => self::CHOICES, + 'multiple' => false, + 'expanded' => true, + 'empty_data' => self::DEFAULT_CHOICE, + 'data' => self::DEFAULT_CHOICE, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + foreach (self::CHOICES as $k => $v) { + if ($v === $data['accepted_intensities']) { + $choice = $k; + } + } + + return [ + 'Filtered by intensity: only %intensity%', [ + '%intensity%' => $this->translator->trans($choice), + ], + ]; + } + + public function getTitle(): string + { + return 'Filter by intensity'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php index 400ab7a14..205707831 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php @@ -1,64 +1,32 @@ add('date_from', ChillDateType::class, [ - 'data' => new DateTime(), - ]) - ->add('date_to', ChillDateType::class, [ - 'data' => new DateTime(), - ]) - ; - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Filter by opened between dates'; - } - - /** - * @inheritDoc - */ - public function describeAction($data, $format = 'string'): array - { - return ['Filtered by opening dates: between %datefrom% and %dateto%', [ - '%datefrom%' => $data['date_from']->format('d-m-Y'), - '%dateto%' => $data['date_to']->format('d-m-Y'), - ]]; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $where = $qb->getDQLPart('where'); @@ -79,11 +47,32 @@ class OpenBetweenDatesFilter implements FilterInterface $qb->setParameter('dateto', $data['date_to'], Types::DATE_MUTABLE); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('date_from', ChillDateType::class, [ + 'data' => new DateTime(), + ]) + ->add('date_to', ChillDateType::class, [ + 'data' => new DateTime(), + ]); + } + + public function describeAction($data, $format = 'string'): array + { + return ['Filtered by opening dates: between %datefrom% and %dateto%', [ + '%datefrom%' => $data['date_from']->format('d-m-Y'), + '%dateto%' => $data['date_to']->format('d-m-Y'), + ]]; + } + + public function getTitle(): string + { + return 'Filter by opened between dates'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OriginFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OriginFilter.php index 3cda93577..a67a0157c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OriginFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OriginFilter.php @@ -1,5 +1,14 @@ translatableStringHelper = $translatableStringHelper; } - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_origins', EntityType::class, [ - 'class' => Origin::class, - 'choice_label' => function (Origin $o) { - return $this->translatableStringHelper->localize($o->getLabel()); - }, - 'multiple' => true, - 'expanded' => true - ]); - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Filter by origin'; - } - - /** - * @inheritDoc - */ - public function describeAction($data, $format = 'string'): array - { - $origins = []; - - foreach ($data['accepted_origins'] as $v) { - $origins[] = $this->translatableStringHelper->localize($v->getLabel()); - } - - return ['Filtered by origins: only %origins%', [ - '%origins%' => implode(', ou ', $origins) - ]]; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $where = $qb->getDQLPart('where'); @@ -90,11 +50,38 @@ class OriginFilter implements FilterInterface $qb->setParameter('origin', $data['accepted_origins']); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_origins', EntityType::class, [ + 'class' => Origin::class, + 'choice_label' => function (Origin $o) { + return $this->translatableStringHelper->localize($o->getLabel()); + }, + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $origins = []; + + foreach ($data['accepted_origins'] as $v) { + $origins[] = $this->translatableStringHelper->localize($v->getLabel()); + } + + return ['Filtered by origins: only %origins%', [ + '%origins%' => implode(', ou ', $origins), + ]]; + } + + public function getTitle(): string + { + return 'Filter by origin'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ReferrerFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ReferrerFilter.php index 89b306678..94bf7a74c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ReferrerFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ReferrerFilter.php @@ -1,5 +1,14 @@ userRender = $userRender; } - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_referrers', EntityType::class, [ - 'class' => User::class, - 'choice_label' => function (User $u) { - return $this->userRender->renderString($u, []); - }, - 'multiple' => true, - 'expanded' => true - ]); - - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Filter by referrers'; - } - - /** - * @inheritDoc - */ - public function describeAction($data, $format = 'string'): array - { - $users = []; - - foreach ($data['accepted_referrers'] as $r) { - $users[] = $r; - } - - return [ - 'Filtered by referrer: only %referrers%', [ - '%referrers' => implode(", ou ", $users) - ]]; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $where = $qb->getDQLPart('where'); @@ -88,11 +49,39 @@ class ReferrerFilter implements FilterInterface $qb->setParameter('referrers', $data['accepted_referrers']); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_referrers', EntityType::class, [ + 'class' => User::class, + 'choice_label' => function (User $u) { + return $this->userRender->renderString($u, []); + }, + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $users = []; + + foreach ($data['accepted_referrers'] as $r) { + $users[] = $r; + } + + return [ + 'Filtered by referrer: only %referrers%', [ + '%referrers' => implode(', ou ', $users), + ], ]; + } + + public function getTitle(): string + { + return 'Filter by referrers'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/RequestorFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/RequestorFilter.php index 180775f01..3b9ed950c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/RequestorFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/RequestorFilter.php @@ -1,5 +1,14 @@ 'participation', @@ -21,12 +32,10 @@ class RequestorFilter implements FilterInterface 'no requestor' => 'no_requestor', ]; - private const DEFAULT_CHOICE = 'participation'; + protected EntityManagerInterface $em; protected TranslatorInterface $translator; - protected EntityManagerInterface $em; - public function __construct( TranslatorInterface $translator, EntityManagerInterface $em @@ -35,75 +44,35 @@ class RequestorFilter implements FilterInterface $this->em = $em; } - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_choices', ChoiceType::class, [ - 'choices' => self::REQUESTOR_CHOICES, - 'multiple' => false, - 'expanded' => true, - 'empty_data' => self::DEFAULT_CHOICE, - 'data' => self::DEFAULT_CHOICE, - ]); - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Filter by requestor'; - } - - /** - * @inheritDoc - */ - public function describeAction($data, $format = 'string'): array - { - $choice = array_flip(self::REQUESTOR_CHOICES)[$data['accepted_choices']]; - - return ['Filtered by requestor: only %choice%', [ - '%choice%' => $this->translator->trans($choice) - ]]; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $where = $qb->getDQLPart('where'); switch ($data['accepted_choices']) { case 'participation': - $qb->join('acp.participations', 'part'); $clause = $qb->expr()->andX( $qb->expr()->isNotNull('acp.requestorPerson'), $qb->expr()->eq('acp.requestorPerson', 'part.person') ); + break; case 'other_person': - $expr = $this->em->getExpressionBuilder(); - $qb->join('acp.participations','part'); + $qb->join('acp.participations', 'part'); $clause = $expr->andX( $expr->isNotNull('acp.requestorPerson'), - $expr->notIn('acp.requestorPerson', + $expr->notIn( + 'acp.requestorPerson', // subquery $this->em->createQueryBuilder() @@ -112,26 +81,26 @@ class RequestorFilter implements FilterInterface ->join('acp2.participations', 'part2') ->where($expr->eq('acp2.requestorPerson', 'part2.person')) ->getDQL() - ) ); + break; case 'thirdparty': - $clause = $qb->expr()->isNotNull('acp.requestorThirdParty'); + break; case 'no_requestor': - $clause = $qb->expr()->andX( $qb->expr()->isNull('acp.requestorPerson'), $qb->expr()->isNull('acp.requestorThirdParty') ); + break; default: - throw new \Exception('Uncaught choice exception'); + throw new Exception('Uncaught choice exception'); } if ($where instanceof Andx) { @@ -143,11 +112,33 @@ class RequestorFilter implements FilterInterface $qb->add('where', $where); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_choices', ChoiceType::class, [ + 'choices' => self::REQUESTOR_CHOICES, + 'multiple' => false, + 'expanded' => true, + 'empty_data' => self::DEFAULT_CHOICE, + 'data' => self::DEFAULT_CHOICE, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $choice = array_flip(self::REQUESTOR_CHOICES)[$data['accepted_choices']]; + + return ['Filtered by requestor: only %choice%', [ + '%choice%' => $this->translator->trans($choice), + ]]; + } + + public function getTitle(): string + { + return 'Filter by requestor'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialActionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialActionFilter.php index d96d0ca44..979793def 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialActionFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialActionFilter.php @@ -1,5 +1,14 @@ actionRender = $actionRender; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_socialactions', EntityType::class, [ - 'class' => SocialAction::class, - 'choice_label' => function (SocialAction $sa) { - return $this->actionRender->renderString($sa, []); - }, - 'multiple' => true, - 'expanded' => true, - ]); - } - - public function getTitle(): string - { - return 'Filter by socialaction'; - } - - public function describeAction($data, $format = 'string'): array - { - $socialactions = []; - - foreach ($data['accepted_socialactions'] as $sa) { - $socialactions[] = $this->actionRender->renderString($sa, []); - } - - return ['Filtered by socialactions: only %socialactions%', [ - '%socialactions%' => implode(", ou ", $socialactions) - ]]; - } - public function addRole() { return null; @@ -69,7 +43,7 @@ class SocialActionFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - if (!in_array('acpw', $qb->getAllAliases())) { + if (!in_array('acpw', $qb->getAllAliases(), true)) { $qb->join('acp.works', 'acpw'); } $qb->join('acpw.socialAction', 'sa'); @@ -91,4 +65,34 @@ class SocialActionFilter implements FilterInterface { return Declarations::ACP_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_socialactions', EntityType::class, [ + 'class' => SocialAction::class, + 'choice_label' => function (SocialAction $sa) { + return $this->actionRender->renderString($sa, []); + }, + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $socialactions = []; + + foreach ($data['accepted_socialactions'] as $sa) { + $socialactions[] = $this->actionRender->renderString($sa, []); + } + + return ['Filtered by socialactions: only %socialactions%', [ + '%socialactions%' => implode(', ou ', $socialactions), + ]]; + } + + public function getTitle(): string + { + return 'Filter by socialaction'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php index c2f9b1a06..f707ab9e2 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php @@ -1,5 +1,14 @@ socialIssueRender = $socialIssueRender; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_socialissues', EntityType::class, [ - 'class' => SocialIssue::class, - 'choice_label' => function ($socialIssue) { - return $this->socialIssueRender->renderString($socialIssue, []); - }, - 'multiple' => true, - 'expanded' => true, - ]); - } - - public function getTitle() - { - return 'Filter by social issue'; - } - - public function describeAction($data, $format = 'string') - { - $issues = []; - - $socialissues = $this->addParentIssues($data['accepted_socialissues']); - - foreach ($socialissues as $i) { - if ('null' === $i) { - $issues[] = $this->translator->trans('Not given'); - } else { - $issues[] = $this->socialIssueRender->renderString($i, []); - } - } - - return [ - 'Filtered by socialissues: only %socialissues%', [ - '%socialissues%' => implode(', ou ', $issues) - ]]; - } - public function addRole() { return null; @@ -96,49 +62,87 @@ class SocialIssueFilter implements FilterInterface } $qb->add('where', $where); - $qb->setParameter('socialissues', + $qb->setParameter( + 'socialissues', $this->addParentIssues($data['accepted_socialissues']) ); } - /** - * "Le filtre retiendra les parcours qui comportent cette problématique, - * ou une problématique parente à celles choisies." - * - * Add parent of each socialissue selected, and remove duplicates - * - * @param $accepted_issues - * @return array - */ - private function addParentIssues($accepted_issues): array - { - $array = []; - foreach ($accepted_issues as $i) - { - /** @var SocialIssue $i */ - if ($i->hasParent()) { - $array[] = $i->getParent(); - } - $array[] = $i; - } - return $this->removeDuplicate($array); - } - - private function removeDuplicate(array $array): array - { - $ids = array_map(function ($item) { - return $item->getId(); - }, $array); - - $unique_ids = array_unique($ids); - - return array_values( - array_intersect_key($array, $unique_ids)); - } - public function applyOn() { return Declarations::ACP_TYPE; } -} \ No newline at end of file + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_socialissues', EntityType::class, [ + 'class' => SocialIssue::class, + 'choice_label' => function ($socialIssue) { + return $this->socialIssueRender->renderString($socialIssue, []); + }, + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string') + { + $issues = []; + + $socialissues = $this->addParentIssues($data['accepted_socialissues']); + + foreach ($socialissues as $i) { + if ('null' === $i) { + $issues[] = $this->translator->trans('Not given'); + } else { + $issues[] = $this->socialIssueRender->renderString($i, []); + } + } + + return [ + 'Filtered by socialissues: only %socialissues%', [ + '%socialissues%' => implode(', ou ', $issues), + ], ]; + } + + public function getTitle() + { + return 'Filter by social issue'; + } + + /** + * "Le filtre retiendra les parcours qui comportent cette problématique, + * ou une problématique parente à celles choisies.". + * + * Add parent of each socialissue selected, and remove duplicates + * + * @param $accepted_issues + */ + private function addParentIssues($accepted_issues): array + { + $array = []; + + foreach ($accepted_issues as $i) { + /** @var SocialIssue $i */ + if ($i->hasParent()) { + $array[] = $i->getParent(); + } + $array[] = $i; + } + + return $this->removeDuplicate($array); + } + + private function removeDuplicate(array $array): array + { + $ids = array_map(static function ($item) { + return $item->getId(); + }, $array); + + $unique_ids = array_unique($ids); + + return array_values( + array_intersect_key($array, $unique_ids) + ); + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/StepFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/StepFilter.php index 1985031de..a7d218acc 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/StepFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/StepFilter.php @@ -1,21 +1,28 @@ AccompanyingPeriod::STEP_DRAFT, @@ -23,8 +30,6 @@ class StepFilter implements FilterInterface 'Closed' => AccompanyingPeriod::STEP_CLOSED, ]; - private const DEFAULT_CHOICE = AccompanyingPeriod::STEP_CONFIRMED; - /** * @var TranslatorInterface */ @@ -35,31 +40,6 @@ class StepFilter implements FilterInterface $this->translator = $translator; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_steps', ChoiceType::class, [ - 'choices' => self::STEPS, - 'multiple' => false, - 'expanded' => true, - 'empty_data' => self::DEFAULT_CHOICE, - 'data' => self::DEFAULT_CHOICE, - ]); - } - - public function getTitle() - { - return 'Filter by step'; - } - - public function describeAction($data, $format = 'string') - { - $step = array_flip(self::STEPS)[$data['accepted_steps']]; - - return ["Filtered by steps: only %step%", [ - '%step%' => $this->translator->trans($step) - ]]; - } - public function addRole() { return null; @@ -85,4 +65,28 @@ class StepFilter implements FilterInterface return Declarations::ACP_TYPE; } -} \ No newline at end of file + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_steps', ChoiceType::class, [ + 'choices' => self::STEPS, + 'multiple' => false, + 'expanded' => true, + 'empty_data' => self::DEFAULT_CHOICE, + 'data' => self::DEFAULT_CHOICE, + ]); + } + + public function describeAction($data, $format = 'string') + { + $step = array_flip(self::STEPS)[$data['accepted_steps']]; + + return ['Filtered by steps: only %step%', [ + '%step%' => $this->translator->trans($step), + ]]; + } + + public function getTitle() + { + return 'Filter by step'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/EvaluationTypeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/EvaluationTypeFilter.php index 6df5e38bf..d467679de 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/EvaluationTypeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/EvaluationTypeFilter.php @@ -1,5 +1,14 @@ 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 { @@ -86,11 +50,38 @@ final class EvaluationTypeFilter implements FilterInterface $qb->setParameter('evaluationtype', $data['accepted_evaluationtype']); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::EVAL_TYPE; } -} \ No newline at end of file + + 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, + ]); + } + + 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), + ]]; + } + + public function getTitle(): string + { + return 'Filter by evaluation type'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/MaxDateFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/MaxDateFilter.php index c69e15d9e..0524e3bba 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/MaxDateFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/MaxDateFilter.php @@ -1,5 +1,14 @@ 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) { + if (true === $data['maxdate']) { $clause = $qb->expr()->isNotNull('eval.maxDate'); } else { $clause = $qb->expr()->isNull('eval.maxDate'); @@ -90,11 +57,35 @@ class MaxDateFilter implements FilterInterface $qb->add('where', $where); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::EVAL_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('maxdate', ChoiceType::class, [ + 'choices' => self::MAXDATE_CHOICES, + 'multiple' => false, + 'expanded' => true, + ]); + } + + 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), + ]]; + } + + public function getTitle(): string + { + return 'Filter by maxdate'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php index 78aba8bf7..95170cb10 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php @@ -1,5 +1,14 @@ 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) { - if (!in_array('composition', $qb->getAllAliases())) { + if (!in_array('composition', $qb->getAllAliases(), true)) { $qb->join('household.compositions', 'composition'); } @@ -113,11 +69,47 @@ class CompositionFilter implements FilterInterface $qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::HOUSEHOLD_TYPE; } -} \ No newline at end of file + + 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'), + ]); + } + + 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'), + ]]; + } + + public function getTitle(): string + { + return 'Filter by composition'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php index 47fb37214..e07d03e04 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php @@ -6,7 +6,7 @@ * For the full copyright and license information, please view * the LICENSE file that was distributed with this source code. */ - + declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\PersonFilters; @@ -38,10 +38,12 @@ class AgeFilter implements ExportElementValidatedInterface, FilterInterface $calc = $data['date_calc']; $clause = $qb->expr()->andX( - $qb->expr()->gte('DATE_DIFF(:calc_date, person.birthdate)/365', + $qb->expr()->gte( + 'DATE_DIFF(:calc_date, person.birthdate)/365', ':min_age' ), - $qb->expr()->lte('DATE_DIFF(:calc_date, person.birthdate)/365', + $qb->expr()->lte( + 'DATE_DIFF(:calc_date, person.birthdate)/365', ':max_age' ) ); diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/BirthdateFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/BirthdateFilter.php index e26e3697d..2ab482039 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/BirthdateFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/BirthdateFilter.php @@ -17,7 +17,6 @@ use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Export\Declarations; use DateTime; use Doctrine\ORM\Query\Expr; -use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Validator\Context\ExecutionContextInterface; class BirthdateFilter implements ExportElementValidatedInterface, FilterInterface @@ -56,12 +55,12 @@ class BirthdateFilter implements ExportElementValidatedInterface, FilterInterfac { $builder->add('date_from', ChillDateType::class, [ 'label' => 'Born after this date', - 'data' => new DateTime() + 'data' => new DateTime(), ]); $builder->add('date_to', ChillDateType::class, [ 'label' => 'Born before this date', - 'data' => new DateTime() + 'data' => new DateTime(), ]); } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/DeadOrAliveFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/DeadOrAliveFilter.php index aaef76436..457378731 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/DeadOrAliveFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/DeadOrAliveFilter.php @@ -34,15 +34,15 @@ class DeadOrAliveFilter implements FilterInterface $personState = $data['person_state']; $calc = $data['date_calc']; - if ($personState == 'alive') { + if ('alive' === $personState) { $clause = $qb->expr()->orX( - $qb->expr()->andX( - $qb->expr()->isNull('person.deathdate'), - $qb->expr()->lte( + $qb->expr()->andX( + $qb->expr()->isNull('person.deathdate'), + $qb->expr()->lte( 'person.birthdate', ':date_calc' ) - ), + ), $qb->expr()->andX( $qb->expr()->isNotNull('person.deathdate'), $qb->expr()->gt( diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/FamilySituationFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/FamilySituationFilter.php index e1a6c7529..895f83e9d 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/FamilySituationFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/FamilySituationFilter.php @@ -12,7 +12,6 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\PersonFilters; use Chill\MainBundle\Export\FilterInterface; -use Chill\PersonBundle\Entity\Household\HouseholdCompositionType; use Chill\PersonBundle\Export\Declarations; use DateTime; use Symfony\Component\Form\Extension\Core\Type\DateType; @@ -27,7 +26,6 @@ class FamilySituationFilter implements FilterInterface public function alterQuery(\Doctrine\ORM\QueryBuilder $qb, $data) { - } public function applyOn() diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php index d8c0e1343..b2eef5285 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php @@ -36,24 +36,6 @@ class ResidentialAddressAtThirdpartyFilter implements FilterInterface return null; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('thirdparty_cat', EntityType::class, [ - 'class' => ThirdPartyCategory::class, - 'label' => 'Category', - 'choice_label' => function (ThirdPartyCategory $tpc) { - return $this->translatableStringHelper->localize($tpc->getName()); - }, - 'multiple' => true, - 'expanded' => true - ]); - - $builder->add('date_calc', ChillDateType::class, [ - 'label' => 'Date during which residential address was valid', - 'data' => new DateTime('now'), - ]); - } - public function alterQuery(QueryBuilder $qb, $data) { $qb->resetDQLPart('from'); @@ -63,7 +45,7 @@ class ResidentialAddressAtThirdpartyFilter implements FilterInterface $qb->join('person.center', 'center'); $qb->join('ra.hostThirdParty', 't'); $qb->join('t.categories', 'tc'); - + $where = $qb->getDQLPart('where'); $clause = $qb->expr()->andX( $qb->expr()->isNotNull('ra.hostThirdParty'), @@ -87,17 +69,34 @@ class ResidentialAddressAtThirdpartyFilter implements FilterInterface return Declarations::PERSON_TYPE; } + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('thirdparty_cat', EntityType::class, [ + 'class' => ThirdPartyCategory::class, + 'label' => 'Category', + 'choice_label' => function (ThirdPartyCategory $tpc) { + return $this->translatableStringHelper->localize($tpc->getName()); + }, + 'multiple' => true, + 'expanded' => true, + ]); + + $builder->add('date_calc', ChillDateType::class, [ + 'label' => 'Date during which residential address was valid', + 'data' => new DateTime('now'), + ]); + } public function describeAction($data, $format = 'string') { return ['Filtered by person\'s who have a residential address located at a thirdparty of type %thirdparty_type% and valid on %date_calc%', [ '%thirdparty_type%' => $this->translatableStringHelper->localize($data['thirdparty_cat'][0]->getName()), '%date_calc%' => $data['date_calc']->format('d-m-Y'), - ],]; + ]]; } public function getTitle() { return 'Filtered by person\'s who have a residential address located at a thirdparty of type'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php index 835185db5..38825e022 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php @@ -13,7 +13,6 @@ namespace Chill\PersonBundle\Export\Filter\PersonFilters; use Chill\MainBundle\Export\FilterInterface; use Chill\PersonBundle\Export\Declarations; -use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; @@ -31,7 +30,7 @@ class ResidentialAddressAtUserFilter implements FilterInterface $qb->join('ra.person', 'person'); $qb->join('person.center', 'center'); - + $where = $qb->getDQLPart('where'); $clause = $qb->expr()->isNotNull('ra.hostPerson'); @@ -42,7 +41,6 @@ class ResidentialAddressAtUserFilter implements FilterInterface } $qb->add('where', $where); - } public function applyOn() @@ -64,4 +62,4 @@ class ResidentialAddressAtUserFilter implements FilterInterface { return 'Filtered by person\'s who have a residential address located at another user'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/JobFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/JobFilter.php index 80efd03e3..8986d4676 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/JobFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/JobFilter.php @@ -19,12 +19,10 @@ use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Security\Core\Security; use Symfony\Contracts\Translation\TranslatorInterface; class JobFilter implements FilterInterface { - protected TranslatorInterface $translator; private TranslatableStringHelper $translatableStringHelper; @@ -37,34 +35,6 @@ class JobFilter implements FilterInterface $this->translatableStringHelper = $translatableStringHelper; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('job', EntityType::class, [ - 'class' => UserJob::class, - 'choice_label' => function (UserJob $j) { - return $this->translatableStringHelper->localize( - $j->getLabel() - ); - }, - 'multiple' => true, - 'expanded' => true - ]); - } - - public function describeAction($data, $format = 'string') - { - $userjobs = []; - - foreach ($data['job'] as $j) { - $userjobs[] = $this->translatableStringHelper->localize( - $j->getLabel()); - } - - return ['Filtered by treating agent job: only %jobs%', [ - '%jobs%' => implode(', ou ', $userjobs) - ]]; - } - public function addRole() { return null; @@ -92,9 +62,37 @@ class JobFilter implements FilterInterface return Declarations::SOCIAL_WORK_ACTION_TYPE; } + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('job', EntityType::class, [ + 'class' => UserJob::class, + 'choice_label' => function (UserJob $j) { + return $this->translatableStringHelper->localize( + $j->getLabel() + ); + }, + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string') + { + $userjobs = []; + + foreach ($data['job'] as $j) { + $userjobs[] = $this->translatableStringHelper->localize( + $j->getLabel() + ); + } + + return ['Filtered by treating agent job: only %jobs%', [ + '%jobs%' => implode(', ou ', $userjobs), + ]]; + } public function getTitle(): string { return 'Filter by treating agent job'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ReferrerFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ReferrerFilter.php index 1ca1d39d2..efe193a6e 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ReferrerFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ReferrerFilter.php @@ -1,5 +1,14 @@ userRender = $userRender; } - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('accepted_agents', EntityType::class, [ - 'class' => User::class, - 'choice_label' => function (User $u) { - return $this->userRender->renderString($u, []); - }, - 'multiple' => true, - 'expanded' => true - ]); - - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Filter by treating agent'; - } - - /** - * @inheritDoc - */ - public function describeAction($data, $format = 'string'): array - { - $users = []; - - foreach ($data['accepted_agents'] as $r) { - $users[] = $r; - } - - return [ - 'Filtered by treating agent: only %agents%', [ - '%agents' => implode(", ou ", $users) - ]]; - } - - /** - * @inheritDoc - */ public function addRole() { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $qb->join('acpw.referrers', 'u'); @@ -90,11 +51,39 @@ class ReferrerFilter implements FilterInterface $qb->setParameter('agents', $data['accepted_agents']); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::SOCIAL_WORK_ACTION_TYPE; } -} \ No newline at end of file + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('accepted_agents', EntityType::class, [ + 'class' => User::class, + 'choice_label' => function (User $u) { + return $this->userRender->renderString($u, []); + }, + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $users = []; + + foreach ($data['accepted_agents'] as $r) { + $users[] = $r; + } + + return [ + 'Filtered by treating agent: only %agents%', [ + '%agents' => implode(', ou ', $users), + ], ]; + } + + public function getTitle(): string + { + return 'Filter by treating agent'; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ScopeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ScopeFilter.php index 6588efc18..b828a6c47 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ScopeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ScopeFilter.php @@ -23,7 +23,6 @@ use Symfony\Contracts\Translation\TranslatorInterface; class ScopeFilter implements FilterInterface { - protected TranslatorInterface $translator; private TranslatableStringHelper $translatableStringHelper; @@ -36,34 +35,6 @@ class ScopeFilter implements FilterInterface $this->translatableStringHelper = $translatableStringHelper; } - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('scope', EntityType::class, [ - 'class' => Scope::class, - 'choice_label' => function (Scope $s) { - return $this->translatableStringHelper->localize( - $s->getName() - ); - }, - 'multiple' => true, - 'expanded' => true - ]); - } - - public function describeAction($data, $format = 'string') - { - $scopes = []; - - foreach ($data['scope'] as $s) { - $scopes[] = $this->translatableStringHelper->localize( - $s->getName()); - } - - return ['Filtered by treating agent scope: only %scopes%', [ - '%scopes%' => implode(', ou ', $scopes) - ]]; - } - public function addRole() { return null; @@ -91,9 +62,37 @@ class ScopeFilter implements FilterInterface return Declarations::SOCIAL_WORK_ACTION_TYPE; } + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('scope', EntityType::class, [ + 'class' => Scope::class, + 'choice_label' => function (Scope $s) { + return $this->translatableStringHelper->localize( + $s->getName() + ); + }, + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string') + { + $scopes = []; + + foreach ($data['scope'] as $s) { + $scopes[] = $this->translatableStringHelper->localize( + $s->getName() + ); + } + + return ['Filtered by treating agent scope: only %scopes%', [ + '%scopes%' => implode(', ou ', $scopes), + ]]; + } public function getTitle() { return 'Filter by treating agent scope'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php index efd8c93af..348e1e9b8 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php @@ -1,24 +1,27 @@ socialActionRender = $socialActionRender; $this->translatableStringHelper = $translatableStringHelper; $this->socialActionRepository = $socialActionRepository; } - public function buildForm(FormBuilderInterface $builder) - { - $socialActions = $this->socialActionRepository->findAll(); - - $builder->add('actionType', ChoiceType::class, [ - 'choices' => $socialActions, - 'choice_label' => function (SocialAction $sa) { - return $this->socialActionRender->renderString($sa, []); - }, - 'multiple' => true, - 'expanded' => true - ]); - - $refreshGoals = function (FormInterface $form, SocialAction $actionType = null) { - - $goals = null === $actionType ? [] : $actionType->getGoals(); - - $form->add('goal', ChoiceType::class, [ - 'placeholder' => '', - 'choices' => $goals, - 'choice_label' => function (Goal $g) { - return $this->translatableStringHelper->localize($g->getTitle()); - }, - ]); - }; - - $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($refreshGoals) { - $data = $event->getData(); - dump($data); - - $refreshGoals($event->getForm(), $data); - }); - -/* $builder->get('actionType')->addEventListener( - FormEvents::POST_SUBMIT, - function (FormEvent $event) use ($refreshGoals) { - $actionType = $event->getForm()->getData(); - dump($actionType); - $refreshGoals($event->getForm()->getParent(), $actionType); - } - );*/ - } - - public function getTitle(): string - { - return 'Filter by type of action, objectives and results'; - } - - public function describeAction($data, $format = 'string'): array - { - $actionTypes = []; - $objectives = []; - $results = []; - - foreach ($data['actionType'] as $at) { - $actionTypes[] = $at->getTitle(); - } - foreach ($data['objectives'] as $o) { - $objectives[] = $o->getTitle(); - } - foreach ($data['results'] as $r) { - $results[] = $r->getTitle(); - } - - return ['Filtered by referrers: only %actionTypes%', [ - '%actionTypes%' => implode(', ou ', $actionTypes) - ]]; - } - public function addRole() { return null; @@ -136,4 +68,74 @@ class SocialWorkTypeFilter implements FilterInterface { return Declarations::SOCIAL_WORK_ACTION_TYPE; } + + public function buildForm(FormBuilderInterface $builder) + { + $socialActions = $this->socialActionRepository->findAll(); + + $builder->add('actionType', ChoiceType::class, [ + 'choices' => $socialActions, + 'choice_label' => function (SocialAction $sa) { + return $this->socialActionRender->renderString($sa, []); + }, + 'multiple' => true, + 'expanded' => true, + ]); + + $refreshGoals = function (FormInterface $form, ?SocialAction $actionType = null) { + $goals = null === $actionType ? [] : $actionType->getGoals(); + + $form->add('goal', ChoiceType::class, [ + 'placeholder' => '', + 'choices' => $goals, + 'choice_label' => function (Goal $g) { + return $this->translatableStringHelper->localize($g->getTitle()); + }, + ]); + }; + + $builder->addEventListener(FormEvents::PRE_SUBMIT, static function (FormEvent $event) use ($refreshGoals) { + $data = $event->getData(); + dump($data); + + $refreshGoals($event->getForm(), $data); + }); + + /* $builder->get('actionType')->addEventListener( + FormEvents::POST_SUBMIT, + function (FormEvent $event) use ($refreshGoals) { + $actionType = $event->getForm()->getData(); + dump($actionType); + $refreshGoals($event->getForm()->getParent(), $actionType); + } + );*/ + } + + public function describeAction($data, $format = 'string'): array + { + $actionTypes = []; + $objectives = []; + $results = []; + + foreach ($data['actionType'] as $at) { + $actionTypes[] = $at->getTitle(); + } + + foreach ($data['objectives'] as $o) { + $objectives[] = $o->getTitle(); + } + + foreach ($data['results'] as $r) { + $results[] = $r->getTitle(); + } + + return ['Filtered by referrers: only %actionTypes%', [ + '%actionTypes%' => implode(', ou ', $actionTypes), + ]]; + } + + public function getTitle(): string + { + return 'Filter by type of action, objectives and results'; + } } diff --git a/src/Bundle/ChillPersonBundle/Form/HouseholdCompositionTypeType.php b/src/Bundle/ChillPersonBundle/Form/HouseholdCompositionTypeType.php index f5d26da7f..ad95c9f7a 100644 --- a/src/Bundle/ChillPersonBundle/Form/HouseholdCompositionTypeType.php +++ b/src/Bundle/ChillPersonBundle/Form/HouseholdCompositionTypeType.php @@ -15,7 +15,6 @@ use Chill\MainBundle\Form\Type\TranslatableStringFormType; use Chill\PersonBundle\Entity\Household\HouseholdCompositionType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; -use Symfony\Component\Form\Extension\Core\Type\NumberType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -36,4 +35,4 @@ class HouseholdCompositionTypeType extends AbstractType $resolver ->setDefault('class', HouseholdCompositionType::class); } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php index c2826485e..8a9fd42cd 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php @@ -35,11 +35,6 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository $this->repository = $entityManager->getRepository(AccompanyingPeriodWork::class); } - public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder - { - return $this->repository->createQueryBuilder($alias, $indexBy); - } - public function countByAccompanyingPeriod(AccompanyingPeriod $period): int { return $this->repository->countByAccompanyingPeriod($period); @@ -61,6 +56,11 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository ->select('count(w)')->getQuery()->getSingleScalarResult(); } + public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder + { + return $this->repository->createQueryBuilder($alias, $indexBy); + } + public function find($id): ?AccompanyingPeriodWork { return $this->repository->find($id); diff --git a/src/Bundle/ChillPersonBundle/Repository/PersonRepository.php b/src/Bundle/ChillPersonBundle/Repository/PersonRepository.php index 309e5f086..144cea507 100644 --- a/src/Bundle/ChillPersonBundle/Repository/PersonRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/PersonRepository.php @@ -31,11 +31,6 @@ final class PersonRepository implements ObjectRepository $this->repository = $entityManager->getRepository(Person::class); } - public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder - { - return $this->repository->createQueryBuilder($alias, $indexBy); - } - /** * @param $centers * @@ -56,6 +51,11 @@ final class PersonRepository implements ObjectRepository return $qb->getQuery()->getSingleScalarResult(); } + public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder + { + return $this->repository->createQueryBuilder($alias, $indexBy); + } + public function find($id, $lockMode = null, $lockVersion = null): ?Person { return $this->repository->find($id, $lockMode, $lockVersion); diff --git a/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php b/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php index 2144db5a3..8da2036d7 100644 --- a/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php +++ b/src/Bundle/ChillPersonBundle/Security/Authorization/AccompanyingPeriodVoter.php @@ -41,9 +41,9 @@ class AccompanyingPeriodVoter extends AbstractChillVoter implements ProvideRoleH ]; /** - * Give the ability to see statistics + * Give the ability to see all confidential courses. */ - public const STATS = 'CHILL_PERSON_ACCOMPANYING_PERIOD_STATS'; + public const CONFIDENTIAL_CRUD = 'CHILL_PERSON_ACCOMPANYING_PERIOD_CRUD_CONFIDENTIAL'; public const CREATE = 'CHILL_PERSON_ACCOMPANYING_PERIOD_CREATE'; @@ -88,6 +88,11 @@ class AccompanyingPeriodVoter extends AbstractChillVoter implements ProvideRoleH */ public const SEE_DETAILS = 'CHILL_PERSON_ACCOMPANYING_PERIOD_SEE_DETAILS'; + /** + * Give the ability to see statistics. + */ + public const STATS = 'CHILL_PERSON_ACCOMPANYING_PERIOD_STATS'; + /** * Right to toggle confidentiality. */ @@ -95,11 +100,6 @@ class AccompanyingPeriodVoter extends AbstractChillVoter implements ProvideRoleH public const TOGGLE_CONFIDENTIAL_ALL = 'CHILL_PERSON_ACCOMPANYING_PERIOD_TOGGLE_CONFIDENTIAL_ALL'; - /** - * Give the ability to see all confidential courses. - */ - public const CONFIDENTIAL_CRUD = 'CHILL_PERSON_ACCOMPANYING_PERIOD_CRUD_CONFIDENTIAL'; - /** * Right to toggle urgency of parcours. */ @@ -218,7 +218,6 @@ class AccompanyingPeriodVoter extends AbstractChillVoter implements ProvideRoleH return $token->getUser() === $subject->getUser(); } - } return $this->voterHelper->voteOnAttribute($attribute, $subject, $token); diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Export/CountAccompanyingCourseTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Export/CountAccompanyingCourseTest.php index d6fa9a843..6ce9827f3 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Export/CountAccompanyingCourseTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Export/CountAccompanyingCourseTest.php @@ -1,11 +1,24 @@ export = self::$container->get('chill.person.export.count_accompanyingcourse'); } - /** - * @inheritDoc - */ public function getExport() { return $this->export; } - /** - * @inheritDoc - */ public function getFormData(): array { return [[]]; } - /** - * @inheritDoc - */ public function getModifiersCombination() { return [[Declarations::ACP_TYPE]]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Export/CountSocialWorkActionsTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Export/CountSocialWorkActionsTest.php index d904f2eeb..6fd26ff74 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Export/CountSocialWorkActionsTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Export/CountSocialWorkActionsTest.php @@ -1,11 +1,23 @@ export = self::$container->get('chill.person.export.count_social_work_actions'); } - /** - * @inheritDoc - */ public function getExport() { return $this->export; } - /** - * @inheritDoc - */ public function getFormData(): array { return [[]]; } - /** - * @inheritDoc - */ public function getModifiersCombination() { return [[Declarations::SOCIAL_WORK_ACTION_TYPE]]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Export/StatAccompanyingCourseDurationTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Export/StatAccompanyingCourseDurationTest.php index 7c1420c86..543c938f2 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Export/StatAccompanyingCourseDurationTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Export/StatAccompanyingCourseDurationTest.php @@ -1,11 +1,25 @@ \DateTime::createFromFormat('Y-m-d', '2022-06-30')], + ['closingdate' => DateTime::createFromFormat('Y-m-d', '2022-06-30')], ]; } @@ -33,4 +47,4 @@ final class StatAccompanyingCourseDurationTest extends AbstractExportTest { return [[Declarations::ACP_TYPE]]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ActiveOnDateFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ActiveOnDateFilterTest.php index b66de0f28..81c132f7c 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ActiveOnDateFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ActiveOnDateFilterTest.php @@ -1,13 +1,27 @@ filter = self::$container->get('chill.person.export.filter_activeondate'); } - /** - * @inheritDoc - */ public function getFilter() { return $this->filter; } - /** - * @inheritDoc - */ public function getFormData(): array { return [ [ - 'on_date' => \DateTime::createFromFormat('Y-m-d', '2022-05-01'), + 'on_date' => DateTime::createFromFormat('Y-m-d', '2022-05-01'), ], ]; } - /** - * @inheritDoc - */ public function getQueryBuilders(): array { if (null === self::$kernel) { @@ -62,4 +67,4 @@ class ActiveOnDateFilterTest extends AbstractFilterTest ->select('acp.id'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ActiveOneDayBetweenDatesFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ActiveOneDayBetweenDatesFilterTest.php index 051db8635..ab173d0b1 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ActiveOneDayBetweenDatesFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ActiveOneDayBetweenDatesFilterTest.php @@ -1,13 +1,27 @@ filter = self::$container->get('chill.person.export.filter_activeonedaybetweendates'); } - /** - * @inheritDoc - */ public function getFilter() { return $this->filter; } - /** - * @inheritDoc - */ public function getFormData(): array { return [ [ - 'date_from' => \DateTime::createFromFormat('Y-m-d', '2022-05-01'), - 'date_to' => \DateTime::createFromFormat('Y-m-d', '2022-06-01'), + 'date_from' => DateTime::createFromFormat('Y-m-d', '2022-05-01'), + 'date_to' => DateTime::createFromFormat('Y-m-d', '2022-06-01'), ], ]; } - /** - * @inheritDoc - */ public function getQueryBuilders(): array { if (null === self::$kernel) { @@ -63,4 +68,4 @@ class ActiveOneDayBetweenDatesFilterTest extends AbstractFilterTest ->select('acp.id'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ActivityTypeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ActivityTypeFilterTest.php index 315592ee9..f59039f35 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ActivityTypeFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ActivityTypeFilterTest.php @@ -1,5 +1,14 @@ filter = self::$container->get('chill.person.export.filter_activitytype'); } - /** - * @inheritDoc - */ public function getFilter() { return $this->filter; } - /** - * @inheritDoc - */ public function getFormData(): array { - $em = self::$container->get(EntityManagerInterface::class); $array = $em->createQueryBuilder() @@ -50,16 +56,13 @@ class ActivityTypeFilterTest extends AbstractFilterTest $data = []; - foreach($array as $t) { + foreach ($array as $t) { $data[] = ['accepted_activitytypes' => $t]; } return $data; } - /** - * @inheritDoc - */ public function getQueryBuilders(): array { if (null === self::$kernel) { @@ -74,4 +77,4 @@ class ActivityTypeFilterTest extends AbstractFilterTest ->select('acp.id'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AdministrativeLocationFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AdministrativeLocationFilterTest.php index f974919aa..190729161 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AdministrativeLocationFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AdministrativeLocationFilterTest.php @@ -1,5 +1,14 @@ filter = self::$container->get('chill.person.export.filter_administrative_location'); } - /** - * @inheritDoc - */ public function getFilter() { return $this->filter; } - /** - * @inheritDoc - */ public function getFormData(): array { $em = self::$container->get(EntityManagerInterface::class); @@ -49,16 +56,13 @@ class AdministrativeLocationFilterTest extends AbstractFilterTest $data = []; - foreach($array as $l) { + foreach ($array as $l) { $data[] = ['accepted_locations' => $l]; } return $data; } - /** - * @inheritDoc - */ public function getQueryBuilders(): array { if (null === self::$kernel) { @@ -73,4 +77,4 @@ class AdministrativeLocationFilterTest extends AbstractFilterTest ->select('acp.id'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ClosingMotiveFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ClosingMotiveFilterTest.php index cbfa1c306..d9403506d 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ClosingMotiveFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ClosingMotiveFilterTest.php @@ -1,5 +1,14 @@ filter = self::$container->get('chill.person.export.filter_closingmotive'); } - /** - * @inheritDoc - */ public function getFilter() { return $this->filter; } - /** - * @inheritDoc - */ public function getFormData(): array { $em = self::$container->get(EntityManagerInterface::class); @@ -47,19 +54,15 @@ class ClosingMotiveFilterTest extends AbstractFilterTest $data = []; - foreach($array as $m) { + foreach ($array as $m) { $data[] = ['accepted_closingmotives' => $m]; } return $data; } - /** - * @inheritDoc - */ public function getQueryBuilders(): array { - if (null === self::$kernel) { self::bootKernel(); } @@ -72,4 +75,4 @@ class ClosingMotiveFilterTest extends AbstractFilterTest ->from('ChillPersonBundle:AccompanyingPeriod', 'acp'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ConfidentialFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ConfidentialFilterTest.php index 1d8573916..6229989fe 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ConfidentialFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ConfidentialFilterTest.php @@ -1,12 +1,25 @@ filter = self::$container->get('chill.person.export.filter_confidential'); } - /** - * @inheritDoc - */ public function getFilter() { return $this->filter; } - /** - * @inheritDoc - */ public function getFormData(): array { return [ ['accepted_confidentials' => true], - ['accepted_confidentials' => false] + ['accepted_confidentials' => false], ]; } - /** - * @inheritDoc - */ public function getQueryBuilders(): array { - if (null === self::$kernel) { self::bootKernel(); } @@ -60,4 +63,4 @@ class ConfidentialFilterTest extends AbstractFilterTest ->from('ChillPersonBundle:AccompanyingPeriod', 'acp'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EmergencyFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EmergencyFilterTest.php index 0533a604a..7ed43ea65 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EmergencyFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EmergencyFilterTest.php @@ -1,12 +1,25 @@ true], - ['accepted_emergencies' => false] + ['accepted_emergencies' => false], ]; } public function getQueryBuilders(): array { - if (null === self::$kernel) { self::bootKernel(); } @@ -51,4 +63,4 @@ class EmergencyFilterTest extends AbstractFilterTest ->from('ChillPersonBundle:AccompanyingPeriod', 'acp'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilterTest.php index 310fde2c9..a4e39792c 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilterTest.php @@ -1,5 +1,14 @@ filter = self::$container->get('chill.person.export.filter_evaluation'); } - /** - * @inheritDoc - */ public function getFilter() { return $this->filter; } - /** - * @inheritDoc - */ public function getFormData(): array { $em = self::$container->get(EntityManagerInterface::class); @@ -49,16 +56,13 @@ class EvaluationFilterTest extends AbstractFilterTest $data = []; - foreach($array as $e) { + foreach ($array as $e) { $data[] = ['accepted_evaluations' => $e]; } return $data; } - /** - * @inheritDoc - */ public function getQueryBuilders(): array { if (null === self::$kernel) { @@ -73,4 +77,4 @@ class EvaluationFilterTest extends AbstractFilterTest ->select('acp.id'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/GeographicalUnitStatFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/GeographicalUnitStatFilterTest.php index 8a205e9ac..a2eef9038 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/GeographicalUnitStatFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/GeographicalUnitStatFilterTest.php @@ -1,13 +1,27 @@ filter = self::$container->get('chill.person.export.filter_geographicalunitstat'); } - /** - * @inheritDoc - */ public function getFilter() { return $this->filter; } - /** - * @inheritDoc - */ public function getFormData(): array { return [ [ - 'date' => \DateTime::createFromFormat('Y-m-d', '2022-05-01'), - 'accepted_loctype' => 'center' + 'date' => DateTime::createFromFormat('Y-m-d', '2022-05-01'), + 'accepted_loctype' => 'center', ], ]; } - /** - * @inheritDoc - */ public function getQueryBuilders(): array { if (null === self::$kernel) { @@ -63,4 +68,4 @@ class GeographicalUnitStatFilterTest extends AbstractFilterTest ->select('acp.id'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/IntensityFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/IntensityFilterTest.php index c39602cf8..65d2c6880 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/IntensityFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/IntensityFilterTest.php @@ -1,12 +1,25 @@ filter = self::$container->get('chill.person.export.filter_intensity'); } - /** - * @inheritDoc - */ public function getFilter() { return $this->filter; } - /** - * @inheritDoc - */ public function getFormData(): array { return [ ['accepted_intensities' => 'occasional'], - ['accepted_intensities' => 'regular'] + ['accepted_intensities' => 'regular'], ]; } - /** - * @inheritDoc - */ public function getQueryBuilders(): array { - if (null === self::$kernel) { self::bootKernel(); } @@ -60,4 +63,4 @@ class IntensityFilterTest extends AbstractFilterTest ->from('ChillPersonBundle:AccompanyingPeriod', 'acp'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/JobFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/JobFilterTest.php index 6f7546b4b..9a9bb2498 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/JobFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/JobFilterTest.php @@ -1,12 +1,24 @@ filter = self::$container->get('chill.person.export.filter_job'); } - /** - * @inheritDoc - */ public function getFilter() { return $this->filter; } - /** - * @inheritDoc - */ public function getFormData(): array { return []; } - /** - * @inheritDoc - */ public function getQueryBuilders(): array { - if (null === self::$kernel) { self::bootKernel(); } @@ -57,4 +59,4 @@ class JobFilterTest extends AbstractFilterTest ->from('ChillPersonBundle:AccompanyingPeriodWork', 'acpw'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/OpenBetweenDatesFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/OpenBetweenDatesFilterTest.php index ccb7e38da..5e4ae3d0a 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/OpenBetweenDatesFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/OpenBetweenDatesFilterTest.php @@ -1,13 +1,27 @@ filter = self::$container->get('chill.person.export.filter_openbetweendates'); } - /** - * @inheritDoc - */ public function getFilter() { return $this->filter; } - /** - * @inheritDoc - */ public function getFormData(): array { return [ [ - 'date_from' => \DateTime::createFromFormat('Y-m-d', '2022-05-01'), - 'date_to' => \DateTime::createFromFormat('Y-m-d', '2022-06-01'), + 'date_from' => DateTime::createFromFormat('Y-m-d', '2022-05-01'), + 'date_to' => DateTime::createFromFormat('Y-m-d', '2022-06-01'), ], ]; } - /** - * @inheritDoc - */ public function getQueryBuilders(): array { if (null === self::$kernel) { @@ -63,4 +68,4 @@ class OpenBetweenDatesFilterTest extends AbstractFilterTest ->select('acp.id'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/OriginFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/OriginFilterTest.php index 7aa1f1c7a..f37cebc72 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/OriginFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/OriginFilterTest.php @@ -1,5 +1,14 @@ filter = self::$container->get('chill.person.export.filter_origin'); } - /** - * @inheritDoc - */ public function getFilter() { return $this->filter; } - /** - * @inheritDoc - */ public function getFormData(): array { $em = self::$container->get(EntityManagerInterface::class); @@ -47,19 +54,15 @@ class OriginFilterTest extends AbstractFilterTest $data = []; - foreach($array as $l) { + foreach ($array as $l) { $data[] = ['accepted_origins' => $l]; } return $data; } - /** - * @inheritDoc - */ public function getQueryBuilders(): array { - if (null === self::$kernel) { self::bootKernel(); } @@ -72,4 +75,4 @@ class OriginFilterTest extends AbstractFilterTest ->from('ChillPersonBundle:AccompanyingPeriod', 'acp'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ReferrerFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ReferrerFilterTest.php index ec84c7027..84340e267 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ReferrerFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ReferrerFilterTest.php @@ -1,5 +1,14 @@ filter = self::$container->get('chill.person.export.filter_referrer'); } - /** - * @inheritDoc - */ public function getFilter() { return $this->filter; } - /** - * @inheritDoc - */ public function getFormData(): array { $em = self::$container->get(EntityManagerInterface::class); @@ -48,16 +55,13 @@ class ReferrerFilterTest extends AbstractFilterTest $data = []; - foreach($array as $u) { + foreach ($array as $u) { $data[] = ['accepted_referrers' => $u]; } return $data; } - /** - * @inheritDoc - */ public function getQueryBuilders(): array { if (null === self::$kernel) { @@ -79,4 +83,4 @@ class ReferrerFilterTest extends AbstractFilterTest ->select('r.id'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/RequestorFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/RequestorFilterTest.php index f8b01aea1..50dcb20f5 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/RequestorFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/RequestorFilterTest.php @@ -1,14 +1,26 @@ filter = self::$container->get('chill.person.export.filter_requestor'); } - /** - * @inheritDoc - */ public function getFilter() { return $this->filter; } - /** - * @inheritDoc - */ public function getFormData(): array { return [ @@ -47,9 +53,6 @@ class RequestorFilterTest extends AbstractFilterTest ]; } - /** - * @inheritDoc - */ public function getQueryBuilders(): array { if (null === self::$kernel) { @@ -64,4 +67,4 @@ class RequestorFilterTest extends AbstractFilterTest ->select('acp.id'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ScopeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ScopeFilterTest.php index d387769cc..1269f0372 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ScopeFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/ScopeFilterTest.php @@ -1,12 +1,24 @@ getLocale()->willReturn('fr'); $this->filter = self::$container->get('chill.person.export.filter_scope'); - } - /** - * @inheritDoc - */ public function getFilter() { return $this->filter; } - /** - * @inheritDoc - */ public function getFormData(): array { return []; } - /** - * @inheritDoc - */ public function getQueryBuilders(): array { - if (null === self::$kernel) { self::bootKernel(); } @@ -58,4 +59,4 @@ class ScopeFilterTest extends AbstractFilterTest ->from('ChillPersonBundle:AccompanyingPeriodWork', 'acpw'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialActionFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialActionFilterTest.php index 6de54c23a..c7834a33a 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialActionFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialActionFilterTest.php @@ -1,5 +1,14 @@ filter = self::$container->get('chill.person.export.filter_socialaction'); } - /** - * @inheritDoc - */ public function getFilter() { return $this->filter; } - /** - * @inheritDoc - */ public function getFormData(): array { $em = self::$container->get(EntityManagerInterface::class); @@ -49,16 +56,13 @@ class SocialActionFilterTest extends AbstractFilterTest $data = []; - foreach($array as $a) { + foreach ($array as $a) { $data[] = ['accepted_socialactions' => $a]; } return $data; } - /** - * @inheritDoc - */ public function getQueryBuilders(): array { if (null === self::$kernel) { @@ -73,4 +77,4 @@ class SocialActionFilterTest extends AbstractFilterTest ->select('acp.id'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialIssueFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialIssueFilterTest.php index ac092884b..16e77381f 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialIssueFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialIssueFilterTest.php @@ -1,5 +1,14 @@ filter = self::$container->get('chill.person.export.filter_socialissue'); } - /** - * @inheritDoc - */ public function getFilter() { return $this->filter; } - /** - * @inheritDoc - */ public function getFormData(): array { $em = self::$container->get(EntityManagerInterface::class); @@ -47,19 +54,15 @@ class SocialIssueFilterTest extends AbstractFilterTest $data = []; - foreach($array as $i) { + foreach ($array as $i) { $data[] = ['accepted_socialissues' => $i]; } return $data; } - /** - * @inheritDoc - */ public function getQueryBuilders(): array { - if (null === self::$kernel) { self::bootKernel(); } @@ -72,4 +75,4 @@ class SocialIssueFilterTest extends AbstractFilterTest ->from('ChillPersonBundle:AccompanyingPeriod', 'acp'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/StepFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/StepFilterTest.php index 1a8dfd2ff..ac75b2123 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/StepFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/StepFilterTest.php @@ -1,12 +1,25 @@ filter = self::$container->get('chill.person.export.filter_step'); } - /** - * @inheritDoc - */ public function getFilter() { return $this->filter; } - /** - * @inheritDoc - */ public function getFormData(): array { return [ @@ -43,12 +50,8 @@ class StepFilterTest extends AbstractFilterTest ]; } - /** - * @inheritDoc - */ public function getQueryBuilders(): array { - if (null === self::$kernel) { self::bootKernel(); } @@ -61,4 +64,4 @@ class StepFilterTest extends AbstractFilterTest ->from('ChillPersonBundle:AccompanyingPeriod', 'acp'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/UserJobFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/UserJobFilterTest.php index 452728384..3c91c8efa 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/UserJobFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/UserJobFilterTest.php @@ -1,12 +1,25 @@ filter = self::$container->get('chill.person.export.filter_userjob'); } - /** - * @inheritDoc - */ public function getFilter() { return $this->filter; } - /** - * @inheritDoc - */ public function getFormData(): array { return []; } - /** - * @inheritDoc - */ public function getQueryBuilders(): array { - if (null === self::$kernel) { self::bootKernel(); } @@ -57,4 +60,4 @@ class UserJobFilterTest extends AbstractFilterTest ->from('ChillPersonBundle:AccompanyingPeriod', 'acp'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/UserScopeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/UserScopeFilterTest.php index fd22ded6e..72d5eaf57 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/UserScopeFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/UserScopeFilterTest.php @@ -1,12 +1,25 @@ getLocale()->willReturn('fr'); $this->filter = self::$container->get('chill.person.export.filter_userscope'); - } - /** - * @inheritDoc - */ public function getFilter() { return $this->filter; } - /** - * @inheritDoc - */ public function getFormData(): array { return []; } - /** - * @inheritDoc - */ public function getQueryBuilders(): array { - if (null === self::$kernel) { self::bootKernel(); } @@ -58,4 +60,4 @@ class UserScopeFilterTest extends AbstractFilterTest ->from('ChillPersonBundle:AccompanyingPeriod', 'acp'), ]; } -} \ No newline at end of file +} From a967e1ed17f5df6cba4c6f58847dcddaedd3ac8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 5 Oct 2022 15:23:28 +0200 Subject: [PATCH 120/120] fix cs --- .../ACPAggregators/DateAggregator.php | 5 +- .../Aggregator/ActivityTypeAggregator.php | 1 - .../Aggregator/ActivityUserAggregator.php | 2 +- .../ActivityReasonAggregator.php | 4 +- .../LinkedToACP/AvgActivityDuration.php | 10 +- .../LinkedToACP/AvgActivityVisitDuration.php | 13 +- .../Export/LinkedToACP/CountActivity.php | 7 +- .../LinkedToACP/SumActivityDuration.php | 10 +- .../LinkedToACP/SumActivityVisitDuration.php | 10 +- .../Export/LinkedToPerson/CountActivity.php | 3 +- .../Filter/ACPFilters/ActivityTypeFilter.php | 2 +- .../Export/Filter/ActivityTypeFilter.php | 6 +- .../PersonFilters/ActivityReasonFilter.php | 3 +- .../Form/Type/TranslatableActivityType.php | 3 - .../Repository/ActivityTypeRepository.php | 23 +- .../ActivityTypeRepositoryInterface.php | 12 +- .../Authorization/ActivityStatsVoter.php | 10 +- .../BySocialActionAggregatorTest.php | 7 +- .../BySocialIssueAggregatorTest.php | 7 +- .../ByThirdpartyAggregatorTest.php | 7 +- .../ACPAggregators/ByUserAggregatorTest.php | 7 +- .../ACPAggregators/DateAggregatorTest.php | 9 +- .../LocationTypeAggregatorTest.php | 7 +- .../UserScopeAggregatorTest.php | 7 +- .../ActivityReasonAggregatorTest.php | 9 +- .../LinkedToPerson/ListActivityTest.php | 2 +- .../ACPFilters/ActivityTypeFilterTest.php | 2 +- .../ACPFilters/BySocialActionFilterTest.php | 2 +- .../ACPFilters/BySocialIssueFilterTest.php | 5 +- .../Filter/ACPFilters/ByUserFilterTest.php | 2 +- .../Filter/ACPFilters/EmergencyFilterTest.php | 4 +- .../ACPFilters/LocationTypeFilterTest.php | 2 +- .../ACPFilters/SentReceivedFilterTest.php | 4 +- .../Filter/ACPFilters/UserFilterTest.php | 2 +- .../Filter/ACPFilters/UserScopeFilterTest.php | 2 +- .../Export/Filter/ActivityDateFilterTest.php | 7 +- .../Filter/ActivityReasonFilterTest.php | 4 +- .../Export/Filter/ActivityTypeFilterTest.php | 2 +- .../ActivityReasonFilterTest.php | 2 +- ...sonHavingActivityBetweenDateFilterTest.php | 7 +- .../Config/ConfigRepository.php | 4 +- .../Export/Aggregator/AgentAggregator.php | 1 + .../Aggregator/CancelReasonAggregator.php | 1 + .../Export/Aggregator/JobAggregator.php | 1 + .../Export/Aggregator/LocationAggregator.php | 2 +- .../Aggregator/LocationTypeAggregator.php | 1 + .../Export/Aggregator/MonthYearAggregator.php | 1 - .../Export/Aggregator/ScopeAggregator.php | 1 + .../Export/Filter/JobFilter.php | 1 + .../Export/Filter/ScopeFilter.php | 1 + .../Controller/ExportController.php | 2 +- .../Entity/GeographicalUnit.php | 55 ++--- .../Entity/GeographicalUnitLayer.php | 42 ++-- .../ChillMainBundle/Export/ExportManager.php | 5 +- .../Form/Type/Export/PickCenterType.php | 5 - .../Repository/CenterRepository.php | 1 - .../Repository/CenterRepositoryInterface.php | 13 +- .../GeographicalUnitLayerLayerRepository.php | 31 ++- ...ographicalUnitLayerRepositoryInterface.php | 11 +- .../Repository/GeographicalUnitRepository.php | 20 +- .../GeographicalUnitRepositoryInterface.php | 12 +- .../Repository/ScopeRepository.php | 1 - .../Repository/ScopeRepositoryInterface.php | 14 +- .../Authorization/ChillExportVoter.php | 1 - .../Import/AddressReferenceBaseImporter.php | 5 +- .../Import/GeographicalUnitBaseImporter.php | 32 +-- .../Test/Export/AbstractAggregatorTest.php | 1 + .../Test/Export/AbstractFilterTest.php | 1 + .../ChillMainBundle/Test/ProphecyTrait.php | 1 + .../AddressReferenceBaseImporterTest.php | 34 ++- .../GeographicalUnitBaseImporterTest.php | 31 ++- .../Import/PostalCodeBaseImporterTest.php | 36 ++- .../NotificationOnTransitionTest.php | 34 ++- ...ntityWorkflowTransitionEventSubscriber.php | 35 +-- .../NotificationOnTransition.php | 8 +- .../migrations/Version20220730204216.php | 17 +- .../migrations/Version20220913174922.php | 17 +- .../migrations/Version20221003112151.php | 66 ++--- .../migrations/Version20221003132620.php | 21 +- .../ChillPersonBundle/Entity/Person.php | 117 +++++---- .../Entity/Person/PersonCenterCurrent.php | 78 +++--- .../Entity/Person/PersonCenterHistory.php | 139 +++++------ .../AdministrativeLocationAggregator.php | 1 + .../ClosingMotiveAggregator.php | 5 +- .../GeographicalUnitStatAggregator.php | 164 ++++++------- .../JobAggregator.php | 1 + .../OriginAggregator.php | 1 + .../ReferrerAggregator.php | 1 + .../ReferrerScopeAggregator.php | 141 +++++------ .../ScopeAggregator.php | 1 + .../SocialIssueAggregator.php | 1 + .../StepAggregator.php | 2 +- .../ChildrenNumberAggregator.php | 3 +- .../CompositionAggregator.php | 1 - .../GeographicalUnitAggregator.php | 163 ++++++------- .../HouseholdPositionAggregator.php | 1 + .../MaritalStatusAggregator.php | 1 + .../ActionTypeAggregator.php | 1 + .../SocialWorkAggregators/GoalAggregator.php | 1 + .../GoalResultAggregator.php | 147 +++++------ .../SocialWorkAggregators/JobAggregator.php | 1 + .../ReferrerAggregator.php | 1 + .../ResultAggregator.php | 1 + .../SocialWorkAggregators/ScopeAggregator.php | 1 + .../Export/Export/CountAccompanyingCourse.php | 7 +- .../Export/Export/CountEvaluation.php | 8 +- .../Export/Export/CountHousehold.php | 10 +- .../Export/Export/CountPerson.php | 5 +- .../CountPersonWithAccompanyingCourse.php | 3 +- .../Export/Export/CountSocialWorkActions.php | 7 +- .../Export/Export/ListPersonDuplicate.php | 1 - .../Export/StatAccompanyingCourseDuration.php | 7 +- .../ActiveOneDayBetweenDatesFilter.php | 1 - .../CurrentUserScopeFilter.php | 1 + .../EvaluationFilter.php | 1 + .../GeographicalUnitStatFilter.php | 29 +-- .../OpenBetweenDatesFilter.php | 1 - .../RequestorFilter.php | 2 +- .../SocialIssueFilter.php | 1 + .../HouseholdFilters/CompositionFilter.php | 1 - .../Export/Filter/PersonFilters/AgeFilter.php | 11 +- .../PersonFilters/DeadOrAliveFilter.php | 6 +- .../PersonFilters/GeographicalUnitFilter.php | 133 +++++----- .../PersonFilters/MaritalStatusFilter.php | 6 +- .../ResidentialAddressAtThirdpartyFilter.php | 1 + .../ResidentialAddressAtUserFilter.php | 4 +- .../Filter/SocialWorkFilters/JobFilter.php | 1 + .../SocialWorkFilters/ReferrerFilter.php | 1 + .../Filter/SocialWorkFilters/ScopeFilter.php | 1 + .../SocialWorkTypeFilter.php | 185 +++++++------- .../ClosingMotiveRepository.php | 53 ++-- .../ClosingMotiveRepositoryInterface.php | 9 + .../Person/PersonCenterHistoryInterface.php | 10 +- .../Person/PersonCenterHistoryRepository.php | 9 + .../Repository/PersonACLAwareRepository.php | 1 - .../Security/Authorization/HouseholdVoter.php | 13 +- .../AdministrativeLocationAggregatorTest.php | 9 +- .../ClosingMotiveAggregatorTest.php | 9 +- .../ConfidentialAggregatorTest.php | 9 +- .../DurationAggregatorTest.php | 9 +- .../EmergencyAggregatorTest.php | 9 +- .../EvaluationAggregatorTest.php | 9 +- .../GeographicalUnitStatAggregatorTest.php | 9 +- .../IntensityAggregatorTest.php | 9 +- .../JobAggregatorTest.php | 9 +- .../OriginAggregatorTest.php | 9 +- .../ReferrerAggregatorTest.php | 9 +- .../ReferrerScopeAggregatorTest.php | 26 +- .../RequestorAggregatorTest.php | 9 +- .../ScopeAggregatorTest.php | 9 +- .../SocialActionAggregatorTest.php | 9 +- .../SocialIssueAggregatorTest.php | 9 +- .../StepAggregatorTest.php | 12 +- .../EvaluationTypeAggregatorTest.php | 9 +- .../ChildrenNumberAggregatorTest.php | 12 +- .../CompositionAggregatorTest.php | 12 +- .../CountryOfBirthAggregatorTest.php | 9 +- .../HouseholdPositionAggregatorTest.php | 10 +- .../MaritalStatusAggregatorTest.php | 7 +- .../ActionTypeAggregatorTest.php | 3 +- .../GoalAggregatorTest.php | 3 +- .../GoalResultAggregatorTest.php | 9 +- .../JobAggregatorTest.php | 9 +- .../ReferrerAggregatorTest.php | 3 +- .../ResultAggregatorTest.php | 3 +- .../ScopeAggregatorTest.php | 9 +- .../ActiveOnDateFilterTest.php | 1 - .../ActiveOneDayBetweenDatesFilterTest.php | 1 - .../AdministrativeLocationFilterTest.php | 3 +- .../EvaluationFilterTest.php | 1 - .../GeographicalUnitStatFilterTest.php | 1 - .../OpenBetweenDatesFilterTest.php | 1 - .../ReferrerFilterTest.php | 1 - .../RequestorFilterTest.php | 1 - .../SocialActionFilterTest.php | 1 - .../EvaluationTypeFilterTest.php | 2 +- .../EvaluationFilters/MaxDateFilterTest.php | 5 +- .../CompositionFilterTest.php | 5 +- .../AccompanyingPeriodClosingFilterTest.php | 7 +- .../AccompanyingPeriodOpeningFilterTest.php | 7 +- .../Filter/PersonFilters/AgeFilterTest.php | 5 +- .../PersonFilters/DeadOrAliveFilterTest.php | 7 +- .../PersonFilters/DeathdateFilterTest.php | 9 +- .../PersonFilters/MaritalStatusFilterTest.php | 4 +- .../PersonFilters/NationalityFilterTest.php | 5 +- ...sidentialAddressAtThirdpartyFilterTest.php | 8 +- .../ResidentialAddressAtUserFilterTest.php | 4 +- .../SocialWorkFilters/ReferrerFilterTest.php | 5 +- .../SocialWorkTypeFilterTest.php | 8 +- .../PersonACLAwareRepositoryTest.php | 38 ++- .../migrations/Version20220926154347.php | 21 +- .../Export/Filter/ReportDateFilterTest.php | 9 +- .../SingleTaskAclAwareRepository.php | 4 +- .../SingleTaskACLAwareRepositoryTest.php | 229 ++++++++++-------- 194 files changed, 1580 insertions(+), 1386 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php index 982f0d6ed..058b1fc15 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/DateAggregator.php @@ -13,7 +13,6 @@ namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators; use Chill\ActivityBundle\Export\Declarations; use Chill\MainBundle\Export\AggregatorInterface; -use DateTime; use Doctrine\ORM\QueryBuilder; use RuntimeException; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; @@ -50,14 +49,17 @@ class DateAggregator implements AggregatorInterface switch ($data['frequency']) { case 'month': $fmt = 'YYYY-MM'; + break; case 'week': $fmt = 'YYYY-IW'; + break; case 'year': $fmt = 'YYYY'; $order = 'DESC'; + break; // order DESC does not works ! default: @@ -98,7 +100,6 @@ class DateAggregator implements AggregatorInterface switch ($data['frequency']) { case 'month': - case 'week': //return $this->translator->trans('for week') .' '. $value ; diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php index 74df67d22..04ce2014c 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityTypeAggregator.php @@ -16,7 +16,6 @@ use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface; use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Closure; -use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use function in_array; diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUserAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUserAggregator.php index 8e29f9cb0..86e9e77bb 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUserAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ActivityUserAggregator.php @@ -61,7 +61,7 @@ class ActivityUserAggregator implements AggregatorInterface public function getLabels($key, $values, $data): Closure { - return function ($value) { + return function ($value) { if ('_header' === $value) { return 'Activity user'; } diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/PersonAggregators/ActivityReasonAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/PersonAggregators/ActivityReasonAggregator.php index f82793e71..bf6b3ca02 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/PersonAggregators/ActivityReasonAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/PersonAggregators/ActivityReasonAggregator.php @@ -25,8 +25,8 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface; -use function array_key_exists; use function count; +use function in_array; class ActivityReasonAggregator implements AggregatorInterface, ExportElementValidatedInterface { @@ -67,7 +67,7 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali $qb->addSelect($elem . ' as ' . $alias); // make a jointure only if needed - if (!in_array( 'actreasons', $qb->getAllAliases(), true)) { + if (!in_array('actreasons', $qb->getAllAliases(), true)) { $qb->innerJoin('activity.reasons', 'actreasons'); } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php index f45adc0b3..326033013 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityDuration.php @@ -23,6 +23,7 @@ use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; class AvgActivityDuration implements ExportInterface, GroupedExportInterface @@ -57,7 +58,7 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface public function getLabels($key, array $values, $data) { if ('export_avg_activity_duration' !== $key) { - throw new \LogicException("the key {$key} is not used by this export"); + throw new LogicException("the key {$key} is not used by this export"); } return static fn ($value) => '_header' === $value ? 'Average activities linked to an accompanying period duration' : $value; @@ -99,14 +100,13 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface $qb ->andWhere( $qb->expr()->exists( - 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part - JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) ' ) ) - ->setParameter('authorized_centers', $centers) - ; + ->setParameter('authorized_centers', $centers); return $qb; } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php index 661f169dd..6514be3e0 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/AvgActivityVisitDuration.php @@ -23,6 +23,7 @@ use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterface @@ -58,7 +59,7 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac public function getLabels($key, array $values, $data) { if ('export_avg_activity_visit_duration' !== $key) { - throw new \LogicException("the key {$key} is not used by this export"); + throw new LogicException("the key {$key} is not used by this export"); } return static fn ($value) => '_header' === $value ? 'Average activities linked to an accompanying period visit duration' : $value; @@ -95,20 +96,18 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac $qb ->join('activity.accompanyingPeriod', 'acp') ->select('AVG(activity.travelTime) as export_avg_activity_visit_duration') - ->andWhere($qb->expr()->isNotNull('activity.travelTime')) - ; + ->andWhere($qb->expr()->isNotNull('activity.travelTime')); $qb ->andWhere( $qb->expr()->exists( - 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part - JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) ' ) ) - ->setParameter('authorized_centers', $centers) - ; + ->setParameter('authorized_centers', $centers); return $qb; } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php index 46d2f3c33..92e8cdc27 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/CountActivity.php @@ -97,14 +97,13 @@ class CountActivity implements ExportInterface, GroupedExportInterface $qb ->andWhere( $qb->expr()->exists( - 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part - JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) ' ) ) - ->setParameter('authorized_centers', $centers) - ; + ->setParameter('authorized_centers', $centers); $qb->select('COUNT(DISTINCT activity.id) as export_count_activity'); diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php index 405b54885..692ff5530 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityDuration.php @@ -23,6 +23,7 @@ use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; class SumActivityDuration implements ExportInterface, GroupedExportInterface @@ -58,7 +59,7 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface public function getLabels($key, array $values, $data) { if ('export_sum_activity_duration' !== $key) { - throw new \LogicException("the key {$key} is not used by this export"); + throw new LogicException("the key {$key} is not used by this export"); } return static fn ($value) => '_header' === $value ? 'Sum activities linked to an accompanying period duration' : $value; @@ -100,14 +101,13 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface $qb ->andWhere( $qb->expr()->exists( - 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part - JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) ' ) ) - ->setParameter('authorized_centers', $centers) - ; + ->setParameter('authorized_centers', $centers); return $qb; } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php index 40020cb4b..e9be03003 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToACP/SumActivityVisitDuration.php @@ -23,6 +23,7 @@ use Chill\PersonBundle\Export\Declarations as PersonDeclarations; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; class SumActivityVisitDuration implements ExportInterface, GroupedExportInterface @@ -58,7 +59,7 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac public function getLabels($key, array $values, $data) { if ('export_sum_activity_visit_duration' !== $key) { - throw new \LogicException("the key {$key} is not used by this export"); + throw new LogicException("the key {$key} is not used by this export"); } return static fn ($value) => '_header' === $value ? 'Sum activities linked to an accompanying period visit duration' : $value; @@ -100,14 +101,13 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac $qb ->andWhere( $qb->expr()->exists( - 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part - JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) ' ) ) - ->setParameter('authorized_centers', $centers) - ; + ->setParameter('authorized_centers', $centers); return $qb; } diff --git a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php index 116fa4b6a..fec2b8617 100644 --- a/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php +++ b/src/Bundle/ChillActivityBundle/Export/Export/LinkedToPerson/CountActivity.php @@ -87,8 +87,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface $qb = $this->activityRepository ->createQueryBuilder('activity') ->join('activity.person', 'person') - ->join('person.centerHistory', 'centerHistory') - ; + ->join('person.centerHistory', 'centerHistory'); $qb->select('COUNT(activity.id) as export_count_activity'); diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ActivityTypeFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ActivityTypeFilter.php index d17a6af99..b6e26dfdb 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ActivityTypeFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/ActivityTypeFilter.php @@ -18,10 +18,10 @@ use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\Query\Expr; -use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; class ActivityTypeFilter implements FilterInterface { diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ActivityTypeFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ActivityTypeFilter.php index 7442fabb7..9bcae2b46 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ActivityTypeFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ActivityTypeFilter.php @@ -17,8 +17,6 @@ use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface; use Chill\MainBundle\Export\ExportElementValidatedInterface; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; -use Doctrine\ORM\Query\Expr; -use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; @@ -79,8 +77,8 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter 'multiple' => true, 'expanded' => false, 'attr' => [ - 'class' => 'select2' - ] + 'class' => 'select2', + ], ]); } diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/PersonFilters/ActivityReasonFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/PersonFilters/ActivityReasonFilter.php index 6d78b3b38..871c5a829 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/PersonFilters/ActivityReasonFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/PersonFilters/ActivityReasonFilter.php @@ -19,14 +19,13 @@ use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Doctrine\ORM\Query\Expr; -use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface; -use function array_key_exists; use function count; +use function in_array; class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInterface { diff --git a/src/Bundle/ChillActivityBundle/Form/Type/TranslatableActivityType.php b/src/Bundle/ChillActivityBundle/Form/Type/TranslatableActivityType.php index 8140043a4..fd2784338 100644 --- a/src/Bundle/ChillActivityBundle/Form/Type/TranslatableActivityType.php +++ b/src/Bundle/ChillActivityBundle/Form/Type/TranslatableActivityType.php @@ -14,11 +14,8 @@ namespace Chill\ActivityBundle\Form\Type; use Chill\ActivityBundle\Entity\ActivityType; use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; -use Doctrine\DBAL\Types\Types; -use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; class TranslatableActivityType extends AbstractType diff --git a/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepository.php b/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepository.php index aee3706cf..7bd679e8b 100644 --- a/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepository.php +++ b/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepository.php @@ -12,11 +12,8 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Repository; use Chill\ActivityBundle\Entity\ActivityType; -use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; -use Doctrine\Persistence\ManagerRegistry; -use UnexpectedValueException; final class ActivityTypeRepository implements ActivityTypeRepositoryInterface { @@ -24,15 +21,7 @@ final class ActivityTypeRepository implements ActivityTypeRepositoryInterface public function __construct(EntityManagerInterface $em) { - $this->repository = $em->getRepository(ActivityType::class); - } - - /** - * @return array|ActivityType[] - */ - public function findAllActive(): array - { - return $this->findBy(['active' => true]); + $this->repository = $em->getRepository(ActivityType::class); } public function find($id): ?ActivityType @@ -48,6 +37,14 @@ final class ActivityTypeRepository implements ActivityTypeRepositoryInterface return $this->repository->findAll(); } + /** + * @return array|ActivityType[] + */ + public function findAllActive(): array + { + return $this->findBy(['active' => true]); + } + /** * @return array|ActivityType[] */ @@ -65,6 +62,4 @@ final class ActivityTypeRepository implements ActivityTypeRepositoryInterface { return ActivityType::class; } - - } diff --git a/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepositoryInterface.php b/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepositoryInterface.php index 533798e5e..6172fe257 100644 --- a/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepositoryInterface.php +++ b/src/Bundle/ChillActivityBundle/Repository/ActivityTypeRepositoryInterface.php @@ -1,5 +1,14 @@ getAttributes(); } - protected function voteOnAttribute($attribute, $subject, TokenInterface $token) - { - return $this->helper->voteOnAttribute($attribute, $subject, $token); - } - protected function supports($attribute, $subject) { return $this->helper->supports($attribute, $subject); } + protected function voteOnAttribute($attribute, $subject, TokenInterface $token) + { + return $this->helper->voteOnAttribute($attribute, $subject, $token); + } + private function getAttributes() { return [self::STATS, self::LISTS]; diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialActionAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialActionAggregatorTest.php index d9b3a8cfb..2d28d0967 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialActionAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialActionAggregatorTest.php @@ -16,6 +16,10 @@ use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\BySocialActionAggregat use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class BySocialActionAggregatorTest extends AbstractAggregatorTest { private BySocialActionAggregator $aggregator; @@ -52,8 +56,7 @@ final class BySocialActionAggregatorTest extends AbstractAggregatorTest ->select('count(activity.id)') ->from(Activity::class, 'activity') ->join('activity.accompanyingPeriod', 'acp') - ->join('activity.socialActions', 'actsocialaction') - , + ->join('activity.socialActions', 'actsocialaction'), ]; } } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialIssueAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialIssueAggregatorTest.php index 87e8462db..36a7c7ceb 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialIssueAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/BySocialIssueAggregatorTest.php @@ -16,6 +16,10 @@ use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\BySocialIssueAggregato use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class BySocialIssueAggregatorTest extends AbstractAggregatorTest { private BySocialIssueAggregator $aggregator; @@ -52,8 +56,7 @@ final class BySocialIssueAggregatorTest extends AbstractAggregatorTest ->select('count(activity.id)') ->from(Activity::class, 'activity') ->join('activity.accompanyingPeriod', 'acp') - ->join('activity.socialIssues', 'actsocialissue') - , + ->join('activity.socialIssues', 'actsocialissue'), ]; } } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByThirdpartyAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByThirdpartyAggregatorTest.php index c9a824dc3..89c853e55 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByThirdpartyAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByThirdpartyAggregatorTest.php @@ -16,6 +16,10 @@ use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByThirdpartyAggregator use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class ByThirdpartyAggregatorTest extends AbstractAggregatorTest { private ByThirdpartyAggregator $aggregator; @@ -52,8 +56,7 @@ final class ByThirdpartyAggregatorTest extends AbstractAggregatorTest ->select('count(activity.id)') ->from(Activity::class, 'activity') ->join('activity.accompanyingPeriod', 'acp') - ->join('activity.thirdParties', 'acttparty') - , + ->join('activity.thirdParties', 'acttparty'), ]; } } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php index 00243ce68..2ee78b2bf 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/ByUserAggregatorTest.php @@ -16,6 +16,10 @@ use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByUserAggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class ByUserAggregatorTest extends AbstractAggregatorTest { private ByUserAggregator $aggregator; @@ -52,8 +56,7 @@ final class ByUserAggregatorTest extends AbstractAggregatorTest ->select('count(activity.id)') ->from(Activity::class, 'activity') ->join('activity.accompanyingPeriod', 'acp') - ->join('activity.users', 'actusers') - , + ->join('activity.users', 'actusers'), ]; } } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/DateAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/DateAggregatorTest.php index c5a1b083c..8b682487b 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/DateAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/DateAggregatorTest.php @@ -16,6 +16,10 @@ use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\DateAggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class DateAggregatorTest extends AbstractAggregatorTest { private DateAggregator $aggregator; @@ -43,7 +47,7 @@ final class DateAggregatorTest extends AbstractAggregatorTest ], [ 'frequency' => 'year', - ] + ], ]; } @@ -59,8 +63,7 @@ final class DateAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(activity.id)') ->from(Activity::class, 'activity') - ->join('activity.accompanyingPeriod', 'acp') - , + ->join('activity.accompanyingPeriod', 'acp'), ]; } } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/LocationTypeAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/LocationTypeAggregatorTest.php index 69be2c900..81f7be13b 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/LocationTypeAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/LocationTypeAggregatorTest.php @@ -16,6 +16,10 @@ use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\LocationTypeAggregator use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class LocationTypeAggregatorTest extends AbstractAggregatorTest { private LocationTypeAggregator $aggregator; @@ -52,8 +56,7 @@ final class LocationTypeAggregatorTest extends AbstractAggregatorTest ->select('count(activity.id)') ->from(Activity::class, 'activity') ->join('activity.accompanyingPeriod', 'acp') - ->join('activity.location', 'actloc') - , + ->join('activity.location', 'actloc'), ]; } } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php index 73943ce7f..28f13028f 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/ACPAggregators/UserScopeAggregatorTest.php @@ -16,6 +16,10 @@ use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\UserScopeAggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class UserScopeAggregatorTest extends AbstractAggregatorTest { private UserScopeAggregator $aggregator; @@ -52,8 +56,7 @@ final class UserScopeAggregatorTest extends AbstractAggregatorTest ->select('count(activity.id)') ->from(Activity::class, 'activity') ->join('activity.accompanyingPeriod', 'acp') - ->join('activity.user', 'actuser') - , + ->join('activity.user', 'actuser'), ]; } } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/PersonAggregators/ActivityReasonAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/PersonAggregators/ActivityReasonAggregatorTest.php index f89c20e31..69a82cffc 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/PersonAggregators/ActivityReasonAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/PersonAggregators/ActivityReasonAggregatorTest.php @@ -11,16 +11,19 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Tests\Export\Aggregator\PersonAggregators; -use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityReasonAggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Doctrine\ORM\EntityManagerInterface; use Prophecy\PhpUnit\ProphecyTrait; +/** + * @internal + * @coversNothing + */ final class ActivityReasonAggregatorTest extends AbstractAggregatorTest { use ProphecyTrait; - + private ActivityReasonAggregator $aggregator; protected function setUp(): void @@ -33,7 +36,7 @@ final class ActivityReasonAggregatorTest extends AbstractAggregatorTest ->willExtend(\Symfony\Component\HttpFoundation\Request::class); $request->getLocale()->willReturn('fr'); - + self::$container->get('request_stack') ->push($request->reveal()); } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/ListActivityTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/ListActivityTest.php index a613bc083..d39592b5e 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/ListActivityTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Export/LinkedToPerson/ListActivityTest.php @@ -22,7 +22,7 @@ use Prophecy\PhpUnit\ProphecyTrait; final class ListActivityTest extends AbstractExportTest { use ProphecyTrait; - + private ListActivity $export; protected function setUp(): void diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php index e38d4cf5c..70918fb54 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ActivityTypeFilterTest.php @@ -53,7 +53,7 @@ final class ActivityTypeFilterTest extends AbstractFilterTest foreach ($array as $a) { $data[] = [ - 'accepted_activitytypes' => $a + 'accepted_activitytypes' => $a, ]; } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialActionFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialActionFilterTest.php index d8091d068..9beb48082 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialActionFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialActionFilterTest.php @@ -51,7 +51,7 @@ final class BySocialActionFilterTest extends AbstractFilterTest foreach ($array as $a) { $data[] = [ - 'accepted_socialactions' => $a + 'accepted_socialactions' => $a, ]; } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialIssueFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialIssueFilterTest.php index 61e5d4715..f92a143ee 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialIssueFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/BySocialIssueFilterTest.php @@ -51,7 +51,7 @@ final class BySocialIssueFilterTest extends AbstractFilterTest foreach ($array as $a) { $data[] = [ - 'accepted_socialissues' => $a + 'accepted_socialissues' => $a, ]; } @@ -70,8 +70,7 @@ final class BySocialIssueFilterTest extends AbstractFilterTest $em->createQueryBuilder() ->select('count(activity.id)') ->from(Activity::class, 'activity') - ->join('activity.socialIssues', 'actsocialissue') - , + ->join('activity.socialIssues', 'actsocialissue'), ]; } } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php index aaf852b14..6765a918d 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/ByUserFilterTest.php @@ -51,7 +51,7 @@ final class ByUserFilterTest extends AbstractFilterTest foreach ($array as $a) { $data[] = [ - 'accepted_users' => $a + 'accepted_users' => $a, ]; } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/EmergencyFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/EmergencyFilterTest.php index 07810efbe..845bb6490 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/EmergencyFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/EmergencyFilterTest.php @@ -39,8 +39,8 @@ final class EmergencyFilterTest extends AbstractFilterTest public function getFormData(): array { return [ - ['accepted_emergency' => true ], - ['accepted_emergency' => false ], + ['accepted_emergency' => true], + ['accepted_emergency' => false], ]; } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/LocationTypeFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/LocationTypeFilterTest.php index 6888e6cf7..18558b7ee 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/LocationTypeFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/LocationTypeFilterTest.php @@ -51,7 +51,7 @@ final class LocationTypeFilterTest extends AbstractFilterTest foreach ($array as $a) { $data[] = [ - 'accepted_locationtype' => $a + 'accepted_locationtype' => $a, ]; } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/SentReceivedFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/SentReceivedFilterTest.php index d9a24dc27..a3cc6a941 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/SentReceivedFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/SentReceivedFilterTest.php @@ -39,8 +39,8 @@ final class SentReceivedFilterTest extends AbstractFilterTest public function getFormData(): array { return [ - ['accepted_sentreceived' => Activity::SENTRECEIVED_SENT ], - ['accepted_sentreceived' => Activity::SENTRECEIVED_RECEIVED ] + ['accepted_sentreceived' => Activity::SENTRECEIVED_SENT], + ['accepted_sentreceived' => Activity::SENTRECEIVED_RECEIVED], ]; } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserFilterTest.php index 838973caa..6dca9e18a 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserFilterTest.php @@ -51,7 +51,7 @@ final class UserFilterTest extends AbstractFilterTest foreach ($array as $a) { $data[] = [ - 'accepted_users' => $a + 'accepted_users' => $a, ]; } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserScopeFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserScopeFilterTest.php index 6b1040036..9a869206e 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserScopeFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ACPFilters/UserScopeFilterTest.php @@ -51,7 +51,7 @@ final class UserScopeFilterTest extends AbstractFilterTest foreach ($array as $a) { $data[] = [ - 'accepted_userscope' => $a + 'accepted_userscope' => $a, ]; } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityDateFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityDateFilterTest.php index e95fd7c20..150ec30f0 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityDateFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityDateFilterTest.php @@ -14,6 +14,7 @@ namespace Chill\ActivityBundle\Tests\Export\Filter; use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Export\Filter\ActivityDateFilter; use Chill\MainBundle\Test\Export\AbstractFilterTest; +use DateTime; use Doctrine\ORM\EntityManagerInterface; /** @@ -40,9 +41,9 @@ final class ActivityDateFilterTest extends AbstractFilterTest { return [ [ - 'date_from' => \DateTime::createFromFormat('Y-m-d', '2020-01-01'), - 'date_to' => \DateTime::createFromFormat('Y-m-d', '2021-01-01'), - ] + 'date_from' => DateTime::createFromFormat('Y-m-d', '2020-01-01'), + 'date_to' => DateTime::createFromFormat('Y-m-d', '2021-01-01'), + ], ]; } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php index d45ecfbb2..0a7d059a6 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityReasonFilterTest.php @@ -23,7 +23,7 @@ use Prophecy\PhpUnit\ProphecyTrait; final class ActivityReasonFilterTest extends AbstractFilterTest { use ProphecyTrait; - + private ActivityReasonFilter $filter; protected function setUp(): void @@ -34,7 +34,7 @@ final class ActivityReasonFilterTest extends AbstractFilterTest $request = $this->prophesize() ->willExtend(\Symfony\Component\HttpFoundation\Request::class); - + $request->getLocale()->willReturn('fr'); self::$container->get('request_stack') diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityTypeFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityTypeFilterTest.php index 1bee8532e..97d63ddc9 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityTypeFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/ActivityTypeFilterTest.php @@ -51,7 +51,7 @@ final class ActivityTypeFilterTest extends AbstractFilterTest foreach ($array as $a) { $data[] = [ - 'types' => $a + 'types' => $a, ]; } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/ActivityReasonFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/ActivityReasonFilterTest.php index fc7dbbd00..c47b0a0a7 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/ActivityReasonFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/ActivityReasonFilterTest.php @@ -51,7 +51,7 @@ final class ActivityReasonFilterTest extends AbstractFilterTest foreach ($array as $a) { $data[] = [ - 'reasons' => $a + 'reasons' => $a, ]; } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilterTest.php index 42cf9d884..c705c5462 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilterTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/PersonFilters/PersonHavingActivityBetweenDateFilterTest.php @@ -15,6 +15,7 @@ use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\ActivityReason; use Chill\ActivityBundle\Export\Filter\PersonFilters\PersonHavingActivityBetweenDateFilter; use Chill\MainBundle\Test\Export\AbstractFilterTest; +use DateTime; use Doctrine\ORM\EntityManagerInterface; /** @@ -51,9 +52,9 @@ final class PersonHavingActivityBetweenDateFilterTest extends AbstractFilterTest foreach ($array as $a) { $data[] = [ - 'date_from' => \DateTime::createFromFormat('Y-m-d', '2021-07-01'), - 'date_to' => \DateTime::createFromFormat('Y-m-d', '2022-07-01'), - 'reasons' => $a + 'date_from' => DateTime::createFromFormat('Y-m-d', '2021-07-01'), + 'date_to' => DateTime::createFromFormat('Y-m-d', '2022-07-01'), + 'reasons' => $a, ]; } diff --git a/src/Bundle/ChillBudgetBundle/Config/ConfigRepository.php b/src/Bundle/ChillBudgetBundle/Config/ConfigRepository.php index e498ba5a3..6443eeaf7 100644 --- a/src/Bundle/ChillBudgetBundle/Config/ConfigRepository.php +++ b/src/Bundle/ChillBudgetBundle/Config/ConfigRepository.php @@ -70,14 +70,14 @@ class ConfigRepository private function getCharges(bool $onlyActive = false): array { return $onlyActive ? - array_filter($this->charges, function ($el) { return $el['active']; }) + array_filter($this->charges, static function ($el) { return $el['active']; }) : $this->charges; } private function getResources(bool $onlyActive = false): array { return $onlyActive ? - array_filter($this->resources, function ($el) { return $el['active']; }) + array_filter($this->resources, static function ($el) { return $el['active']; }) : $this->resources; } diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php index 57ec33cdd..d1cdd56b1 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/AgentAggregator.php @@ -18,6 +18,7 @@ use Chill\MainBundle\Templating\Entity\UserRender; use Closure; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class AgentAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php index a07f052bf..502f8000c 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/CancelReasonAggregator.php @@ -18,6 +18,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; use Closure; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; class CancelReasonAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php index 17905cf35..d5471d36e 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/JobAggregator.php @@ -18,6 +18,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; use Closure; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class JobAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php index 236b1b74f..7746bc7f2 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationAggregator.php @@ -17,7 +17,7 @@ use Chill\MainBundle\Repository\LocationRepository; use Closure; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Security\Core\Role\Role; +use function in_array; final class LocationAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php index c8d02160f..f48eafd79 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/LocationTypeAggregator.php @@ -18,6 +18,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; use Closure; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class LocationTypeAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php index 26329ad13..a74853119 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/MonthYearAggregator.php @@ -16,7 +16,6 @@ use Chill\MainBundle\Export\AggregatorInterface; use Closure; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Security\Core\Role\Role; class MonthYearAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php b/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php index 7605c3d5d..3b8123b65 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php +++ b/src/Bundle/ChillCalendarBundle/Export/Aggregator/ScopeAggregator.php @@ -18,6 +18,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; use Closure; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class ScopeAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php index 03cd4857d..ed4c2dadd 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/JobFilter.php @@ -20,6 +20,7 @@ use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; +use function in_array; class JobFilter implements FilterInterface { diff --git a/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php b/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php index cbd566e9e..0093865aa 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php +++ b/src/Bundle/ChillCalendarBundle/Export/Filter/ScopeFilter.php @@ -20,6 +20,7 @@ use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; +use function in_array; class ScopeFilter implements FilterInterface { diff --git a/src/Bundle/ChillMainBundle/Controller/ExportController.php b/src/Bundle/ChillMainBundle/Controller/ExportController.php index 4ced2cacc..3d861fdfa 100644 --- a/src/Bundle/ChillMainBundle/Controller/ExportController.php +++ b/src/Bundle/ChillMainBundle/Controller/ExportController.php @@ -445,7 +445,7 @@ class ExportController extends AbstractController $this->logger->notice('[export] choices for an export unserialized', [ 'key' => $key, - 'rawData' => json_encode($rawData) + 'rawData' => json_encode($rawData), ]); $alias = $rawData['alias']; diff --git a/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php b/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php index 49414db27..bd49fe892 100644 --- a/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php +++ b/src/Bundle/ChillMainBundle/Entity/GeographicalUnit.php @@ -15,7 +15,7 @@ use Doctrine\ORM\Mapping as ORM; /** * @ORM\Table(name="chill_main_geographical_unit", uniqueConstraints={ - * @ORM\UniqueConstraint(name="geographical_unit_refid", columns={"layer_id", "unitRefId"}) + * @ORM\UniqueConstraint(name="geographical_unit_refid", columns={"layer_id", "unitRefId"}) * }) * @ORM\Entity(readOnly=true) */ @@ -33,6 +33,11 @@ class GeographicalUnit */ private ?int $id = null; + /** + * @ORM\ManyToOne(targetEntity=GeographicalUnitLayer::class, inversedBy="units") + */ + private ?GeographicalUnitLayer $layer; + /** * @ORM\Column(type="text", nullable=false, options={"default": ""}) */ @@ -43,21 +48,14 @@ class GeographicalUnit */ private string $unitRefId; - /** - * @ORM\ManyToOne(targetEntity=GeographicalUnitLayer::class, inversedBy="units") - */ - private ?GeographicalUnitLayer $layer; - public function getId(): ?int { return $this->id; } - protected function setId(int $id): self + public function getLayer(): ?GeographicalUnitLayer { - $this->id = $id; - - return $this; + return $this->layer; } public function getUnitName(): ?string @@ -65,31 +63,10 @@ class GeographicalUnit return $this->unitName; } - /** - * @return GeographicalUnitLayer|null - */ - public function getLayer(): ?GeographicalUnitLayer - { - 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; } @@ -99,4 +76,18 @@ class GeographicalUnit return $this; } + + public function setUnitRefId(string $unitRefId): GeographicalUnit + { + $this->unitRefId = $unitRefId; + + return $this; + } + + protected function setId(int $id): self + { + $this->id = $id; + + return $this; + } } diff --git a/src/Bundle/ChillMainBundle/Entity/GeographicalUnitLayer.php b/src/Bundle/ChillMainBundle/Entity/GeographicalUnitLayer.php index 162d6ccf4..961a0e79a 100644 --- a/src/Bundle/ChillMainBundle/Entity/GeographicalUnitLayer.php +++ b/src/Bundle/ChillMainBundle/Entity/GeographicalUnitLayer.php @@ -1,5 +1,14 @@ units = new ArrayCollection(); } - /** - * @return int|null - */ public function getId(): ?int { return $this->id; } - /** - * @return array - */ public function getName(): array { return $this->name; } - /** - * @param array $name - * @return GeographicalUnitLayer - */ - public function setName(array $name): GeographicalUnitLayer - { - $this->name = $name; - return $this; - } - - /** - * @return string - */ public function getRefId(): string { return $this->refId; } - /** - * @return Collection - */ public function getUnits(): Collection { return $this->units; } -} \ No newline at end of file + + public function setName(array $name): GeographicalUnitLayer + { + $this->name = $name; + + return $this; + } +} diff --git a/src/Bundle/ChillMainBundle/Export/ExportManager.php b/src/Bundle/ChillMainBundle/Export/ExportManager.php index ba15d0cc0..37aa39695 100644 --- a/src/Bundle/ChillMainBundle/Export/ExportManager.php +++ b/src/Bundle/ChillMainBundle/Export/ExportManager.php @@ -13,7 +13,6 @@ namespace Chill\MainBundle\Export; use Chill\MainBundle\Form\Type\Export\ExportType; use Chill\MainBundle\Form\Type\Export\PickCenterType; -use Chill\MainBundle\Security\Authorization\AuthorizationHelper; use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\QueryBuilder; @@ -265,16 +264,14 @@ class ExportManager $this->handleAggregators($export, $query, $data[ExportType::AGGREGATOR_KEY], $centers); $this->logger->notice('[export] will execute this qb in export', [ - 'dql' => $query->getDQL() + 'dql' => $query->getDQL(), ]); - } else { throw new UnexpectedValueException('The method `intiateQuery` should return ' . 'a `\\Doctrine\\ORM\\NativeQuery` or a `Doctrine\\ORM\\QueryBuilder` ' . 'object.'); } - $result = $export->getResult($query, $data[ExportType::EXPORT_KEY]); if (!is_iterable($result)) { diff --git a/src/Bundle/ChillMainBundle/Form/Type/Export/PickCenterType.php b/src/Bundle/ChillMainBundle/Form/Type/Export/PickCenterType.php index 07776cb71..e74d8e8f4 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/Export/PickCenterType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/Export/PickCenterType.php @@ -14,9 +14,7 @@ namespace Chill\MainBundle\Form\Type\Export; use Chill\MainBundle\Center\GroupingCenterInterface; use Chill\MainBundle\Entity\Center; use Chill\MainBundle\Export\ExportManager; -use Chill\MainBundle\Security\Authorization\AuthorizationHelper; use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; -use Doctrine\ORM\EntityRepository; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\CallbackTransformer; @@ -49,9 +47,6 @@ class PickCenterType extends AbstractType */ protected array $groupingCenters = []; - /** - * @var \Symfony\Component\Security\Core\User\UserInterface - */ protected UserInterface $user; public function __construct( diff --git a/src/Bundle/ChillMainBundle/Repository/CenterRepository.php b/src/Bundle/ChillMainBundle/Repository/CenterRepository.php index d8e54d1c4..6a52f3399 100644 --- a/src/Bundle/ChillMainBundle/Repository/CenterRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/CenterRepository.php @@ -14,7 +14,6 @@ namespace Chill\MainBundle\Repository; use Chill\MainBundle\Entity\Center; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; -use Doctrine\Persistence\ObjectRepository; final class CenterRepository implements CenterRepositoryInterface { diff --git a/src/Bundle/ChillMainBundle/Repository/CenterRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/CenterRepositoryInterface.php index 27ba64caf..eb656f322 100644 --- a/src/Bundle/ChillMainBundle/Repository/CenterRepositoryInterface.php +++ b/src/Bundle/ChillMainBundle/Repository/CenterRepositoryInterface.php @@ -1,5 +1,14 @@ repository->findAll(); } + public function findAllHavingUnits(): array + { + $qb = $this->repository->createQueryBuilder('l'); + + return $qb->where($qb->expr()->gt('SIZE(l.units)', 0)) + ->getQuery() + ->getResult(); + } + /** * @return array|GeographicalUnitLayer[] */ @@ -47,13 +63,4 @@ final class GeographicalUnitLayerLayerRepository implements GeographicalUnitLaye { return GeographicalUnitLayer::class; } - - public function findAllHavingUnits(): array - { - $qb = $this->repository->createQueryBuilder('l'); - - return $qb->where($qb->expr()->gt('SIZE(l.units)', 0)) - ->getQuery() - ->getResult(); - } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillMainBundle/Repository/GeographicalUnitLayerRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/GeographicalUnitLayerRepositoryInterface.php index e7c5abdd9..dbc100bf7 100644 --- a/src/Bundle/ChillMainBundle/Repository/GeographicalUnitLayerRepositoryInterface.php +++ b/src/Bundle/ChillMainBundle/Repository/GeographicalUnitLayerRepositoryInterface.php @@ -1,5 +1,14 @@ 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 + * Will return only partial object, where the @see{GeographicalUnit::geom} property is not loaded. * * @return array|GeographicalUnit[] */ @@ -56,4 +62,4 @@ class GeographicalUnitRepository implements GeographicalUnitRepositoryInterface { return GeographicalUnit::class; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepositoryInterface.php index 10d07893b..76db33422 100644 --- a/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepositoryInterface.php +++ b/src/Bundle/ChillMainBundle/Repository/GeographicalUnitRepositoryInterface.php @@ -1,10 +1,18 @@ executeStatement(array_merge(...$this->waitingForInsert)); - if ($affected === 0) { - throw new \RuntimeException('no row affected'); + if (0 === $affected) { + throw new RuntimeException('no row affected'); } } catch (Exception $e) { // in some case, we can add debug code here diff --git a/src/Bundle/ChillMainBundle/Service/Import/GeographicalUnitBaseImporter.php b/src/Bundle/ChillMainBundle/Service/Import/GeographicalUnitBaseImporter.php index 539e69028..2668d2c2e 100644 --- a/src/Bundle/ChillMainBundle/Service/Import/GeographicalUnitBaseImporter.php +++ b/src/Bundle/ChillMainBundle/Service/Import/GeographicalUnitBaseImporter.php @@ -13,11 +13,10 @@ namespace Chill\MainBundle\Service\Import; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Statement; -use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Types\Types; use Exception; -use LogicException; use Psr\Log\LoggerInterface; +use RuntimeException; use function array_key_exists; use function count; @@ -77,7 +76,7 @@ final class GeographicalUnitBaseImporter string $unitName, string $unitKey, string $geomAsWKT, - int $srid = null + ?int $srid = null ): void { $this->initialize(); @@ -87,7 +86,7 @@ final class GeographicalUnitBaseImporter 'unitName' => $unitName, 'unitKey' => $unitKey, 'geomAsWKT' => $geomAsWKT, - 'srid' => $srid + 'srid' => $srid, ]; if (100 <= count($this->waitingForInsert)) { @@ -138,8 +137,10 @@ final class GeographicalUnitBaseImporter } $statement = $this->cachingStatements[$forNumber]; + try { $i = 0; + foreach ($this->waitingForInsert as $insert) { $statement->bindValue(++$i, $insert['layerKey'], Types::STRING); $statement->bindValue(++$i, $insert['layerName'], Types::JSON); @@ -151,8 +152,8 @@ final class GeographicalUnitBaseImporter $affected = $statement->executeStatement(); - if ($affected === 0) { - throw new \RuntimeException('no row affected'); + if (0 === $affected) { + throw new RuntimeException('no row affected'); } } catch (Exception $e) { throw $e; @@ -182,10 +183,10 @@ final class GeographicalUnitBaseImporter private function updateGeographicalUnitTable(): void { $this->defaultConnection->transactional( - function() { + function () { // 0) create new layers $this->defaultConnection->executeStatement( - " + " WITH unique_layers AS ( SELECT DISTINCT layerKey, layerName FROM geographical_unit_temp ) @@ -195,14 +196,15 @@ final class GeographicalUnitBaseImporter layerName, layerKey FROM unique_layers - ON CONFLICT (refid) + ON CONFLICT (refid) DO UPDATE SET name=EXCLUDED.name - "); + " + ); //1) Add new units $this->logger->info(self::LOG_PREFIX . 'upsert new units'); - $affected = $this->defaultConnection->executeStatement("INSERT INTO chill_main_geographical_unit - (id, geom, unitname, layer_id, unitrefid) + $affected = $this->defaultConnection->executeStatement("INSERT INTO chill_main_geographical_unit + (id, geom, unitname, layer_id, unitrefid) SELECT nextval('chill_main_geographical_unit_id_seq'), geom, @@ -210,7 +212,7 @@ final class GeographicalUnitBaseImporter layer.id, unitKey FROM geographical_unit_temp JOIN chill_main_geographical_unit_layer AS layer ON layer.refid = layerKey - ON CONFLICT (layer_id, unitrefid) + ON CONFLICT (layer_id, unitrefid) DO UPDATE SET geom = EXCLUDED.geom, unitname = EXCLUDED.unitname "); @@ -219,9 +221,9 @@ final class GeographicalUnitBaseImporter //3) Delete units $this->logger->info(self::LOG_PREFIX . 'soft delete adresses'); $affected = $this->defaultConnection->executeStatement('WITH to_delete AS ( - SELECT cmgu.id + SELECT cmgu.id FROM chill_main_geographical_unit AS cmgu - JOIN chill_main_geographical_unit_layer AS cmgul ON cmgul.id = cmgu.layer_id + JOIN chill_main_geographical_unit_layer AS cmgul ON cmgul.id = cmgu.layer_id JOIN geographical_unit_temp AS gut ON cmgul.refid = gut.layerKey AND cmgu.unitrefid = gut.unitKey ) DELETE FROM chill_main_geographical_unit diff --git a/src/Bundle/ChillMainBundle/Test/Export/AbstractAggregatorTest.php b/src/Bundle/ChillMainBundle/Test/Export/AbstractAggregatorTest.php index 40792a1c6..cf5fe5c31 100644 --- a/src/Bundle/ChillMainBundle/Test/Export/AbstractAggregatorTest.php +++ b/src/Bundle/ChillMainBundle/Test/Export/AbstractAggregatorTest.php @@ -25,6 +25,7 @@ use function is_string; /** * Helper which creates a set of test for aggregators. * + * @internal */ abstract class AbstractAggregatorTest extends KernelTestCase { diff --git a/src/Bundle/ChillMainBundle/Test/Export/AbstractFilterTest.php b/src/Bundle/ChillMainBundle/Test/Export/AbstractFilterTest.php index 8e9761c67..bef4fd200 100644 --- a/src/Bundle/ChillMainBundle/Test/Export/AbstractFilterTest.php +++ b/src/Bundle/ChillMainBundle/Test/Export/AbstractFilterTest.php @@ -24,6 +24,7 @@ use function is_string; /** * Helper to test filters. * + * @internal */ abstract class AbstractFilterTest extends KernelTestCase { diff --git a/src/Bundle/ChillMainBundle/Test/ProphecyTrait.php b/src/Bundle/ChillMainBundle/Test/ProphecyTrait.php index ae274ca1d..82a2c434f 100644 --- a/src/Bundle/ChillMainBundle/Test/ProphecyTrait.php +++ b/src/Bundle/ChillMainBundle/Test/ProphecyTrait.php @@ -18,6 +18,7 @@ namespace Chill\MainBundle\Test; * and use tearDownTrait after usage. * * @codeCoverageIgnore + * * @deprecated use @class{Prophecy\PhpUnit\ProphecyTrait} instead */ trait ProphecyTrait diff --git a/src/Bundle/ChillMainBundle/Tests/Services/Import/AddressReferenceBaseImporterTest.php b/src/Bundle/ChillMainBundle/Tests/Services/Import/AddressReferenceBaseImporterTest.php index cb23634f1..0ed1f2059 100644 --- a/src/Bundle/ChillMainBundle/Tests/Services/Import/AddressReferenceBaseImporterTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Services/Import/AddressReferenceBaseImporterTest.php @@ -1,5 +1,14 @@ setRefPostalCodeId($postalCodeId = '1234'.uniqid()) + ->setRefPostalCodeId($postalCodeId = '1234' . uniqid()) ->setPostalCodeSource('testing') ->setCode('TEST456') ->setName('testing'); @@ -54,7 +70,8 @@ class AddressReferenceBaseImporterTest extends KernelTestCase $addresses = $this->addressReferenceRepository->findByPostalCodePattern( $postalCode, - 'Rue test abcc guessed'); + 'Rue test abcc guessed' + ); $this->assertCount(1, $addresses); $this->assertEquals('Rue test abccc-guessed', $addresses[0]->getStreet()); @@ -79,12 +96,11 @@ class AddressReferenceBaseImporterTest extends KernelTestCase $addresses = $this->addressReferenceRepository->findByPostalCodePattern( $postalCode, - 'abcc guessed fixed'); + 'abcc guessed fixed' + ); $this->assertCount('1', $addresses); - $this->assertEquals( 'Rue test abccc guessed fixed', $addresses[0]->getStreet()); + $this->assertEquals('Rue test abccc guessed fixed', $addresses[0]->getStreet()); $this->assertEquals($previousAddressId, $addresses[0]->getId()); } - - -} \ No newline at end of file +} diff --git a/src/Bundle/ChillMainBundle/Tests/Services/Import/GeographicalUnitBaseImporterTest.php b/src/Bundle/ChillMainBundle/Tests/Services/Import/GeographicalUnitBaseImporterTest.php index bc66c3691..60ad20fc5 100644 --- a/src/Bundle/ChillMainBundle/Tests/Services/Import/GeographicalUnitBaseImporterTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Services/Import/GeographicalUnitBaseImporterTest.php @@ -1,5 +1,14 @@ finalize(); - $unit = $this->connection->executeQuery(" + $unit = $this->connection->executeQuery(' SELECT unitname, unitrefid, cmgul.refid AS layerrefid, cmgul.name AS layername, ST_AsText(ST_snapToGrid(ST_Transform(u.geom, 3812), 1)) AS geom - FROM chill_main_geographical_unit u JOIN chill_main_geographical_unit_layer cmgul on u.layer_id = cmgul.id - WHERE u.unitrefid = ?", ['layer_one']); + FROM chill_main_geographical_unit u JOIN chill_main_geographical_unit_layer cmgul on u.layer_id = cmgul.id + WHERE u.unitrefid = ?', ['layer_one']); $results = $unit->fetchAssociative(); @@ -71,10 +84,10 @@ class GeographicalUnitBaseImporterTest extends KernelTestCase $importer->finalize(); - $unit = $this->connection->executeQuery(" + $unit = $this->connection->executeQuery(' SELECT unitname, unitrefid, cmgul.refid AS layerrefid, cmgul.name AS layername, ST_AsText(ST_snapToGrid(ST_Transform(u.geom, 3812), 1)) AS geom - FROM chill_main_geographical_unit u JOIN chill_main_geographical_unit_layer cmgul on u.layer_id = cmgul.id - WHERE u.unitrefid = ?", ['layer_one']); + FROM chill_main_geographical_unit u JOIN chill_main_geographical_unit_layer cmgul on u.layer_id = cmgul.id + WHERE u.unitrefid = ?', ['layer_one']); $results = $unit->fetchAssociative(); @@ -83,7 +96,5 @@ class GeographicalUnitBaseImporterTest extends KernelTestCase $this->assertEquals(json_decode($results['layername'], true), ['fr' => 'Test Layer fixed']); $this->assertEquals($results['layerrefid'], 'test'); $this->assertEquals($results['geom'], 'MULTIPOLYGON(((130 120,45 40,10 40,130 120)),((0 0,15 5,40 10,10 20,0 0)))'); - } - -} \ No newline at end of file +} diff --git a/src/Bundle/ChillMainBundle/Tests/Services/Import/PostalCodeBaseImporterTest.php b/src/Bundle/ChillMainBundle/Tests/Services/Import/PostalCodeBaseImporterTest.php index 826e581ac..ce40d40bf 100644 --- a/src/Bundle/ChillMainBundle/Tests/Services/Import/PostalCodeBaseImporterTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Services/Import/PostalCodeBaseImporterTest.php @@ -1,5 +1,14 @@ importer->importCode( 'BE', - 'tested with pattern '. ($uniqid = uniqid()), + 'tested with pattern ' . ($uniqid = uniqid()), '12345', - $refPostalCodeId = 'test'.uniqid(), + $refPostalCodeId = 'test' . uniqid(), 'test', 50.0, 5.0, @@ -46,8 +59,8 @@ class PostalCodeBaseImporterTest extends KernelTestCase $this->importer->finalize(); $postalCodes = $this->postalCodeRepository->findByPattern( - 'with pattern '.$uniqid, - $this->countryRepository->findOneBy(['countryCode' => 'BE']) + 'with pattern ' . $uniqid, + $this->countryRepository->findOneBy(['countryCode' => 'BE']) ); $this->assertCount(1, $postalCodes); @@ -59,7 +72,7 @@ class PostalCodeBaseImporterTest extends KernelTestCase $this->importer->importCode( 'BE', - 'tested with adapted pattern '. ($uniqid = uniqid()), + 'tested with adapted pattern ' . ($uniqid = uniqid()), '12345', $refPostalCodeId, 'test', @@ -71,7 +84,7 @@ class PostalCodeBaseImporterTest extends KernelTestCase $this->importer->finalize(); $postalCodes = $this->postalCodeRepository->findByPattern( - 'with pattern '.$uniqid, + 'with pattern ' . $uniqid, $this->countryRepository->findOneBy(['countryCode' => 'BE']) ); @@ -79,7 +92,4 @@ class PostalCodeBaseImporterTest extends KernelTestCase $this->assertStringStartsWith('tested with adapted pattern', $postalCodes[0]->getName()); $this->assertEquals($previousId, $postalCodes[0]->getId()); } - - - -} \ No newline at end of file +} diff --git a/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationOnTransitionTest.php b/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationOnTransitionTest.php index 3aa2c71b1..365b01a67 100644 --- a/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationOnTransitionTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationOnTransitionTest.php @@ -1,5 +1,14 @@ setWorkflowName('workflow_name') - ->setRelatedEntityClass(\stdClass::class) - ->setRelatedEntityId(1) - ; + ->setRelatedEntityClass(stdClass::class) + ->setRelatedEntityId(1); // force an id to entityWorkflow: - $reflection = new \ReflectionClass($entityWorkflow); + $reflection = new ReflectionClass($entityWorkflow); $id = $reflection->getProperty('id'); $id->setAccessible(true); $id->setValue($entityWorkflow, 1); @@ -48,12 +62,11 @@ class NotificationOnTransitionTest extends TestCase $step = new EntityWorkflowStep(); $entityWorkflow->addStep($step); $step->addDestUser($dest) - ->setCurrentStep('to_state') - ; + ->setCurrentStep('to_state'); $em = $this->prophesize(EntityManagerInterface::class); $em->persist(Argument::type(Notification::class))->should( - function($args) use ($dest) { + static function ($args) use ($dest) { /** @var Call[] $args */ if (1 !== count($args)) { throw new FailedPredictionException('no notification sent'); @@ -68,7 +81,8 @@ class NotificationOnTransitionTest extends TestCase if (!$notification->getAddressees()->contains($dest)) { throw new FailedPredictionException('the dest is not notified'); } - }); + } + ); $engine = $this->prophesize(EngineInterface::class); $engine->render(Argument::type('string'), Argument::type('array')) diff --git a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php index 2510f9460..0f6a4799a 100644 --- a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php +++ b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/EntityWorkflowTransitionEventSubscriber.php @@ -41,6 +41,24 @@ class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterfac $this->userRender = $userRender; } + public function addDests(Event $event): void + { + if (!$event->getSubject() instanceof EntityWorkflow) { + return; + } + + /** @var EntityWorkflow $entityWorkflow */ + $entityWorkflow = $event->getSubject(); + + foreach ($entityWorkflow->futureDestUsers as $user) { + $entityWorkflow->getCurrentStep()->addDestUser($user); + } + + foreach ($entityWorkflow->futureDestEmails as $email) { + $entityWorkflow->getCurrentStep()->addDestEmail($email); + } + } + public static function getSubscribedEvents(): array { return [ @@ -55,23 +73,6 @@ class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterfac ]; } - public function addDests(Event $event): void - { - if (!$event->getSubject() instanceof EntityWorkflow) { - return; - } - - /** @var EntityWorkflow $entityWorkflow */ - $entityWorkflow = $event->getSubject(); - foreach ($entityWorkflow->futureDestUsers as $user) { - $entityWorkflow->getCurrentStep()->addDestUser($user); - } - - foreach ($entityWorkflow->futureDestEmails as $email) { - $entityWorkflow->getCurrentStep()->addDestEmail($email); - } - } - public function guardEntityWorkflow(GuardEvent $event) { if (!$event->getSubject() instanceof EntityWorkflow) { diff --git a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php index 982a7024e..c56dc34ce 100644 --- a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php +++ b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php @@ -57,14 +57,15 @@ class NotificationOnTransition implements EventSubscriberInterface } /** - * Send a notification to: + * Send a notification to:. * * * the dests of the new step; * * the users which subscribed to workflow, on each step, or on final * * **Warning** take care that this method must be executed **after** the dest users are added to - * the step (@link{EntityWorkflowStep::addDestUser}). Currently, this is done during - * @link{EntityWorkflowTransitionEventSubscriber::addDests}. + * the step (@see{EntityWorkflowStep::addDestUser}). Currently, this is done during + * + * @see{EntityWorkflowTransitionEventSubscriber::addDests}. */ public function onCompletedSendNotification(Event $event): void { @@ -77,6 +78,7 @@ class NotificationOnTransition implements EventSubscriberInterface /** @var array $dests array of unique values, where keys is the object's hash */ $dests = []; + foreach (array_merge( // the subscriber to each step $entityWorkflow->getSubscriberToStep()->toArray(), diff --git a/src/Bundle/ChillMainBundle/migrations/Version20220730204216.php b/src/Bundle/ChillMainBundle/migrations/Version20220730204216.php index 4ed1b2918..e4ad98318 100644 --- a/src/Bundle/ChillMainBundle/migrations/Version20220730204216.php +++ b/src/Bundle/ChillMainBundle/migrations/Version20220730204216.php @@ -1,5 +1,12 @@ addSql('DROP INDEX chill_main_address_reference_unicity'); + } + public function getDescription(): string { return 'Add an unique constraint on addresses references'; @@ -18,9 +30,4 @@ final class Version20220730204216 extends AbstractMigration { $this->addSql('CREATE UNIQUE INDEX chill_main_address_reference_unicity ON chill_main_address_reference (refId, source)'); } - - public function down(Schema $schema): void - { - $this->addSql('DROP INDEX chill_main_address_reference_unicity'); - } } diff --git a/src/Bundle/ChillMainBundle/migrations/Version20220913174922.php b/src/Bundle/ChillMainBundle/migrations/Version20220913174922.php index cabc7f1f8..f00cb5aa1 100644 --- a/src/Bundle/ChillMainBundle/migrations/Version20220913174922.php +++ b/src/Bundle/ChillMainBundle/migrations/Version20220913174922.php @@ -1,5 +1,12 @@ addSql('ALTER TABLE chill_main_geographical_unit ALTER COLUMN geom SET DATA TYPE TEXT'); + } + public function getDescription(): string { return 'Geographical Unit correction'; @@ -21,9 +33,4 @@ final class Version20220913174922 extends AbstractMigration { $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER COLUMN geom SET DATA TYPE GEOMETRY(MULTIPOLYGON, 4326)'); } - - public function down(Schema $schema): void - { - $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER COLUMN geom SET DATA TYPE TEXT'); - } } diff --git a/src/Bundle/ChillMainBundle/migrations/Version20221003112151.php b/src/Bundle/ChillMainBundle/migrations/Version20221003112151.php index 36b1834ea..b9753ce8c 100644 --- a/src/Bundle/ChillMainBundle/migrations/Version20221003112151.php +++ b/src/Bundle/ChillMainBundle/migrations/Version20221003112151.php @@ -1,5 +1,12 @@ addSql('CREATE SEQUENCE chill_main_geographical_unit_layer_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); - $this->addSql('CREATE TABLE chill_main_geographical_unit_layer (id INT NOT NULL, name JSONB DEFAULT \'[]\'::jsonb NOT NULL, refid TEXT DEFAULT \'\' NOT NULL, PRIMARY KEY(id))'); - $this->addSql("COMMENT ON COLUMN chill_main_geographical_unit_layer.name IS '(DC2Type:json)';"); - - $this->addSql('INSERT INTO chill_main_geographical_unit_layer (id, name, refid) - SELECT DISTINCT nextval(\'chill_main_geographical_unit_layer_id_seq\'), jsonb_build_object(\'fr\', layername), layername FROM chill_main_geographical_unit'); - - $this->addSql('ALTER TABLE chill_main_geographical_unit ADD layer_id INT DEFAULT NULL'); - - $this->addSql('UPDATE chill_main_geographical_unit SET layer_id = layer.id FROM chill_main_geographical_unit_layer AS layer WHERE layer.refid = chill_main_geographical_unit.layername'); - - $this->addSql('ALTER TABLE chill_main_geographical_unit ADD unitRefId TEXT DEFAULT \'\' NOT NULL'); - $this->addSql('ALTER TABLE chill_main_geographical_unit DROP layername'); - $this->addSql("COMMENT ON COLUMN chill_main_geographical_unit.geom IS '(DC2Type:text)';"); - $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitname TYPE TEXT'); - $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitname SET DEFAULT \'\''); - $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitname SET NOT NULL'); - $this->addSql('ALTER TABLE chill_main_geographical_unit ADD CONSTRAINT FK_360A2B2FEA6EFDCD FOREIGN KEY (layer_id) REFERENCES chill_main_geographical_unit_layer (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); - $this->addSql('CREATE INDEX IDX_360A2B2FEA6EFDCD ON chill_main_geographical_unit (layer_id)'); - - } - public function down(Schema $schema): void { $this->throwIrreversibleMigrationException(); @@ -53,6 +31,34 @@ final class Version20221003112151 extends AbstractMigration $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitName TYPE VARCHAR(255)'); $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitName DROP DEFAULT'); $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitName DROP NOT NULL'); - */ + */ + } + + public function getDescription(): string + { + return 'Add a proper entity for GeographicalUnitLayer'; + } + + public function up(Schema $schema): void + { + $this->addSql('CREATE SEQUENCE chill_main_geographical_unit_layer_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); + $this->addSql('CREATE TABLE chill_main_geographical_unit_layer (id INT NOT NULL, name JSONB DEFAULT \'[]\'::jsonb NOT NULL, refid TEXT DEFAULT \'\' NOT NULL, PRIMARY KEY(id))'); + $this->addSql("COMMENT ON COLUMN chill_main_geographical_unit_layer.name IS '(DC2Type:json)';"); + + $this->addSql('INSERT INTO chill_main_geographical_unit_layer (id, name, refid) + SELECT DISTINCT nextval(\'chill_main_geographical_unit_layer_id_seq\'), jsonb_build_object(\'fr\', layername), layername FROM chill_main_geographical_unit'); + + $this->addSql('ALTER TABLE chill_main_geographical_unit ADD layer_id INT DEFAULT NULL'); + + $this->addSql('UPDATE chill_main_geographical_unit SET layer_id = layer.id FROM chill_main_geographical_unit_layer AS layer WHERE layer.refid = chill_main_geographical_unit.layername'); + + $this->addSql('ALTER TABLE chill_main_geographical_unit ADD unitRefId TEXT DEFAULT \'\' NOT NULL'); + $this->addSql('ALTER TABLE chill_main_geographical_unit DROP layername'); + $this->addSql("COMMENT ON COLUMN chill_main_geographical_unit.geom IS '(DC2Type:text)';"); + $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitname TYPE TEXT'); + $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitname SET DEFAULT \'\''); + $this->addSql('ALTER TABLE chill_main_geographical_unit ALTER unitname SET NOT NULL'); + $this->addSql('ALTER TABLE chill_main_geographical_unit ADD CONSTRAINT FK_360A2B2FEA6EFDCD FOREIGN KEY (layer_id) REFERENCES chill_main_geographical_unit_layer (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX IDX_360A2B2FEA6EFDCD ON chill_main_geographical_unit (layer_id)'); } } diff --git a/src/Bundle/ChillMainBundle/migrations/Version20221003132620.php b/src/Bundle/ChillMainBundle/migrations/Version20221003132620.php index e07885dce..8186ce7ed 100644 --- a/src/Bundle/ChillMainBundle/migrations/Version20221003132620.php +++ b/src/Bundle/ChillMainBundle/migrations/Version20221003132620.php @@ -1,5 +1,12 @@ addSql('DROP INDEX geographical_unit_layer_refid'); + $this->addSql('DROP INDEX geographical_unit_refid'); + $this->addSql('DROP INDEX chill_internal_geographical_unit_layer_geom_idx'); + } + public function getDescription(): string { return 'Create indexes and unique constraints on geographical unit entities'; @@ -20,11 +34,4 @@ final class Version20221003132620 extends AbstractMigration $this->addSql('CREATE UNIQUE INDEX geographical_unit_refid ON chill_main_geographical_unit (layer_id, unitRefId)'); $this->addSql('CREATE INDEX chill_internal_geographical_unit_layer_geom_idx ON chill_main_geographical_unit USING GIST (layer_id, geom)'); } - - public function down(Schema $schema): void - { - $this->addSql('DROP INDEX geographical_unit_layer_refid'); - $this->addSql('DROP INDEX geographical_unit_refid'); - $this->addSql('DROP INDEX chill_internal_geographical_unit_layer_geom_idx'); - } } diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index dc311ad07..731b41905 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -46,6 +46,7 @@ use libphonenumber\PhoneNumber; use Symfony\Component\Serializer\Annotation\DiscriminatorMap; use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Context\ExecutionContextInterface; +use UnexpectedValueException; use function count; use function in_array; @@ -182,21 +183,23 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI * The person's center. * * @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Center") + * * @deprecated */ private ?Center $center = null; - /** - * @ORM\OneToMany(targetEntity=PersonCenterHistory::class, mappedBy="person", cascade={"persist"}) - * @var Collection|PersonCenterHistory[] - */ - private Collection $centerHistory; - /** * @ORM\OneToOne(targetEntity=PersonCenterCurrent::class, mappedBy="person") */ private ?PersonCenterCurrent $centerCurrent = null; + /** + * @ORM\OneToMany(targetEntity=PersonCenterHistory::class, mappedBy="person", cascade={"persist"}) + * + * @var Collection|PersonCenterHistory[] + */ + private Collection $centerHistory; + /** * Array where customfield's data are stored. * @@ -910,32 +913,6 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI return $this->budgetResources; } - private function getCurrentCenterHistory(): ?PersonCenterHistory - { - if (0 === $this->centerHistory->count()) { - return null; - } - - $criteria = Criteria::create(); - $now = new DateTimeImmutable('now'); - $criteria->where(Criteria::expr()->lte('startDate', $now)) - ->andWhere(Criteria::expr()->orX( - Criteria::expr()->isNull('endDate'), - Criteria::expr()->gt('endDate', $now) - )); - - $histories = $this->centerHistory->matching($criteria); - - switch ($histories->count()) { - case 0: - return null; - case 1: - return $histories->first(); - default: - throw new \UnexpectedValueException('It should not contains more than one center at a time'); - } - } - public function getCenter(): ?Center { if (null !== $this->centerCurrent) { @@ -949,6 +926,24 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI return $currentCenterHistory->getCenter(); } + public function getCenterCurrent(): ?PersonCenterCurrent + { + if (null !== $this->centerCurrent) { + return $this->centerCurrent; + } + + if (null === $currentCenterHistory = $this->getCurrentCenterHistory()) { + return null; + } + + return new PersonCenterCurrent($currentCenterHistory); + } + + public function getCenterHistory(): Collection + { + return $this->centerHistory; + } + public function getCFData(): ?array { if (null === $this->cFData) { @@ -1562,7 +1557,6 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI /** * Associate the center with the person. The association start on 'now'. * - * @param Center $center * @return $this */ public function setCenter(Center $center): self @@ -1582,40 +1576,13 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI return $this; } - /** - * @return Collection - */ - public function getCenterHistory(): Collection - { - return $this->centerHistory; - } - - /** - * @param Collection $centerHistory - * @return Person - */ public function setCenterHistory(Collection $centerHistory): Person { $this->centerHistory = $centerHistory; + return $this; } - /** - * @return PersonCenterCurrent|null - */ - public function getCenterCurrent(): ?PersonCenterCurrent - { - if (null !== $this->centerCurrent) { - return $this->centerCurrent; - } - - if (null === $currentCenterHistory = $this->getCurrentCenterHistory()) { - return null; - } - - return new PersonCenterCurrent($currentCenterHistory); - } - /** * @return Person */ @@ -1818,6 +1785,34 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI return $this; } + private function getCurrentCenterHistory(): ?PersonCenterHistory + { + if (0 === $this->centerHistory->count()) { + return null; + } + + $criteria = Criteria::create(); + $now = new DateTimeImmutable('now'); + $criteria->where(Criteria::expr()->lte('startDate', $now)) + ->andWhere(Criteria::expr()->orX( + Criteria::expr()->isNull('endDate'), + Criteria::expr()->gt('endDate', $now) + )); + + $histories = $this->centerHistory->matching($criteria); + + switch ($histories->count()) { + case 0: + return null; + + case 1: + return $histories->first(); + + default: + throw new UnexpectedValueException('It should not contains more than one center at a time'); + } + } + /** * This private function scan accompanyingPeriodParticipations Collection, * searching for a given AccompanyingPeriod. diff --git a/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterCurrent.php b/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterCurrent.php index 690ff025e..30de1f55a 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterCurrent.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterCurrent.php @@ -1,15 +1,19 @@ id = $history->getId(); } + public function getCenter(): Center + { + return $this->center; + } + + public function getEndDate(): ?DateTimeImmutable + { + return $this->endDate; + } + /** - * The id will be the same as the current @link{PersonCenterHistory::class} - * - * @return int + * The id will be the same as the current @see{PersonCenterHistory::class}. */ public function getId(): int { return $this->id; } - /** - * @return Person - */ public function getPerson(): Person { return $this->person; } - /** - * @return Center - */ - public function getCenter(): Center - { - return $this->center; - } - - /** - * @return \DateTimeImmutable - */ - public function getStartDate(): \DateTimeImmutable + public function getStartDate(): DateTimeImmutable { return $this->startDate; } - - /** - * @return \DateTimeImmutable|null - */ - public function getEndDate(): ?\DateTimeImmutable - { - return $this->endDate; - } - } diff --git a/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterHistory.php b/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterHistory.php index 6bfb28cc6..a9ec61251 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterHistory.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person/PersonCenterHistory.php @@ -1,5 +1,14 @@ person = $person; $this->center = $center; $this->startDate = $startDate; } - - /** - * @return int|null - */ - public function getId(): ?int - { - return $this->id; - } - - /** - * @return Person|null - */ - public function getPerson(): ?Person - { - return $this->person; - } - - /** - * @param Person|null $person - */ - public function setPerson(?Person $person): self - { - $this->person = $person; - - return $this; - } - - /** - * @return Center|null - */ public function getCenter(): ?Center { return $this->center; } - /** - * @param Center|null $center - */ + public function getEndDate(): ?DateTimeImmutable + { + return $this->endDate; + } + + public function getId(): ?int + { + return $this->id; + } + + public function getPerson(): ?Person + { + return $this->person; + } + + public function getStartDate(): ?DateTimeImmutable + { + return $this->startDate; + } + public function setCenter(?Center $center): self { $this->center = $center; @@ -105,38 +98,24 @@ class PersonCenterHistory implements TrackCreationInterface, TrackUpdateInterfac return $this; } - /** - * @return \DateTimeImmutable|null - */ - public function getStartDate(): ?\DateTimeImmutable - { - return $this->startDate; - } - - /** - * @param \DateTimeImmutable|null $startDate - */ - public function setStartDate(?\DateTimeImmutable $startDate): self - { - $this->startDate = $startDate; - return $this; - } - - /** - * @return \DateTimeImmutable|null - */ - public function getEndDate(): ?\DateTimeImmutable - { - return $this->endDate; - } - - /** - * @param \DateTimeImmutable|null $endDate - */ - public function setEndDate(?\DateTimeImmutable $endDate): self + public function setEndDate(?DateTimeImmutable $endDate): self { $this->endDate = $endDate; return $this; } + + public function setPerson(?Person $person): self + { + $this->person = $person; + + return $this; + } + + public function setStartDate(?DateTimeImmutable $startDate): self + { + $this->startDate = $startDate; + + return $this; + } } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregator.php index c0d20e7c7..b51b31c60 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregator.php @@ -17,6 +17,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; class AdministrativeLocationAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregator.php index de276f007..de3075747 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregator.php @@ -13,11 +13,8 @@ namespace Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators; use Chill\MainBundle\Export\AggregatorInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; -use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive; use Chill\PersonBundle\Export\Declarations; -use Chill\PersonBundle\Repository\AccompanyingPeriod\ClosingMotiveRepository; use Chill\PersonBundle\Repository\AccompanyingPeriod\ClosingMotiveRepositoryInterface; -use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; @@ -63,7 +60,7 @@ class ClosingMotiveAggregator implements AggregatorInterface return 'Closing motive'; } - if (NULL === $value) { + if (null === $value) { return ''; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregator.php index 9f1ff1ba4..884b9c8e9 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregator.php @@ -1,5 +1,14 @@ translatableStringHelper = $translatableStringHelper; } - /** - * @inheritDoc - */ - public function getLabels($key, array $values, $data) - { - switch ($key) { - case 'acp_geog_agg_unitname': - return function ($value): string { - if ('_header' === $value) { - return 'acp_geog_agg_unitname'; - } - - if (null === $value) { - return ''; - } - - 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'); - } - } - - /** - * @inheritDoc - */ - public function getQueryKeys($data): array - { - return ['acp_geog_agg_unitname', 'acp_geog_agg_unitrefid']; - } - - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - $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()); - }, - ]); - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Group by geographical unit'; - } - - /** - * @inheritDoc - */ public function addRole(): ?string { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('acp_geog_agg_location_history', $qb->getAllAliases(), true)) { @@ -181,15 +111,75 @@ final class GeographicalUnitStatAggregator implements AggregatorInterface ->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') - ; + ->addGroupBy('acp_geog_agg_unitrefid'); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::ACP_TYPE; } + + public function buildForm(FormBuilderInterface $builder) + { + $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()); + }, + ]); + } + + public function getLabels($key, array $values, $data) + { + switch ($key) { + case 'acp_geog_agg_unitname': + return static function ($value): string { + if ('_header' === $value) { + return 'acp_geog_agg_unitname'; + } + + if (null === $value) { + return ''; + } + + return $value; + }; + + case 'acp_geog_agg_unitrefid': + return static 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'); + } + } + + public function getQueryKeys($data): array + { + return ['acp_geog_agg_unitname', 'acp_geog_agg_unitrefid']; + } + + public function getTitle(): string + { + return 'Group by geographical unit'; + } } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php index 0809f7654..df48d61af 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/JobAggregator.php @@ -17,6 +17,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class JobAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregator.php index c7a178fa3..e9c2ef878 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregator.php @@ -19,6 +19,7 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class OriginAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php index 7851fe203..26e724526 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregator.php @@ -17,6 +17,7 @@ use Chill\MainBundle\Templating\Entity\UserRender; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class ReferrerAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregator.php index 3701371a7..b56f70614 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregator.php @@ -1,25 +1,34 @@ translatableStringHelper = $translatableStringHelper; } - /** - * @inheritDoc - */ + public function addRole(): ?string + { + return null; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + $userHistory = 'acp_agg_refscope_user_history'; + $ref = 'acp_agg_refscope_user_history_ref'; + $scopeName = self::SCOPE_KEY; + $dateCalc = 'acp_agg_refscope_user_history_date_calc'; + + $qb + ->leftJoin('acp.userHistories', $userHistory) + ->leftJoin($userHistory . '.user', $ref) + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull($userHistory), + $qb->expr()->andX( + $qb->expr()->lte($userHistory . '.startDate', ':' . $dateCalc), + $qb->expr()->orX( + $qb->expr()->isNull($userHistory . '.endDate'), + $qb->expr()->lt($userHistory . '.endDate', ':' . $dateCalc) + ) + ) + ) + ) + ->setParameter($dateCalc, $data['date_calc']); + + // add groups + $qb + ->addSelect('IDENTITY(' . $ref . '.mainScope) AS ' . $scopeName) + ->addGroupBy($scopeName); + } + + public function applyOn() + { + return Declarations::ACP_TYPE; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder->add('date_calc', ChillDateType::class, [ + 'input' => 'datetime_immutable', + 'data' => new DateTimeImmutable('now'), + 'label' => 'export.aggregator.course.by_user_scope.Computation date for referrer', + 'required' => true, + ]); + } + public function getLabels($key, array $values, $data) { return function ($value) { @@ -45,89 +101,20 @@ class ReferrerScopeAggregator implements AggregatorInterface $scope = $this->scopeRepository->find($value); if (null === $scope) { - throw new \LogicException('no scope found with this id: ' . $value); + throw new LogicException('no scope found with this id: ' . $value); } return $this->translatableStringHelper->localize($scope->getName()); }; } - /** - * @inheritDoc - */ public function getQueryKeys($data) { return [self::SCOPE_KEY]; } - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - $builder->add('date_calc', ChillDateType::class, [ - 'input' => 'datetime_immutable', - 'data' => new \DateTimeImmutable('now'), - 'label' => 'export.aggregator.course.by_user_scope.Computation date for referrer', - 'required' => true, - ]); - } - - /** - * @inheritDoc - */ public function getTitle() { return 'export.aggregator.course.by_user_scope.Group course by referrer\'s scope'; } - - /** - * @inheritDoc - */ - public function addRole(): ?string - { - return null; - } - - /** - * @inheritDoc - */ - public function alterQuery(QueryBuilder $qb, $data) - { - $userHistory = 'acp_agg_refscope_user_history'; - $ref = 'acp_agg_refscope_user_history_ref'; - $scopeName = self::SCOPE_KEY; - $dateCalc = 'acp_agg_refscope_user_history_date_calc'; - - $qb - ->leftJoin('acp.userHistories', $userHistory) - ->leftJoin($userHistory.'.user', $ref) - ->andWhere( - $qb->expr()->orX( - $qb->expr()->isNull($userHistory), - $qb->expr()->andX( - $qb->expr()->lte($userHistory.'.startDate', ':'.$dateCalc), - $qb->expr()->orX( - $qb->expr()->isNull($userHistory.'.endDate'), - $qb->expr()->lt($userHistory.'.endDate', ':'.$dateCalc) - ) - ) - ) - ) - ->setParameter($dateCalc, $data['date_calc']); - - // add groups - $qb - ->addSelect('IDENTITY('.$ref.'.mainScope) AS '.$scopeName) - ->addGroupBy($scopeName) - ; - } - - /** - * @inheritDoc - */ - public function applyOn() - { - return Declarations::ACP_TYPE; - } } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregator.php index 77eb06835..f711cf9ef 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregator.php @@ -17,6 +17,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class ScopeAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregator.php index 98fdbad0c..e013f94d3 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregator.php @@ -17,6 +17,7 @@ use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository; use Chill\PersonBundle\Templating\Entity\SocialIssueRender; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class SocialIssueAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php index 2a1a3db25..809bb9645 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/StepAggregator.php @@ -63,7 +63,7 @@ final class StepAggregator implements AggregatorInterface //, FilterInterface $qb->add('where', $where); $qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE); - */ + */ } public function applyOn(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregator.php index 2343b2bab..d51575d6e 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregator.php @@ -17,7 +17,6 @@ use Chill\PersonBundle\Export\Declarations; use DateTime; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Query\Expr; -use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; @@ -73,7 +72,7 @@ class ChildrenNumberAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return 'Number of children'; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php index 7e10ea429..6e180934b 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/HouseholdAggregators/CompositionAggregator.php @@ -19,7 +19,6 @@ use Chill\PersonBundle\Repository\Household\HouseholdCompositionTypeRepository; use DateTime; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Query\Expr; -use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use function in_array; diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GeographicalUnitAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GeographicalUnitAggregator.php index 816666e2f..feccafe50 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GeographicalUnitAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/GeographicalUnitAggregator.php @@ -1,5 +1,14 @@ translatableStringHelper = $translatableStringHelper; } - /** - * @inheritDoc - */ - public function getLabels($key, array $values, $data) - { - switch ($key) { - case 'geog_unit_name': - return function ($value): string { - if ('_header' === $value) { - return 'acp_geog_agg_unitname'; - } - - if (null === $value) { - return ''; - } - - return $value; - }; - - case 'geog_unit_key': - return function ($value): string { - if ('_header' === $value) { - return 'acp_geog_agg_unitrefid'; - } - - if (null === $value) { - return ''; - } - - return $value; - }; - - default: - throw new \LogicException('key not supported'); - } - } - - /** - * @inheritDoc - */ - public function getQueryKeys($data) - { - return ['geog_unit_name', 'geog_unit_key']; - } - - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - $builder - ->add('date_calc', ChillDateType::class, [ - 'label' => 'Address valid at this 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()); - }, - ]); - } - - /** - * @inheritDoc - */ - public function getTitle() - { - return 'Group people by geographical unit based on his address'; - } - - /** - * @inheritDoc - */ public function addRole(): ?string { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data): void { $qb @@ -144,20 +73,80 @@ class GeographicalUnitAggregator implements AggregatorInterface ->addSelect('person_geog_agg_geog_unit.unitName AS geog_unit_name') ->addSelect('person_geog_agg_geog_unit.unitRefId AS geog_unit_key') ->addGroupBy('geog_unit_name') - ->addGroupBy('geog_unit_key') - ; + ->addGroupBy('geog_unit_key'); } - /** - * @inheritDoc - */ public function applyOn() { return Declarations::PERSON_TYPE; } + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('date_calc', ChillDateType::class, [ + 'label' => 'Address valid at this 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()); + }, + ]); + } + public static function getDefaultAlias(): string { return 'person_geog_agg'; } + + public function getLabels($key, array $values, $data) + { + switch ($key) { + case 'geog_unit_name': + return static function ($value): string { + if ('_header' === $value) { + return 'acp_geog_agg_unitname'; + } + + if (null === $value) { + return ''; + } + + return $value; + }; + + case 'geog_unit_key': + return static function ($value): string { + if ('_header' === $value) { + return 'acp_geog_agg_unitrefid'; + } + + if (null === $value) { + return ''; + } + + return $value; + }; + + default: + throw new LogicException('key not supported'); + } + } + + public function getQueryKeys($data) + { + return ['geog_unit_name', 'geog_unit_key']; + } + + public function getTitle() + { + return 'Group people by geographical unit based on his address'; + } } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php index 89c0bb8f5..1f902357e 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php @@ -24,6 +24,7 @@ use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Contracts\Translation\TranslatorInterface; +use function in_array; final class HouseholdPositionAggregator implements AggregatorInterface, ExportElementValidatedInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/MaritalStatusAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/MaritalStatusAggregator.php index 2ed1752b0..333523487 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/MaritalStatusAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/MaritalStatusAggregator.php @@ -17,6 +17,7 @@ use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Repository\MaritalStatusRepository; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class MaritalStatusAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php index fc96b9e6b..38fd40071 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregator.php @@ -17,6 +17,7 @@ use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository; use Chill\PersonBundle\Templating\Entity\SocialActionRender; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class ActionTypeAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php index 0f418a678..353602db9 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalAggregator.php @@ -17,6 +17,7 @@ use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Repository\SocialWork\GoalRepository; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class GoalAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalResultAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalResultAggregator.php index 48075c38b..97745f576 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalResultAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/GoalResultAggregator.php @@ -1,5 +1,14 @@ translatableStringHelper = $translatableStringHelper; } - - /** - * @inheritDoc - */ - public function getLabels($key, array $values, $data) - { - return function ($value) use ($key): string { - if (null === $value) { - return ''; - } - - switch ($key) { - case 'goal_aggregator': - - if ('_header' === $value) { - return 'Goal Type'; - } - - $g = $this->goalRepository->find($value); - - return $this->translatableStringHelper->localize( - $g->getTitle() - ); - - case 'result_aggregator': - - if ('_header' === $value) { - return 'Result Type'; - } - - $r = $this->resultRepository->find($value); - - return $this->translatableStringHelper->localize( - $r->getTitle() - ); - - default: - throw new \LogicException(); - } - }; - } - - /** - * @inheritDoc - */ - public function getQueryKeys($data): array - { - return [ - 'goal_aggregator', - 'result_aggregator' - ]; - } - - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - // no form - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Group social work actions by goal and result'; - } - - /** - * @inheritDoc - */ public function addRole(): ?string { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { if (!in_array('goal', $qb->getAllAliases(), true)) { @@ -123,11 +59,62 @@ class GoalResultAggregator implements AggregatorInterface $qb->addGroupBy('goal_aggregator')->addGroupBy('result_aggregator'); } - /** - * @inheritDoc - */ public function applyOn(): string { return Declarations::SOCIAL_WORK_ACTION_TYPE; } + + public function buildForm(FormBuilderInterface $builder) + { + // no form + } + + public function getLabels($key, array $values, $data) + { + return function ($value) use ($key): string { + if (null === $value) { + return ''; + } + + switch ($key) { + case 'goal_aggregator': + if ('_header' === $value) { + return 'Goal Type'; + } + + $g = $this->goalRepository->find($value); + + return $this->translatableStringHelper->localize( + $g->getTitle() + ); + + case 'result_aggregator': + if ('_header' === $value) { + return 'Result Type'; + } + + $r = $this->resultRepository->find($value); + + return $this->translatableStringHelper->localize( + $r->getTitle() + ); + + default: + throw new LogicException(); + } + }; + } + + public function getQueryKeys($data): array + { + return [ + 'goal_aggregator', + 'result_aggregator', + ]; + } + + public function getTitle(): string + { + return 'Group social work actions by goal and result'; + } } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/JobAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/JobAggregator.php index ca2e36d97..c95f4bf50 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/JobAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/JobAggregator.php @@ -17,6 +17,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class JobAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ReferrerAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ReferrerAggregator.php index 46ef78035..33469f719 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ReferrerAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ReferrerAggregator.php @@ -17,6 +17,7 @@ use Chill\MainBundle\Templating\Entity\UserRender; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class ReferrerAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php index 4b190e0a5..215c51c3c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ResultAggregator.php @@ -17,6 +17,7 @@ use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Repository\SocialWork\ResultRepository; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class ResultAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ScopeAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ScopeAggregator.php index 1cf29d46f..9db70af91 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ScopeAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/ScopeAggregator.php @@ -17,6 +17,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; final class ScopeAggregator implements AggregatorInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php index 02682c3a9..f3995a335 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php @@ -101,14 +101,13 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface $qb ->andWhere( $qb->expr()->exists( - 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part - JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) ' ) ) - ->setParameter('authorized_centers', $centers) - ; + ->setParameter('authorized_centers', $centers); $qb->select('COUNT(DISTINCT acp.id) AS export_result'); diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php b/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php index 7ff5349f6..544734055 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountEvaluation.php @@ -23,6 +23,7 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; class CountEvaluation implements ExportInterface, GroupedExportInterface { @@ -107,14 +108,13 @@ class CountEvaluation implements ExportInterface, GroupedExportInterface $qb ->andWhere( $qb->expr()->exists( - 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part - JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) ' ) ) - ->setParameter('authorized_centers', $centers) - ; + ->setParameter('authorized_centers', $centers); $qb->select('COUNT(DISTINCT workeval.id) AS export_result'); diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php index e1c7adf88..2b358634f 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountHousehold.php @@ -100,20 +100,18 @@ class CountHousehold implements ExportInterface, GroupedExportInterface ->join('acp.participations', 'acppart') // little optimization: we remove joins and make a direct join between participations and household members ->join(HouseholdMember::class, 'member', Query\Expr\Join::WITH, 'IDENTITY(acppart.person) = IDENTITY(member.person)') - ->join('member.household', 'household') - ; + ->join('member.household', 'household'); $qb ->andWhere( $qb->expr()->exists( - 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part - JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) ' ) ) - ->setParameter('authorized_centers', $centers) - ; + ->setParameter('authorized_centers', $centers); $qb->select('COUNT(DISTINCT household.id) AS export_result'); diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php index 3b2e47bdd..c411fad2e 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountPerson.php @@ -18,7 +18,6 @@ use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Repository\PersonRepository; use Chill\PersonBundle\Security\Authorization\PersonVoter; -use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query; use Doctrine\ORM\QueryBuilder; use LogicException; @@ -104,10 +103,10 @@ class CountPerson implements ExportInterface, GroupedExportInterface $qb->select('COUNT(person.id) AS export_result') ->andWhere( $qb->expr()->exists( - 'SELECT 1 FROM '.PersonCenterHistory::class.' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)' + 'SELECT 1 FROM ' . PersonCenterHistory::class . ' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)' ) ) - ->setParameter('authorized_centers', $centers); + ->setParameter('authorized_centers', $centers); return $qb; } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php b/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php index fa28e2abe..df48fe7c6 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountPersonWithAccompanyingCourse.php @@ -22,6 +22,7 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; class CountPersonWithAccompanyingCourse implements ExportInterface, GroupedExportInterface { @@ -105,7 +106,7 @@ class CountPersonWithAccompanyingCourse implements ExportInterface, GroupedExpor $qb->andWhere( $qb->expr()->exists( - 'SELECT 1 FROM '.PersonCenterHistory::class.' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)' + 'SELECT 1 FROM ' . PersonCenterHistory::class . ' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)' ) )->setParameter('authorized_centers', $centers); diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php b/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php index 4ba071d3b..caf6cdbc2 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php @@ -102,14 +102,13 @@ class CountSocialWorkActions implements ExportInterface, GroupedExportInterface $qb ->andWhere( $qb->expr()->exists( - 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part - JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) ' ) ) - ->setParameter('authorized_centers', $centers) - ; + ->setParameter('authorized_centers', $centers); $qb->select('COUNT(DISTINCT acpw.id) as export_result'); diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonDuplicate.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonDuplicate.php index 75d757f9e..543be933c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonDuplicate.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonDuplicate.php @@ -24,7 +24,6 @@ use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\ResponseHeaderBag; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Contracts\Translation\TranslatorInterface; diff --git a/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php b/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php index 3b2036797..f59be8399 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/StatAccompanyingCourseDuration.php @@ -105,14 +105,13 @@ class StatAccompanyingCourseDuration implements ExportInterface, GroupedExportIn $qb ->andWhere( $qb->expr()->exists( - 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part - JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) ' ) ) - ->setParameter('authorized_centers', $centers) - ; + ->setParameter('authorized_centers', $centers); $qb ->select('AVG( diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilter.php index ae9535a67..3ec26183c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilter.php @@ -16,7 +16,6 @@ use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Export\Declarations; use DateTime; use Doctrine\DBAL\Types\Types; -use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilter.php index 569146614..8e164d668 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CurrentUserScopeFilter.php @@ -20,6 +20,7 @@ use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Security\Core\Security; +use function in_array; class CurrentUserScopeFilter implements FilterInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EvaluationFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EvaluationFilter.php index c04d5bcd0..3b95261db 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EvaluationFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/EvaluationFilter.php @@ -19,6 +19,7 @@ use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; class EvaluationFilter implements FilterInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilter.php index 5b2478f19..f720896c4 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilter.php @@ -20,27 +20,23 @@ use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress; use Chill\PersonBundle\Export\Declarations; -use DateTime; -use Doctrine\DBAL\Types\Types; +use DateTimeImmutable; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\Query\Expr; -use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; /** - * Filter accompanying period by geographical zone + * Filter accompanying period by geographical zone. */ class GeographicalUnitStatFilter implements FilterInterface { + private EntityManagerInterface $em; private GeographicalUnitRepositoryInterface $geographicalUnitRepository; private TranslatableStringHelperInterface $translatableStringHelper; - private EntityManagerInterface $em; - public function __construct( EntityManagerInterface $em, GeographicalUnitRepositoryInterface $geographicalUnitRepository, @@ -61,12 +57,12 @@ class GeographicalUnitStatFilter implements FilterInterface $subQueryDql = 'SELECT 1 - FROM '.AccompanyingPeriod\AccompanyingPeriodLocationHistory::class.' acp_geog_filter_location_history - LEFT JOIN '.PersonHouseholdAddress::class.' acp_geog_filter_address_person_location + 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 + 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 + 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 @@ -82,8 +78,7 @@ class GeographicalUnitStatFilter implements FilterInterface $qb ->andWhere($qb->expr()->exists($subQueryDql)) ->setParameter('acp_geog_filter_date', $data['date_calc']) - ->setParameter('acp_geog_filter_units', $data['units']) - ; + ->setParameter('acp_geog_filter_units', $data['units']); } public function applyOn(): string @@ -97,7 +92,7 @@ class GeographicalUnitStatFilter implements FilterInterface ->add('date_calc', ChillDateType::class, [ 'label' => 'Compute geographical location at date', 'required' => true, - 'data' => new \DateTimeImmutable('today'), + 'data' => new DateTimeImmutable('today'), 'input' => 'datetime_immutable', ]) ->add('units', EntityType::class, [ @@ -105,7 +100,7 @@ class GeographicalUnitStatFilter implements FilterInterface 'placeholder' => 'Select a geographical unit', 'class' => GeographicalUnit::class, 'choices' => $this->geographicalUnitRepository->findAll(), - 'choice_label' => function(GeographicalUnit $item) { + 'choice_label' => function (GeographicalUnit $item) { return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName(); }, 'attr' => [ @@ -122,12 +117,12 @@ class GeographicalUnitStatFilter implements FilterInterface '%units' => implode( ', ', array_map( - function(GeographicalUnit $item) { + function (GeographicalUnit $item) { return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName(); }, $data['units'] ) - ) + ), ]]; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php index b47f2020d..814819a57 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilter.php @@ -16,7 +16,6 @@ use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Export\Declarations; use DateTime; use Doctrine\DBAL\Types\Types; -use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/RequestorFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/RequestorFilter.php index e81cf3114..299dbafb6 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/RequestorFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/RequestorFilter.php @@ -20,6 +20,7 @@ use Exception; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; +use function in_array; final class RequestorFilter implements FilterInterface { @@ -55,7 +56,6 @@ final class RequestorFilter implements FilterInterface switch ($data['accepted_choices']) { case 'participation': - if (!in_array('acppart', $qb->getAllAliases(), true)) { $qb->join('acp.participations', 'acppart'); } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php index ad232d7c2..598652ff8 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php @@ -21,6 +21,7 @@ use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; +use function in_array; class SocialIssueFilter implements FilterInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php index 1ca78d669..94b55b67c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/HouseholdFilters/CompositionFilter.php @@ -19,7 +19,6 @@ use Chill\PersonBundle\Export\Declarations; use DateTime; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Query\Expr; -use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php index eb83f3ff0..394ccddcb 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/AgeFilter.php @@ -15,7 +15,8 @@ use Chill\MainBundle\Export\ExportElementValidatedInterface; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Export\Declarations; -use DateTime; +use DateInterval; +use DateTimeImmutable; use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\Extension\Core\Type\IntegerType; @@ -37,8 +38,8 @@ class AgeFilter implements ExportElementValidatedInterface, FilterInterface $max = null !== $data['max_age'] ? $data['max_age'] : 3000; $calc = $data['date_calc']; - $minDate = $calc->sub(new \DateInterval('P' . $max . 'Y')); - $maxDate = $calc->sub(new \DateInterval('P' . $min . 'Y')); + $minDate = $calc->sub(new DateInterval('P' . $max . 'Y')); + $maxDate = $calc->sub(new DateInterval('P' . $min . 'Y')); $clause = $qb->expr()->andX( $qb->expr()->gte( @@ -79,8 +80,8 @@ class AgeFilter implements ExportElementValidatedInterface, FilterInterface $builder->add('date_calc', ChillDateType::class, [ 'label' => 'Calculate age in relation to this date', - 'data' => new \DateTimeImmutable('now'), - 'input' => 'datetime_immutable' + 'data' => new DateTimeImmutable('now'), + 'input' => 'datetime_immutable', ]); } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/DeadOrAliveFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/DeadOrAliveFilter.php index a161df44f..e069af849 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/DeadOrAliveFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/DeadOrAliveFilter.php @@ -39,9 +39,9 @@ class DeadOrAliveFilter implements FilterInterface $qb->expr()->andX( $qb->expr()->isNull('person.deathdate'), $qb->expr()->lte( - 'person.birthdate', - ':date_calc' - ) + 'person.birthdate', + ':date_calc' + ) ), $qb->expr()->andX( $qb->expr()->isNotNull('person.deathdate'), diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/GeographicalUnitFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/GeographicalUnitFilter.php index e95aff798..7463c36a9 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/GeographicalUnitFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/GeographicalUnitFilter.php @@ -1,15 +1,23 @@ translatableStringHelper = $translatableStringHelper; } - /** - * @inheritDoc - */ - public function buildForm(FormBuilderInterface $builder) - { - $builder - ->add('date_calc', ChillDateType::class, [ - 'label' => 'Compute geographical location at date', - 'required' => true, - 'data' => new \DateTimeImmutable('today'), - 'input' => 'datetime_immutable', - ]) - ->add('units', EntityType::class, [ - 'label' => 'Geographical unit', - 'placeholder' => 'Select a geographical unit', - 'class' => GeographicalUnit::class, - 'choices' => $this->geographicalUnitRepository->findAll(), - 'choice_label' => function(GeographicalUnit $item) { - return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName(); - }, - 'attr' => [ - 'class' => 'select2', - ], - 'multiple' => true, - ]); - } - - /** - * @inheritDoc - */ - public function getTitle(): string - { - return 'Filter by person\'s geographical unit (based on address)'; - } - - /** - * @inheritDoc - */ - public function describeAction($data, $format = 'string') - { - return [ - 'exports.by_person.Filtered by person\'s geographical unit (based on address) computed at datecalc, only units', - [ - 'datecalc' => $data['date_calc']->format('Y-m-d'), - 'units' => implode( - ', ', - array_map( - function (GeographicalUnit $item) { - return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName(); - }, - $data['units']->toArray() - ) - ) - ] - ]; - } - - /** - * @inheritDoc - */ public function addRole(): ?string { return null; } - /** - * @inheritDoc - */ public function alterQuery(QueryBuilder $qb, $data) { $subQuery = 'SELECT 1 - FROM '.PersonHouseholdAddress::class.' person_filter_geog_person_household_address + FROM ' . PersonHouseholdAddress::class . ' person_filter_geog_person_household_address JOIN person_filter_geog_person_household_address.address person_filter_geog_address - JOIN '.GeographicalUnit::class.' person_filter_geog_unit + JOIN ' . GeographicalUnit::class . ' person_filter_geog_unit WITH ST_CONTAINS(person_filter_geog_unit.geom, person_filter_geog_address.point) = TRUE WHERE person_filter_geog_person_household_address.validFrom <= :person_filter_geog_date @@ -120,15 +65,59 @@ class GeographicalUnitFilter implements \Chill\MainBundle\Export\FilterInterface $qb->expr()->exists($subQuery) ) ->setParameter('person_filter_geog_date', $data['date_calc']) - ->setParameter('person_filter_geog_units', $data['units']) - ; + ->setParameter('person_filter_geog_units', $data['units']); } - /** - * @inheritDoc - */ public function applyOn() { return Declarations::PERSON_TYPE; } + + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('date_calc', ChillDateType::class, [ + 'label' => 'Compute geographical location at date', + 'required' => true, + 'data' => new DateTimeImmutable('today'), + 'input' => 'datetime_immutable', + ]) + ->add('units', EntityType::class, [ + 'label' => 'Geographical unit', + 'placeholder' => 'Select a geographical unit', + 'class' => GeographicalUnit::class, + 'choices' => $this->geographicalUnitRepository->findAll(), + 'choice_label' => function (GeographicalUnit $item) { + return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName(); + }, + 'attr' => [ + 'class' => 'select2', + ], + 'multiple' => true, + ]); + } + + public function describeAction($data, $format = 'string') + { + return [ + 'exports.by_person.Filtered by person\'s geographical unit (based on address) computed at datecalc, only units', + [ + 'datecalc' => $data['date_calc']->format('Y-m-d'), + 'units' => implode( + ', ', + array_map( + function (GeographicalUnit $item) { + return $this->translatableStringHelper->localize($item->getLayer()->getName()) . ' > ' . $item->getUnitName(); + }, + $data['units']->toArray() + ) + ), + ], + ]; + } + + public function getTitle(): string + { + return 'Filter by person\'s geographical unit (based on address)'; + } } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/MaritalStatusFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/MaritalStatusFilter.php index a7c637bda..5d136c0e6 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/MaritalStatusFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/MaritalStatusFilter.php @@ -11,18 +11,14 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\PersonFilters; -use Chill\MainBundle\Entity\UserJob; 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\Entity\MaritalStatus; use Chill\PersonBundle\Export\Declarations; use DateTime; use Doctrine\ORM\Query\Expr\Andx; use Symfony\Bridge\Doctrine\Form\Type\EntityType; -use Symfony\Component\Form\Extension\Core\Type\DateType; -use Symfony\Contracts\Translation\TranslatorInterface; class MaritalStatusFilter implements FilterInterface { @@ -77,7 +73,7 @@ class MaritalStatusFilter implements FilterInterface ); }, 'multiple' => true, - 'expanded' => true + 'expanded' => true, ]); $builder->add('calc_date', ChillDateType::class, [ diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php index 2770ae82f..ce632ddd0 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilter.php @@ -22,6 +22,7 @@ use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; class ResidentialAddressAtThirdpartyFilter implements FilterInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php index 7c07121e1..53e9de406 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/PersonFilters/ResidentialAddressAtUserFilter.php @@ -15,9 +15,11 @@ use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Entity\Person\ResidentialAddress; use Chill\PersonBundle\Export\Declarations; +use DateTime; use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; +use function in_array; class ResidentialAddressAtUserFilter implements FilterInterface { @@ -68,7 +70,7 @@ class ResidentialAddressAtUserFilter implements FilterInterface { $builder->add('date_calc', ChillDateType::class, [ 'label' => 'Date during which residential address was valid', - 'data' => new \DateTime('now'), + 'data' => new DateTime('now'), ]); } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/JobFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/JobFilter.php index a93dae54d..f43cc458a 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/JobFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/JobFilter.php @@ -20,6 +20,7 @@ use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; +use function in_array; class JobFilter implements FilterInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ReferrerFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ReferrerFilter.php index e1ddd70fd..a52b9e177 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ReferrerFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ReferrerFilter.php @@ -19,6 +19,7 @@ use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; class ReferrerFilter implements FilterInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ScopeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ScopeFilter.php index ae525a4e5..6efaf9989 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ScopeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/ScopeFilter.php @@ -20,6 +20,7 @@ use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Contracts\Translation\TranslatorInterface; +use function in_array; class ScopeFilter implements FilterInterface { diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php index 0cf7f979d..41125cfe1 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/SocialWorkTypeFilter.php @@ -13,121 +13,38 @@ namespace Chill\PersonBundle\Export\Filter\SocialWorkFilters; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; -use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Chill\PersonBundle\Entity\SocialWork\Goal; use Chill\PersonBundle\Entity\SocialWork\Result; +use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Chill\PersonBundle\Export\Declarations; +use Chill\PersonBundle\Templating\Entity\SocialActionRender; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\FormBuilderInterface; -use Chill\PersonBundle\Templating\Entity\SocialActionRender; +use function count; +use function in_array; class SocialWorkTypeFilter implements FilterInterface { + private EntityManagerInterface $em; + private SocialActionRender $socialActionRender; private TranslatableStringHelper $translatableStringHelper; - private EntityManagerInterface $em; - - public function __construct - ( + public function __construct( SocialActionRender $socialActionRender, TranslatableStringHelper $translatableStringHelper, EntityManagerInterface $em - ) - { + ) { $this->socialActionRender = $socialActionRender; $this->translatableStringHelper = $translatableStringHelper; $this->em = $em; } - public function buildForm(FormBuilderInterface $builder) - { - $builder - ->add('actionType', HiddenType::class) - ->get('actionType') - ->addModelTransformer( - $this->iterableToIdTransformer(SocialAction::class) - ) - ; - $builder - ->add('goal', HiddenType::class) - ->get('goal') - ->addModelTransformer( - $this->iterableToIdTransformer(Goal::class) - ) - ; - $builder - ->add('result', HiddenType::class) - ->get('result') - ->addModelTransformer( - $this->iterableToIdTransformer(Result::class) - ) - ; - } - - private function iterableToIdTransformer(string $entity): CallbackTransformer - { - return new CallbackTransformer( - static function (?iterable $asIterable): string { - if (null === $asIterable) { return ''; } - $ids = []; - foreach ($asIterable as $value) { - $ids[] = $value->getId(); - } - return implode(',', $ids); - }, - function (?string $asString) use ($entity): array { - if (null === $asString) { return []; } - return array_map( - fn (string $id) - => $this->em - ->getRepository($entity) - ->findOneBy(['id' => (int) $id]), - explode(',', $asString) - ); - } - ); - } - - public function getTitle(): string - { - return 'Filter by type of action, goals and results'; - } - - public function describeAction($data, $format = 'string'): array - { - $actionTypes = []; - $goals = []; - $results = []; - - foreach ($data['actionType'] as $at) { - $actionTypes[] = $this->translatableStringHelper->localize( - $at->getTitle() - ); - } - - foreach ($data['goal'] as $g) { - $goals[] = $this->translatableStringHelper->localize( - $g->getTitle() - ); - } - - foreach ($data['result'] as $r) { - $results[] = $this->translatableStringHelper->localize( - $r->getTitle() - ); - } - - return ['Filtered actions by type, goals and results: %selected%', [ - '%selected%' => implode(', ', array_merge($actionTypes, $goals, $results)) - ]]; - } - public function addRole(): ?string { return null; @@ -180,4 +97,90 @@ class SocialWorkTypeFilter implements FilterInterface { return Declarations::SOCIAL_WORK_ACTION_TYPE; } + + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('actionType', HiddenType::class) + ->get('actionType') + ->addModelTransformer( + $this->iterableToIdTransformer(SocialAction::class) + ); + $builder + ->add('goal', HiddenType::class) + ->get('goal') + ->addModelTransformer( + $this->iterableToIdTransformer(Goal::class) + ); + $builder + ->add('result', HiddenType::class) + ->get('result') + ->addModelTransformer( + $this->iterableToIdTransformer(Result::class) + ); + } + + public function describeAction($data, $format = 'string'): array + { + $actionTypes = []; + $goals = []; + $results = []; + + foreach ($data['actionType'] as $at) { + $actionTypes[] = $this->translatableStringHelper->localize( + $at->getTitle() + ); + } + + foreach ($data['goal'] as $g) { + $goals[] = $this->translatableStringHelper->localize( + $g->getTitle() + ); + } + + foreach ($data['result'] as $r) { + $results[] = $this->translatableStringHelper->localize( + $r->getTitle() + ); + } + + return ['Filtered actions by type, goals and results: %selected%', [ + '%selected%' => implode(', ', array_merge($actionTypes, $goals, $results)), + ]]; + } + + public function getTitle(): string + { + return 'Filter by type of action, goals and results'; + } + + private function iterableToIdTransformer(string $entity): CallbackTransformer + { + return new CallbackTransformer( + static function (?iterable $asIterable): string { + if (null === $asIterable) { + return ''; + } + $ids = []; + + foreach ($asIterable as $value) { + $ids[] = $value->getId(); + } + + return implode(',', $ids); + }, + function (?string $asString) use ($entity): array { + if (null === $asString) { + return []; + } + + return array_map( + fn (string $id) => $this->em + ->getRepository($entity) + ->findOneBy(['id' => (int) $id]), + explode(',', $asString) + ); + } + ); + } } diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepository.php index 283dac151..5cf394df6 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepository.php @@ -15,7 +15,6 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Query\ResultSetMappingBuilder; -use UnexpectedValueException; final class ClosingMotiveRepository implements ClosingMotiveRepositoryInterface { @@ -29,6 +28,32 @@ final class ClosingMotiveRepository implements ClosingMotiveRepositoryInterface $this->repository = $entityManager->getRepository(ClosingMotive::class); } + public function find($id): ?ClosingMotive + { + return $this->repository->find($id); + } + + /** + * @return array|ClosingMotive[] + */ + public function findAll(): array + { + return $this->repository->findAll(); + } + + /** + * @return array|ClosingMotive[] + */ + 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): ?ClosingMotive + { + return $this->findOneBy($criteria); + } + /** * @return mixed */ @@ -56,32 +81,6 @@ final class ClosingMotiveRepository implements ClosingMotiveRepositoryInterface ->getResult(); } - public function find($id): ?ClosingMotive - { - return $this->repository->find($id); - } - - /** - * @return array|ClosingMotive[] - */ - public function findAll(): array - { - return $this->repository->findAll(); - } - - /** - * @return array|ClosingMotive[] - */ - 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): ?ClosingMotive - { - return $this->findOneBy($criteria); - } - public function getClassName(): string { return ClosingMotive::class; diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepositoryInterface.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepositoryInterface.php index fbea29a0b..73bc3ff5d 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepositoryInterface.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepositoryInterface.php @@ -1,5 +1,14 @@ build(); } - public function getRolesWithHierarchy(): array - { - return [ 'Person' => $this->getRoles() ]; - } - public function getRoles(): array { return [self::STATS]; } + public function getRolesWithHierarchy(): array + { + return ['Person' => $this->getRoles()]; + } + public function getRolesWithoutScope(): array { return $this->getRoles(); @@ -72,8 +72,7 @@ class HouseholdVoter extends Voter implements ProvideRoleHierarchyInterface { return ($subject instanceof Household && in_array($attribute, self::ALL, true)) - || $this->helper->supports($attribute, $subject) - ; + || $this->helper->supports($attribute, $subject); } protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregatorTest.php index 82a879b19..84487b0b6 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/AdministrativeLocationAggregatorTest.php @@ -11,11 +11,15 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\AdministrativeLocationAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class AdministrativeLocationAggregatorTest extends AbstractAggregatorTest { private AdministrativeLocationAggregator $aggregator; @@ -51,8 +55,7 @@ final class AdministrativeLocationAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(acp.id)') ->from(AccompanyingPeriod::class, 'acp') - ->join('acp.administrativeLocation', 'acploc') - , + ->join('acp.administrativeLocation', 'acploc'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregatorTest.php index 279b621d4..2aa4e3e3e 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ClosingMotiveAggregatorTest.php @@ -11,11 +11,15 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ClosingMotiveAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class ClosingMotiveAggregatorTest extends AbstractAggregatorTest { private ClosingMotiveAggregator $aggregator; @@ -51,8 +55,7 @@ final class ClosingMotiveAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(acp.id)') ->from(AccompanyingPeriod::class, 'acp') - ->join('acp.closingMotive', 'acpmotive') - , + ->join('acp.closingMotive', 'acpmotive'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregatorTest.php index ab0e3dae3..a96b14d28 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ConfidentialAggregatorTest.php @@ -11,11 +11,15 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ConfidentialAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class ConfidentialAggregatorTest extends AbstractAggregatorTest { private ConfidentialAggregator $aggregator; @@ -50,8 +54,7 @@ final class ConfidentialAggregatorTest extends AbstractAggregatorTest return [ $em->createQueryBuilder() ->select('count(acp.id)') - ->from(AccompanyingPeriod::class, 'acp') - , + ->from(AccompanyingPeriod::class, 'acp'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregatorTest.php index 056c396e0..616ee83c5 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/DurationAggregatorTest.php @@ -11,11 +11,15 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\DurationAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class DurationAggregatorTest extends AbstractAggregatorTest { private DurationAggregator $aggregator; @@ -50,8 +54,7 @@ final class DurationAggregatorTest extends AbstractAggregatorTest return [ $em->createQueryBuilder() ->select('count(acp.id)') - ->from(AccompanyingPeriod::class, 'acp') - , + ->from(AccompanyingPeriod::class, 'acp'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregatorTest.php index 0b8ff2b12..c4a2fc74e 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EmergencyAggregatorTest.php @@ -11,11 +11,15 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\EmergencyAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class EmergencyAggregatorTest extends AbstractAggregatorTest { private EmergencyAggregator $aggregator; @@ -50,8 +54,7 @@ final class EmergencyAggregatorTest extends AbstractAggregatorTest return [ $em->createQueryBuilder() ->select('count(acp.id)') - ->from(AccompanyingPeriod::class, 'acp') - , + ->from(AccompanyingPeriod::class, 'acp'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregatorTest.php index 8c67eb649..cd5b1f4c7 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/EvaluationAggregatorTest.php @@ -11,11 +11,15 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\EvaluationAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class EvaluationAggregatorTest extends AbstractAggregatorTest { private EvaluationAggregator $aggregator; @@ -52,8 +56,7 @@ final class EvaluationAggregatorTest extends AbstractAggregatorTest ->select('count(acp.id)') ->from(AccompanyingPeriod::class, 'acp') ->join('acp.works', 'acpw') - ->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval') - , + ->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregatorTest.php index 84204e629..8808961bf 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregatorTest.php @@ -11,11 +11,15 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\GeographicalUnitStatAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class GeographicalUnitStatAggregatorTest extends AbstractAggregatorTest { private GeographicalUnitStatAggregator $aggregator; @@ -50,8 +54,7 @@ final class GeographicalUnitStatAggregatorTest extends AbstractAggregatorTest return [ $em->createQueryBuilder() ->select('count(acp.id)') - ->from(AccompanyingPeriod::class, 'acp') - , + ->from(AccompanyingPeriod::class, 'acp'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregatorTest.php index b3da8ea13..e4c976cc9 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/IntensityAggregatorTest.php @@ -11,11 +11,15 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\IntensityAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class IntensityAggregatorTest extends AbstractAggregatorTest { private IntensityAggregator $aggregator; @@ -50,8 +54,7 @@ final class IntensityAggregatorTest extends AbstractAggregatorTest return [ $em->createQueryBuilder() ->select('count(acp.id)') - ->from(AccompanyingPeriod::class, 'acp') - , + ->from(AccompanyingPeriod::class, 'acp'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/JobAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/JobAggregatorTest.php index 8e14ffe4c..f1b980640 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/JobAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/JobAggregatorTest.php @@ -11,11 +11,15 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\JobAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class JobAggregatorTest extends AbstractAggregatorTest { private JobAggregator $aggregator; @@ -51,8 +55,7 @@ final class JobAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(acp.id)') ->from(AccompanyingPeriod::class, 'acp') - ->join('acp.job', 'acpjob') - , + ->join('acp.job', 'acpjob'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregatorTest.php index cdd709226..a2126c47c 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/OriginAggregatorTest.php @@ -11,11 +11,15 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\OriginAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class OriginAggregatorTest extends AbstractAggregatorTest { private OriginAggregator $aggregator; @@ -51,8 +55,7 @@ final class OriginAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(acp.id)') ->from(AccompanyingPeriod::class, 'acp') - ->join('acp.origin', 'acporigin') - , + ->join('acp.origin', 'acporigin'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregatorTest.php index 789baa228..c5c04374f 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerAggregatorTest.php @@ -11,11 +11,15 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ReferrerAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class ReferrerAggregatorTest extends AbstractAggregatorTest { private ReferrerAggregator $aggregator; @@ -51,8 +55,7 @@ final class ReferrerAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(acp.id)') ->from(AccompanyingPeriod::class, 'acp') - ->join('acp.user', 'acpuser') - , + ->join('acp.user', 'acpuser'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregatorTest.php index 6069a024d..f91fedad9 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ReferrerScopeAggregatorTest.php @@ -1,19 +1,32 @@ new \DateTimeImmutable('now') - ] + 'date_calc' => new DateTimeImmutable('now'), + ], ]; } @@ -54,10 +67,7 @@ class ReferrerScopeAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(acp.id)') ->from(AccompanyingPeriod::class, 'acp') - ->join('acp.scopes', 'acpscope') - , + ->join('acp.scopes', 'acpscope'), ]; } - - } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/RequestorAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/RequestorAggregatorTest.php index f888e2535..84924e262 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/RequestorAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/RequestorAggregatorTest.php @@ -11,11 +11,15 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\RequestorAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class RequestorAggregatorTest extends AbstractAggregatorTest { private RequestorAggregator $aggregator; @@ -51,8 +55,7 @@ final class RequestorAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(acp.id)') ->from(AccompanyingPeriod::class, 'acp') - ->join('acp.participations', 'acppart') - , + ->join('acp.participations', 'acppart'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregatorTest.php index 05fe5e980..066ff2bc5 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/ScopeAggregatorTest.php @@ -11,11 +11,15 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\ScopeAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class ScopeAggregatorTest extends AbstractAggregatorTest { private ScopeAggregator $aggregator; @@ -51,8 +55,7 @@ final class ScopeAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(acp.id)') ->from(AccompanyingPeriod::class, 'acp') - ->join('acp.scopes', 'acpscope') - , + ->join('acp.scopes', 'acpscope'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregatorTest.php index cdf27c1a5..66b59e5f9 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialActionAggregatorTest.php @@ -11,11 +11,15 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\SocialActionAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class SocialActionAggregatorTest extends AbstractAggregatorTest { private SocialActionAggregator $aggregator; @@ -51,8 +55,7 @@ final class SocialActionAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(acp.id)') ->from(AccompanyingPeriod::class, 'acp') - ->join('acp.works', 'acpw') - , + ->join('acp.works', 'acpw'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregatorTest.php index 912a2b7c6..360937cac 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/SocialIssueAggregatorTest.php @@ -11,11 +11,15 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\SocialIssueAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class SocialIssueAggregatorTest extends AbstractAggregatorTest { private SocialIssueAggregator $aggregator; @@ -51,8 +55,7 @@ final class SocialIssueAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(acp.id)') ->from(AccompanyingPeriod::class, 'acp') - ->join('acp.socialIssues', 'acpsocialissue') - , + ->join('acp.socialIssues', 'acpsocialissue'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/StepAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/StepAggregatorTest.php index ee11eeee4..f038c8a1c 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/StepAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/AccompanyingCourseAggregators/StepAggregatorTest.php @@ -11,11 +11,16 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\AccompanyingCourseAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\AccompanyingCourseAggregators\StepAggregator; +use DateTime; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class StepAggregatorTest extends AbstractAggregatorTest { private StepAggregator $aggregator; @@ -36,7 +41,7 @@ final class StepAggregatorTest extends AbstractAggregatorTest { return [ [ - 'on_date' => \DateTime::createFromFormat('Y-m-d', '2022-01-01'), + 'on_date' => DateTime::createFromFormat('Y-m-d', '2022-01-01'), ], ]; } @@ -52,8 +57,7 @@ final class StepAggregatorTest extends AbstractAggregatorTest return [ $em->createQueryBuilder() ->select('count(acp.id)') - ->from(AccompanyingPeriod::class, 'acp') - , + ->from(AccompanyingPeriod::class, 'acp'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregatorTest.php index 60b8d838c..624f03ef5 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/EvaluationAggregators/EvaluationTypeAggregatorTest.php @@ -11,11 +11,15 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\EvaluationAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\EvaluationAggregators\EvaluationTypeAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class EvaluationTypeAggregatorTest extends AbstractAggregatorTest { private EvaluationTypeAggregator $aggregator; @@ -52,8 +56,7 @@ final class EvaluationTypeAggregatorTest extends AbstractAggregatorTest ->select('count(acp.id)') ->from(AccompanyingPeriod::class, 'acp') ->join('acp.works', 'acpw') - ->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval') - , + ->join('acpw.accompanyingPeriodWorkEvaluations', 'workeval'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregatorTest.php index bef052329..429cf825b 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/ChildrenNumberAggregatorTest.php @@ -11,11 +11,16 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\HouseholdAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\HouseholdAggregators\ChildrenNumberAggregator; +use DateTime; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class ChildrenNumberAggregatorTest extends AbstractAggregatorTest { private ChildrenNumberAggregator $aggregator; @@ -36,7 +41,7 @@ final class ChildrenNumberAggregatorTest extends AbstractAggregatorTest { return [ [ - 'on_date' => \DateTime::createFromFormat('Y-m-d', '2022-07-01') + 'on_date' => DateTime::createFromFormat('Y-m-d', '2022-07-01'), ], ]; } @@ -57,8 +62,7 @@ final class ChildrenNumberAggregatorTest extends AbstractAggregatorTest ->join('acppart.person', 'partperson') ->join('partperson.householdParticipations', 'member') ->join('member.household', 'household') - ->join('household.compositions', 'composition') - , + ->join('household.compositions', 'composition'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/CompositionAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/CompositionAggregatorTest.php index fc808c03d..5e017144e 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/CompositionAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/HouseholdAggregators/CompositionAggregatorTest.php @@ -11,11 +11,16 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\HouseholdAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\HouseholdAggregators\CompositionAggregator; +use DateTime; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class CompositionAggregatorTest extends AbstractAggregatorTest { private CompositionAggregator $aggregator; @@ -36,7 +41,7 @@ final class CompositionAggregatorTest extends AbstractAggregatorTest { return [ [ - 'on_date' => \DateTime::createFromFormat('Y-m-d', '2022-07-01') + 'on_date' => DateTime::createFromFormat('Y-m-d', '2022-07-01'), ], ]; } @@ -57,8 +62,7 @@ final class CompositionAggregatorTest extends AbstractAggregatorTest ->join('acppart.person', 'partperson') ->join('partperson.householdParticipations', 'member') ->join('member.household', 'household') - ->join('household.compositions', 'composition') - , + ->join('household.compositions', 'composition'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/CountryOfBirthAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/CountryOfBirthAggregatorTest.php index 90596d892..a22135ec5 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/CountryOfBirthAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/CountryOfBirthAggregatorTest.php @@ -16,6 +16,10 @@ use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Export\Aggregator\PersonAggregators\CountryOfBirthAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class CountryOfBirthAggregatorTest extends AbstractAggregatorTest { private CountryOfBirthAggregator $aggregator; @@ -36,7 +40,7 @@ final class CountryOfBirthAggregatorTest extends AbstractAggregatorTest { return [ ['group_by_level' => 'continent'], - ['group_by_level' => 'country',], + ['group_by_level' => 'country'], ]; } @@ -52,8 +56,7 @@ final class CountryOfBirthAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(person.id)') ->from(Person::class, 'person') - ->leftJoin('person.countryOfBirth', 'countryOfBirth') - , + ->leftJoin('person.countryOfBirth', 'countryOfBirth'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/HouseholdPositionAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/HouseholdPositionAggregatorTest.php index 91a22d05a..036b00302 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/HouseholdPositionAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/HouseholdPositionAggregatorTest.php @@ -15,9 +15,14 @@ use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Chill\PersonBundle\Entity\Household\HouseholdMember; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Export\Aggregator\PersonAggregators\HouseholdPositionAggregator; +use DateTime; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query\Expr; +/** + * @internal + * @coversNothing + */ final class HouseholdPositionAggregatorTest extends AbstractAggregatorTest { private HouseholdPositionAggregator $aggregator; @@ -38,7 +43,7 @@ final class HouseholdPositionAggregatorTest extends AbstractAggregatorTest { return [ [ - 'date_position' => \DateTime::createFromFormat('Y-m-d', '2022-07-01') + 'date_position' => DateTime::createFromFormat('Y-m-d', '2022-07-01'), ], ]; } @@ -56,8 +61,7 @@ final class HouseholdPositionAggregatorTest extends AbstractAggregatorTest ->select('count(person.id)') ->from(Person::class, 'person') ->join(HouseholdMember::class, 'householdmember', Expr\Join::WITH, 'householdmember.person = person') - ->join('person.center', 'center') - , + ->join('person.center', 'center'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/MaritalStatusAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/MaritalStatusAggregatorTest.php index 25ddb602d..a24210fb4 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/MaritalStatusAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/PersonAggregators/MaritalStatusAggregatorTest.php @@ -16,6 +16,10 @@ use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Export\Aggregator\PersonAggregators\MaritalStatusAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class MaritalStatusAggregatorTest extends AbstractAggregatorTest { private MaritalStatusAggregator $aggregator; @@ -51,8 +55,7 @@ final class MaritalStatusAggregatorTest extends AbstractAggregatorTest $em->createQueryBuilder() ->select('count(person.id)') ->from(Person::class, 'person') - ->join('person.maritalStatus', 'personmarital') - , + ->join('person.maritalStatus', 'personmarital'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregatorTest.php index ed83c3af7..3ba391d60 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ActionTypeAggregatorTest.php @@ -54,8 +54,7 @@ final class ActionTypeAggregatorTest extends AbstractAggregatorTest return [ $em->createQueryBuilder() ->select('count(acpw.id)') - ->from(AccompanyingPeriodWork::class, 'acpw') - , + ->from(AccompanyingPeriodWork::class, 'acpw'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalAggregatorTest.php index 979f3c488..deef482ee 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalAggregatorTest.php @@ -54,8 +54,7 @@ final class GoalAggregatorTest extends AbstractAggregatorTest return [ $em->createQueryBuilder() ->select('count(acpw.id)') - ->from(AccompanyingPeriodWork::class, 'acpw') - , + ->from(AccompanyingPeriodWork::class, 'acpw'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalResultAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalResultAggregatorTest.php index 37642fa33..429833569 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalResultAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/GoalResultAggregatorTest.php @@ -11,11 +11,15 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\SocialWorkAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\GoalResultAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class GoalResultAggregatorTest extends AbstractAggregatorTest { private GoalResultAggregator $aggregator; @@ -53,8 +57,7 @@ final class GoalResultAggregatorTest extends AbstractAggregatorTest ->from(AccompanyingPeriod::class, 'acp') ->join('acp.works', 'acpw') ->join('acpw.goals', 'goal') - ->join('goal.results', 'goalresult') - , + ->join('goal.results', 'goalresult'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/JobAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/JobAggregatorTest.php index c686e66b6..e8ed02aa1 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/JobAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/JobAggregatorTest.php @@ -11,11 +11,15 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\SocialWorkAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\JobAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class JobAggregatorTest extends AbstractAggregatorTest { private JobAggregator $aggregator; @@ -52,8 +56,7 @@ final class JobAggregatorTest extends AbstractAggregatorTest ->select('count(acp.id)') ->from(AccompanyingPeriod::class, 'acp') ->join('acp.works', 'acpw') - ->join('acpw.referrers', 'acpwuser') - , + ->join('acpw.referrers', 'acpwuser'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ReferrerAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ReferrerAggregatorTest.php index b8070398a..060655eb3 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ReferrerAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ReferrerAggregatorTest.php @@ -54,8 +54,7 @@ final class ReferrerAggregatorTest extends AbstractAggregatorTest return [ $em->createQueryBuilder() ->select('count(acpw.id)') - ->from(AccompanyingPeriodWork::class, 'acpw') - , + ->from(AccompanyingPeriodWork::class, 'acpw'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ResultAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ResultAggregatorTest.php index 984e296bc..cca033b5b 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ResultAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ResultAggregatorTest.php @@ -54,8 +54,7 @@ final class ResultAggregatorTest extends AbstractAggregatorTest return [ $em->createQueryBuilder() ->select('count(acpw.id)') - ->from(AccompanyingPeriodWork::class, 'acpw') - , + ->from(AccompanyingPeriodWork::class, 'acpw'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ScopeAggregatorTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ScopeAggregatorTest.php index 3cece9ed9..95b3a784c 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ScopeAggregatorTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Aggregator/SocialWorkAggregators/ScopeAggregatorTest.php @@ -11,11 +11,15 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Aggregator\SocialWorkAggregators; -use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators\ScopeAggregator; use Doctrine\ORM\EntityManagerInterface; +/** + * @internal + * @coversNothing + */ final class ScopeAggregatorTest extends AbstractAggregatorTest { private ScopeAggregator $aggregator; @@ -52,8 +56,7 @@ final class ScopeAggregatorTest extends AbstractAggregatorTest ->select('count(acp.id)') ->from(AccompanyingPeriod::class, 'acp') ->join('acp.works', 'acpw') - ->join('acpw.referrers', 'acpwuser') - , + ->join('acpw.referrers', 'acpwuser'), ]; } } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOnDateFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOnDateFilterTest.php index fa03315d2..ac94a9c33 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOnDateFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOnDateFilterTest.php @@ -15,7 +15,6 @@ use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ActiveOnDateFilter; use DateTime; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\Request; /** * @internal diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilterTest.php index 44f55e69f..5c25abefc 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ActiveOneDayBetweenDatesFilterTest.php @@ -15,7 +15,6 @@ use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ActiveOneDayBetweenDatesFilter; use DateTime; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\Request; /** * @internal diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/AdministrativeLocationFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/AdministrativeLocationFilterTest.php index 3bd7518f2..df301537c 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/AdministrativeLocationFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/AdministrativeLocationFilterTest.php @@ -9,13 +9,12 @@ declare(strict_types=1); -namespace Chill\PersonBundle\Tests\Export\Filter\AccompanyingCourseFilters;; +namespace Chill\PersonBundle\Tests\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Entity\Location; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\AdministrativeLocationFilter; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\Request; /** * @internal diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/EvaluationFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/EvaluationFilterTest.php index b1436f345..b0fdb24b9 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/EvaluationFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/EvaluationFilterTest.php @@ -15,7 +15,6 @@ use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Entity\SocialWork\Evaluation; use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\EvaluationFilter; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\Request; /** * @internal diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilterTest.php index 247cb870b..72a838f1e 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/GeographicalUnitStatFilterTest.php @@ -15,7 +15,6 @@ use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\GeographicalUnitStatFilter; use DateTime; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\Request; /** * @internal diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilterTest.php index a402b1f6a..e11e8228f 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/OpenBetweenDatesFilterTest.php @@ -15,7 +15,6 @@ use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\OpenBetweenDatesFilter; use DateTime; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\Request; /** * @internal diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ReferrerFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ReferrerFilterTest.php index 71cfd44da..ade7de2f9 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ReferrerFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/ReferrerFilterTest.php @@ -15,7 +15,6 @@ use Chill\MainBundle\Entity\User; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\ReferrerFilter; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\Request; /** * @internal diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/RequestorFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/RequestorFilterTest.php index 318bccf0a..d56c57741 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/RequestorFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/RequestorFilterTest.php @@ -14,7 +14,6 @@ namespace Chill\PersonBundle\Tests\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\RequestorFilter; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\Request; /** * @internal diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialActionFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialActionFilterTest.php index 7b9a8b63e..15454c899 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialActionFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/AccompanyingCourseFilters/SocialActionFilterTest.php @@ -15,7 +15,6 @@ use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters\SocialActionFilter; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Component\HttpFoundation\Request; /** * @internal diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/EvaluationTypeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/EvaluationTypeFilterTest.php index 81c764c99..c6c407348 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/EvaluationTypeFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/EvaluationTypeFilterTest.php @@ -51,7 +51,7 @@ final class EvaluationTypeFilterTest extends AbstractFilterTest foreach ($array as $r) { $data[] = [ - 'accepted_evaluationtype' => $r + 'accepted_evaluationtype' => $r, ]; } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/MaxDateFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/MaxDateFilterTest.php index be4b3580a..71658d711 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/MaxDateFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/EvaluationFilters/MaxDateFilterTest.php @@ -13,7 +13,6 @@ namespace Chill\PersonBundle\Tests\Export\Filter\EvaluationFilters; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Entity\AccompanyingPeriod; -use Chill\PersonBundle\Entity\SocialWork\Evaluation; use Chill\PersonBundle\Export\Filter\EvaluationFilters\MaxDateFilter; use Doctrine\ORM\EntityManagerInterface; @@ -40,8 +39,8 @@ final class MaxDateFilterTest extends AbstractFilterTest public function getFormData(): array { return [ - ['maxdate' => false ], - ['maxdate' => true ] + ['maxdate' => false], + ['maxdate' => true], ]; } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/HouseholdFilters/CompositionFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/HouseholdFilters/CompositionFilterTest.php index e71949958..c0eccef81 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/HouseholdFilters/CompositionFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/HouseholdFilters/CompositionFilterTest.php @@ -15,6 +15,7 @@ use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Entity\Household\Household; use Chill\PersonBundle\Entity\Household\HouseholdCompositionType; use Chill\PersonBundle\Export\Filter\HouseholdFilters\CompositionFilter; +use DateTime; use Doctrine\ORM\EntityManagerInterface; /** @@ -52,7 +53,7 @@ final class CompositionFilterTest extends AbstractFilterTest foreach ($array as $r) { $data[] = [ 'accepted_composition' => $r, - 'on_date' => \DateTime::createFromFormat('Y-m-d', '2022-05-01'), + 'on_date' => DateTime::createFromFormat('Y-m-d', '2022-05-01'), ]; } @@ -73,4 +74,4 @@ final class CompositionFilterTest extends AbstractFilterTest ->from(Household::class, 'h'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodClosingFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodClosingFilterTest.php index bf975c5a1..1440eca51 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodClosingFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodClosingFilterTest.php @@ -13,6 +13,7 @@ namespace Chill\PersonBundle\Tests\Export\Filter\PersonFilters; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Export\Filter\PersonFilters\AccompanyingPeriodClosingFilter; +use DateTime; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; /** @@ -43,8 +44,8 @@ final class AccompanyingPeriodClosingFilterTest extends AbstractFilterTest { return [ [ - 'date_from' => \DateTime::createFromFormat('Y-m-d', '2000-01-01'), - 'date_to' => \DateTime::createFromFormat('Y-m-d', '2010-01-01'), + 'date_from' => DateTime::createFromFormat('Y-m-d', '2000-01-01'), + 'date_to' => DateTime::createFromFormat('Y-m-d', '2010-01-01'), ], ]; } @@ -86,4 +87,4 @@ final class AccompanyingPeriodClosingFilterTest extends AbstractFilterTest ->join('person.center', 'center'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodOpeningFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodOpeningFilterTest.php index 54ed981ec..1cb46257d 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodOpeningFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AccompanyingPeriodOpeningFilterTest.php @@ -13,6 +13,7 @@ namespace Chill\PersonBundle\Tests\Export\Filter\PersonFilters; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Export\Filter\PersonFilters\AccompanyingPeriodOpeningFilter; +use DateTime; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; /** @@ -43,8 +44,8 @@ final class AccompanyingPeriodOpeningFilterTest extends AbstractFilterTest { return [ [ - 'date_from' => \DateTime::createFromFormat('Y-m-d', '2000-01-01'), - 'date_to' => \DateTime::createFromFormat('Y-m-d', '2010-01-01'), + 'date_from' => DateTime::createFromFormat('Y-m-d', '2000-01-01'), + 'date_to' => DateTime::createFromFormat('Y-m-d', '2010-01-01'), ], ]; } @@ -86,4 +87,4 @@ final class AccompanyingPeriodOpeningFilterTest extends AbstractFilterTest ->join('person.center', 'center'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AgeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AgeFilterTest.php index 30c3ed86d..0131016ab 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AgeFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/AgeFilterTest.php @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Tests\Export\Filter\PersonFilters; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Export\Filter\PersonFilters\AgeFilter; +use DateTime; use Doctrine\ORM\EntityManagerInterface; /** @@ -42,12 +43,12 @@ final class AgeFilterTest extends AbstractFilterTest [ 'min_age' => '18', 'max_age' => '60', - 'date_calc' => \DateTime::createFromFormat('Y-m-d', '2020-05-01'), + 'date_calc' => DateTime::createFromFormat('Y-m-d', '2020-05-01'), ], [ // ça devrait faire boum ! 'min_age' => '35', 'max_age' => '30', - 'date_calc' => \DateTime::createFromFormat('Y-m-d', '2020-05-01'), + 'date_calc' => DateTime::createFromFormat('Y-m-d', '2020-05-01'), ], ]; } diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeadOrAliveFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeadOrAliveFilterTest.php index 0cbb22be7..ce48394c6 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeadOrAliveFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeadOrAliveFilterTest.php @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Tests\Export\Filter\PersonFilters; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Export\Filter\PersonFilters\DeadOrAliveFilter; +use DateTime; use Doctrine\ORM\EntityManagerInterface; /** @@ -41,11 +42,11 @@ final class DeadOrAliveFilterTest extends AbstractFilterTest return [ [ 'person_state' => 'alive', - 'date_calc' => \DateTime::createFromFormat('Y-m-d', '2021-05-01'), + 'date_calc' => DateTime::createFromFormat('Y-m-d', '2021-05-01'), ], [ 'person_state' => 'deceased', - 'date_calc' => \DateTime::createFromFormat('Y-m-d', '2021-05-01'), + 'date_calc' => DateTime::createFromFormat('Y-m-d', '2021-05-01'), ], ]; } @@ -64,4 +65,4 @@ final class DeadOrAliveFilterTest extends AbstractFilterTest ->from(Person::class, 'p'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeathdateFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeathdateFilterTest.php index 17742dcd6..6562233e4 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeathdateFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/DeathdateFilterTest.php @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Tests\Export\Filter\PersonFilters; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Export\Filter\PersonFilters\DeathdateFilter; +use DateTime; use Doctrine\ORM\EntityManagerInterface; /** @@ -40,9 +41,9 @@ final class DeathdateFilterTest extends AbstractFilterTest { return [ [ - 'date_from' => \DateTime::createFromFormat('Y-m-d', '2020-05-01'), - 'date_to' => \DateTime::createFromFormat('Y-m-d', '2022-05-01'), - ] + 'date_from' => DateTime::createFromFormat('Y-m-d', '2020-05-01'), + 'date_to' => DateTime::createFromFormat('Y-m-d', '2022-05-01'), + ], ]; } @@ -60,4 +61,4 @@ final class DeathdateFilterTest extends AbstractFilterTest ->from(Person::class, 'p'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/MaritalStatusFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/MaritalStatusFilterTest.php index 40fa814b1..a01566ba8 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/MaritalStatusFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/MaritalStatusFilterTest.php @@ -15,6 +15,7 @@ use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Entity\MaritalStatus; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Export\Filter\PersonFilters\MaritalStatusFilter; +use DateTime; use Doctrine\ORM\EntityManagerInterface; /** @@ -52,7 +53,7 @@ final class MaritalStatusFilterTest extends AbstractFilterTest foreach ($array as $m) { $data[] = [ 'maritalStatus' => $m, - 'calc_date' => \DateTime::createFromFormat('Y-m-d', '2022-05-01'), + 'calc_date' => DateTime::createFromFormat('Y-m-d', '2022-05-01'), ]; } @@ -74,4 +75,3 @@ final class MaritalStatusFilterTest extends AbstractFilterTest ]; } } - diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/NationalityFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/NationalityFilterTest.php index 6dd1a944e..e142fa9a0 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/NationalityFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/NationalityFilterTest.php @@ -47,7 +47,7 @@ final class NationalityFilterTest extends AbstractFilterTest foreach ($countries as $c) { $data[] = [ - 'nationalities' => $c + 'nationalities' => $c, ]; } @@ -68,5 +68,4 @@ final class NationalityFilterTest extends AbstractFilterTest ->from(Person::class, 'p'), ]; } - -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilterTest.php index 8444f6865..1c9225f80 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtThirdpartyFilterTest.php @@ -16,6 +16,7 @@ use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person\ResidentialAddress; use Chill\PersonBundle\Export\Filter\PersonFilters\ResidentialAddressAtThirdpartyFilter; use Chill\ThirdPartyBundle\Entity\ThirdPartyCategory; +use DateTime; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query\Expr; @@ -54,7 +55,7 @@ final class ResidentialAddressAtThirdpartyFilterTest extends AbstractFilterTest foreach ($array as $r) { $data[] = [ 'thirdparty_cat' => $r, - 'date_calc' => \DateTime::createFromFormat('Y-m-d', '2022-05-01'), + 'date_calc' => DateTime::createFromFormat('Y-m-d', '2022-05-01'), ]; } @@ -76,8 +77,7 @@ final class ResidentialAddressAtThirdpartyFilterTest extends AbstractFilterTest ->join(ResidentialAddress::class, 'resaddr', Expr\Join::WITH, 'resaddr.person = p') ->join('p.center', 'center') ->join('resaddr.hostThirdParty', 'tparty') - ->join('tparty.categories', 'tpartycat') - , + ->join('tparty.categories', 'tpartycat'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtUserFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtUserFilterTest.php index ba3ededca..4ce578ca2 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtUserFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/PersonFilters/ResidentialAddressAtUserFilterTest.php @@ -13,9 +13,7 @@ namespace Chill\PersonBundle\Tests\Export\Filter\PersonFilters; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\PersonBundle\Entity\Person; -use Chill\PersonBundle\Export\Filter\PersonFilters\ResidentialAddressAtThirdpartyFilter; use Chill\PersonBundle\Export\Filter\PersonFilters\ResidentialAddressAtUserFilter; -use Chill\ThirdPartyBundle\Entity\ThirdPartyCategory; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query\Expr; @@ -60,4 +58,4 @@ final class ResidentialAddressAtUserFilterTest extends AbstractFilterTest ->join('p.center', 'center'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ReferrerFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ReferrerFilterTest.php index dd4a17c72..8b4a69c98 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ReferrerFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/ReferrerFilterTest.php @@ -47,9 +47,10 @@ final class ReferrerFilterTest extends AbstractFilterTest foreach ($users as $u) { $data[] = [ - 'accepted_agents' => $u + 'accepted_agents' => $u, ]; } + return $data; } @@ -68,4 +69,4 @@ final class ReferrerFilterTest extends AbstractFilterTest ->join('acp.works', 'acpw'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/SocialWorkTypeFilterTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/SocialWorkTypeFilterTest.php index b9def05cd..99fb3aae9 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/SocialWorkTypeFilterTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Filter/SocialWorkFilters/SocialWorkTypeFilterTest.php @@ -50,17 +50,21 @@ final class SocialWorkTypeFilterTest extends AbstractFilterTest $results = []; $social_actions = $action_repository->findAll(); + foreach ($social_actions as $action) { $actions[] = $action->getId(); $goals_by_action = $goal_repository->findBySocialActionWithDescendants($action); + foreach ($goals_by_action as $goal) { $goals[] = $goal->getId(); $results_by_goal = $result_repository->findByGoal($goal); + foreach ($results_by_goal as $result) { $results[] = $result->getId(); } } $results_by_action = $result_repository->findBySocialActionWithDescendants($action); + foreach ($results_by_action as $result) { $results[] = $result->getId(); } @@ -79,7 +83,7 @@ final class SocialWorkTypeFilterTest extends AbstractFilterTest 'actionType' => implode(',', $actions), 'goal' => implode(',', $goals), 'result' => implode(',', $results), - ] + ], ]; /// TODO ne fonctionne pas var_dump($data); @@ -102,4 +106,4 @@ final class SocialWorkTypeFilterTest extends AbstractFilterTest ->join('acp.works', 'acpw'), ]; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Repository/PersonACLAwareRepositoryTest.php b/src/Bundle/ChillPersonBundle/Tests/Repository/PersonACLAwareRepositoryTest.php index 4540e3716..3f7ac3027 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Repository/PersonACLAwareRepositoryTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Repository/PersonACLAwareRepositoryTest.php @@ -1,5 +1,14 @@ entityManager = self::$container->get(EntityManagerInterface::class); $this->countryRepository = self::$container->get(CountryRepository::class); $this->centerRepository = self::$container->get(CenterRepositoryInterface::class); - } public function testCountByCriteria() @@ -46,8 +59,12 @@ class PersonACLAwareRepositoryTest extends KernelTestCase $security = $this->prophesize(Security::class); $security->getUser()->willReturn($user); - $repository = new PersonACLAwareRepository($security->reveal(), $this->entityManager, $this->countryRepository, - $authorizationHelper->reveal()); + $repository = new PersonACLAwareRepository( + $security->reveal(), + $this->entityManager, + $this->countryRepository, + $authorizationHelper->reveal() + ); $number = $repository->countBySearchCriteria('diallo'); @@ -65,13 +82,18 @@ class PersonACLAwareRepositoryTest extends KernelTestCase $security = $this->prophesize(Security::class); $security->getUser()->willReturn($user); - $repository = new PersonACLAwareRepository($security->reveal(), $this->entityManager, $this->countryRepository, - $authorizationHelper->reveal()); + $repository = new PersonACLAwareRepository( + $security->reveal(), + $this->entityManager, + $this->countryRepository, + $authorizationHelper->reveal() + ); $results = $repository->findBySearchCriteria(0, 5, false, 'diallo'); $this->assertGreaterThan(0, count($results)); $this->assertContainsOnlyInstancesOf(Person::class, $results); + foreach ($results as $person) { $this->assertStringContainsString('diallo', strtolower($person->getFirstName() . ' ' . $person->getLastName())); } diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20220926154347.php b/src/Bundle/ChillPersonBundle/migrations/Version20220926154347.php index b308c22e3..e04d6b90a 100644 --- a/src/Bundle/ChillPersonBundle/migrations/Version20220926154347.php +++ b/src/Bundle/ChillPersonBundle/migrations/Version20220926154347.php @@ -1,5 +1,12 @@ addSql('DROP VIEW view_chill_person_person_center_history_current'); + $this->addSql('DROP SEQUENCE chill_person_person_center_history_id_seq CASCADE'); + $this->addSql('DROP TABLE chill_person_person_center_history'); + } + public function getDescription(): string { return 'Add a center history on person'; @@ -59,11 +73,4 @@ final class Version20220926154347 extends AbstractMigration SELECT nextval(\'chill_person_person_center_history_id_seq\'), id, center_id, COALESCE(createdat, NOW()) FROM chill_person_person WHERE center_id IS NOT NULL'); } - - public function down(Schema $schema): void - { - $this->addSql('DROP VIEW view_chill_person_person_center_history_current'); - $this->addSql('DROP SEQUENCE chill_person_person_center_history_id_seq CASCADE'); - $this->addSql('DROP TABLE chill_person_person_center_history'); - } } diff --git a/src/Bundle/ChillReportBundle/Tests/Export/Filter/ReportDateFilterTest.php b/src/Bundle/ChillReportBundle/Tests/Export/Filter/ReportDateFilterTest.php index 54291a02f..b8d4510de 100644 --- a/src/Bundle/ChillReportBundle/Tests/Export/Filter/ReportDateFilterTest.php +++ b/src/Bundle/ChillReportBundle/Tests/Export/Filter/ReportDateFilterTest.php @@ -14,6 +14,7 @@ namespace Chill\ReportBundle\Tests\Export\Filter; use Chill\MainBundle\Test\Export\AbstractFilterTest; use Chill\ReportBundle\Entity\Report; use Chill\ReportBundle\Export\Filter\ReportDateFilter; +use DateTime; use Doctrine\ORM\EntityManagerInterface; /** @@ -40,9 +41,9 @@ final class ReportDateFilterTest extends AbstractFilterTest { return [ [ - 'date_from' => \DateTime::createFromFormat('Y-m-d', '2021-07-01'), - 'date_to' => \DateTime::createFromFormat('Y-m-d', '2022-07-01'), - ] + 'date_from' => DateTime::createFromFormat('Y-m-d', '2021-07-01'), + 'date_to' => DateTime::createFromFormat('Y-m-d', '2022-07-01'), + ], ]; } @@ -57,7 +58,7 @@ final class ReportDateFilterTest extends AbstractFilterTest return [ $em->createQueryBuilder() ->select('r.id') - ->from(Report::class, 'r') + ->from(Report::class, 'r'), ]; } } diff --git a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php index dca83eb71..11fd1aa4c 100644 --- a/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php +++ b/src/Bundle/ChillTaskBundle/Repository/SingleTaskAclAwareRepository.php @@ -12,7 +12,6 @@ declare(strict_types=1); namespace Chill\TaskBundle\Repository; use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface; -use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface; use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\Person; @@ -336,8 +335,7 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository ->leftJoin('course.participations', 'participation') ->leftJoin('participation.person', 'person_p') ->leftJoin('person.centerCurrent', 'center_current_person') - ->leftJoin('person_p.centerCurrent', 'center_current_participation') - ; + ->leftJoin('person_p.centerCurrent', 'center_current_participation'); $qb->distinct(true); $k = 0; diff --git a/src/Bundle/ChillTaskBundle/Tests/Repository/SingleTaskACLAwareRepositoryTest.php b/src/Bundle/ChillTaskBundle/Tests/Repository/SingleTaskACLAwareRepositoryTest.php index f4c54b6cc..2cd0788ac 100644 --- a/src/Bundle/ChillTaskBundle/Tests/Repository/SingleTaskACLAwareRepositoryTest.php +++ b/src/Bundle/ChillTaskBundle/Tests/Repository/SingleTaskACLAwareRepositoryTest.php @@ -1,5 +1,14 @@ personRepository = self::$container->get(PersonRepository::class); } - public function testCountByPerson(): void - { - $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); - $user = new User(); - $scopes = $this->scopeRepository->findAll(); - $person = $this->getRandomPerson($this->em); - - $security = $this->prophesize(Security::class); - $security->getUser()->willReturn($user); - - $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); - $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any()) - ->willReturn([$centerA]); - - $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); - $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA)) - ->willReturn($scopes); - - $repository = new SingleTaskAclAwareRepository( - $centerResolverDispatcher->reveal(), - $this->em, - $security->reveal(), - $authorizationHelper->reveal() - ); - - $nb = $repository->countByPerson($person, null, []); - - $this->assertGreaterThanOrEqual(0, $nb); - } - - public function testFindByPerson(): void - { - $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); - $user = new User(); - $scopes = $this->scopeRepository->findAll(); - $person = $this->getRandomPerson($this->em); - - $security = $this->prophesize(Security::class); - $security->getUser()->willReturn($user); - - $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); - $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any()) - ->willReturn([$centerA]); - - $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); - $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA)) - ->willReturn($scopes); - - $repository = new SingleTaskAclAwareRepository( - $centerResolverDispatcher->reveal(), - $this->em, - $security->reveal(), - $authorizationHelper->reveal() - ); - - $tasks = $repository->findByPerson($person, null, []); - - $this->assertGreaterThanOrEqual(0, count($tasks)); - } - - public function testFindByAllViewable(): void - { - $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); - $user = new User(); - $scopes = $this->scopeRepository->findAll(); - - $security = $this->prophesize(Security::class); - $security->getUser()->willReturn($user); - - $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); - $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any()) - ->willReturn([$centerA]); - - $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); - $authorizationHelper->getReachableCenters(Argument::exact($user), Argument::exact(TaskVoter::SHOW)) - ->willReturn([$centerA]); - $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA)) - ->willReturn($scopes); - - $repository = new SingleTaskAclAwareRepository( - $centerResolverDispatcher->reveal(), - $this->em, - $security->reveal(), - $authorizationHelper->reveal() - ); - - $tasks = $repository->findByAllViewable(null, []); - - $this->assertGreaterThanOrEqual(0, count($tasks)); - } - public function testCountByAllViewable(): void { $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); @@ -169,6 +94,67 @@ class SingleTaskACLAwareRepositoryTest extends KernelTestCase $this->assertGreaterThanOrEqual(0, $nb); } + public function testCountByPerson(): void + { + $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); + $user = new User(); + $scopes = $this->scopeRepository->findAll(); + $person = $this->getRandomPerson($this->em); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); + $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any()) + ->willReturn([$centerA]); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA)) + ->willReturn($scopes); + + $repository = new SingleTaskAclAwareRepository( + $centerResolverDispatcher->reveal(), + $this->em, + $security->reveal(), + $authorizationHelper->reveal() + ); + + $nb = $repository->countByPerson($person, null, []); + + $this->assertGreaterThanOrEqual(0, $nb); + } + + public function testFindByAllViewable(): void + { + $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); + $user = new User(); + $scopes = $this->scopeRepository->findAll(); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); + $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any()) + ->willReturn([$centerA]); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableCenters(Argument::exact($user), Argument::exact(TaskVoter::SHOW)) + ->willReturn([$centerA]); + $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA)) + ->willReturn($scopes); + + $repository = new SingleTaskAclAwareRepository( + $centerResolverDispatcher->reveal(), + $this->em, + $security->reveal(), + $authorizationHelper->reveal() + ); + + $tasks = $repository->findByAllViewable(null, []); + + $this->assertGreaterThanOrEqual(0, count($tasks)); + } + public function testFindByCourse(): void { $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); @@ -176,14 +162,13 @@ class SingleTaskACLAwareRepositoryTest extends KernelTestCase $scopes = $this->scopeRepository->findAll(); /** @var Person $person */ $person = $this->em->createQuery( - 'SELECT p FROM '.Person::class.' p JOIN p.centerCurrent cc - WHERE SIZE(p.accompanyingPeriodParticipations) > 0 + 'SELECT p FROM ' . Person::class . ' p JOIN p.centerCurrent cc + WHERE SIZE(p.accompanyingPeriodParticipations) > 0 AND cc.center = :center' ) ->setParameter('center', $centerA) ->setMaxResults(1) - ->getSingleResult() - ; + ->getSingleResult(); $period = $person->getAccompanyingPeriodParticipations()->first()->getAccompanyingPeriod(); $security = $this->prophesize(Security::class); @@ -208,4 +193,34 @@ class SingleTaskACLAwareRepositoryTest extends KernelTestCase $this->assertGreaterThanOrEqual(0, count($tasks)); } -} \ No newline at end of file + + public function testFindByPerson(): void + { + $centerA = $this->centerRepository->findOneBy(['name' => 'Center A']); + $user = new User(); + $scopes = $this->scopeRepository->findAll(); + $person = $this->getRandomPerson($this->em); + + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + $centerResolverDispatcher = $this->prophesize(CenterResolverManagerInterface::class); + $centerResolverDispatcher->resolveCenters(Argument::type(Person::class), Argument::any()) + ->willReturn([$centerA]); + + $authorizationHelper = $this->prophesize(AuthorizationHelperInterface::class); + $authorizationHelper->getReachableScopes(Argument::exact($user), Argument::exact(TaskVoter::SHOW), Argument::exact($centerA)) + ->willReturn($scopes); + + $repository = new SingleTaskAclAwareRepository( + $centerResolverDispatcher->reveal(), + $this->em, + $security->reveal(), + $authorizationHelper->reveal() + ); + + $tasks = $repository->findByPerson($person, null, []); + + $this->assertGreaterThanOrEqual(0, count($tasks)); + } +}

  • hL;EvXE+Lz^+b@`N$XJ);N}~t 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