From abebb79e8ba26cc86c0ae90a74458accd39cb110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 7 Nov 2023 14:41:56 +0100 Subject: [PATCH 1/3] Fix geographical unit stat aggregator by course: show also the course if they are not located within a location --- .changes/unreleased/Fixed-20231107-144039.yaml | 6 ++++++ .../GeographicalUnitStatAggregator.php | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 .changes/unreleased/Fixed-20231107-144039.yaml diff --git a/.changes/unreleased/Fixed-20231107-144039.yaml b/.changes/unreleased/Fixed-20231107-144039.yaml new file mode 100644 index 000000000..2292b8adb --- /dev/null +++ b/.changes/unreleased/Fixed-20231107-144039.yaml @@ -0,0 +1,6 @@ +kind: Fixed +body: 'Export: fix "group accompanying period by geographical unit": take into account + the accompanying periods when the period is not located within an unit' +time: 2023-11-07T14:40:39.46483404+01:00 +custom: + Issue: "185" diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregator.php index f1c0d60ad..3fa612988 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregator.php @@ -83,7 +83,12 @@ final readonly class GeographicalUnitStatAggregator implements AggregatorInterfa 'acp_geog_units' ); - $qb->andWhere($qb->expr()->in('acp_geog_units.layer', ':acp_geog_unit_layer')); + $qb->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('acp_geog_units'), + $qb->expr()->in('acp_geog_units.layer', ':acp_geog_unit_layer') + ) + ); $qb->setParameter('acp_geog_unit_layer', $data['level']); From 83fe3ec3fca0bb2c8474b4db0e199f9a50b479f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 7 Nov 2023 16:06:22 +0100 Subject: [PATCH 2/3] Export: add a "filter activity by creator job" filter --- .../unreleased/Feature-20231107-160435.yaml | 5 + .../Export/Filter/CreatorJobFilter.php | 116 ++++++++++++++++++ .../Export/Filter/CreatorJobFilterTest.php | 75 +++++++++++ .../config/services/export.yaml | 4 + .../translations/messages.fr.yml | 6 +- .../Repository/UserJobRepository.php | 16 ++- .../Repository/UserJobRepositoryInterface.php | 7 ++ 7 files changed, 225 insertions(+), 4 deletions(-) create mode 100644 .changes/unreleased/Feature-20231107-160435.yaml create mode 100644 src/Bundle/ChillActivityBundle/Export/Filter/CreatorJobFilter.php create mode 100644 src/Bundle/ChillActivityBundle/Tests/Export/Filter/CreatorJobFilterTest.php diff --git a/.changes/unreleased/Feature-20231107-160435.yaml b/.changes/unreleased/Feature-20231107-160435.yaml new file mode 100644 index 000000000..e7976d27e --- /dev/null +++ b/.changes/unreleased/Feature-20231107-160435.yaml @@ -0,0 +1,5 @@ +kind: Feature +body: 'Export: add a filter "filter activity by creator job"' +time: 2023-11-07T16:04:35.921093231+01:00 +custom: + Issue: "194" diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/CreatorJobFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/CreatorJobFilter.php new file mode 100644 index 000000000..4288920e9 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Export/Filter/CreatorJobFilter.php @@ -0,0 +1,116 @@ +leftJoin('activity.createdBy', "{$p}_user") + ->leftJoin( + UserJobHistory::class, + "{$p}_history", + Join::WITH, + $qb->expr()->eq("{$p}_history.user", "{$p}_user") + ) + // job_at based on activity.date + ->andWhere( + $qb->expr()->andX( + $qb->expr()->lte("{$p}_history.startDate", 'activity.date'), + $qb->expr()->orX( + $qb->expr()->isNull("{$p}_history.endDate"), + $qb->expr()->gt("{$p}_history.endDate", 'activity.date') + ) + ) + ) + ->andWhere( + $qb->expr()->in("{$p}_history.job", ":{$p}_jobs") + ) + ->setParameter( + "{$p}_jobs", + $data['jobs'], + ); + } + + public function applyOn(): string + { + return Declarations::ACTIVITY; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('jobs', EntityType::class, [ + 'choices' => $this->userJobRepository->findAllOrderedByName(), + 'class' => UserJob::class, + 'choice_label' => fn (UserJob $s) => $this->translatableStringHelper->localize( + $s->getLabel() + ).($s->isActive() ? '' : '('.$this->translator->trans('inactive').')'), + 'label' => 'export.filter.activity.by_creator_job.job_form_label', + 'multiple' => true, + 'expanded' => true, + ]); + } + + public function describeAction($data, $format = 'string'): array + { + $jobs = array_map( + fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()), + $data['jobs'] instanceof Collection ? $data['jobs']->toArray() : $data['jobs'] + ); + + return ['export.filter.activity.by_creator_job.Filtered activity by user job: only %jobs%', [ + '%jobs%' => implode(', ', $jobs), + ]]; + } + + public function getFormDefaultData(): array + { + return [ + 'jobs' => [], + ]; + } + + public function getTitle(): string + { + return 'export.filter.activity.by_creator_job.Filter activity by user job'; + } +} diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Filter/CreatorJobFilterTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/CreatorJobFilterTest.php new file mode 100644 index 000000000..bbf9b830e --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Filter/CreatorJobFilterTest.php @@ -0,0 +1,75 @@ +entityManager = self::$container->get(EntityManagerInterface::class); + $this->translatableStringHelper = self::$container->get(TranslatableStringHelperInterface::class); + $this->translator = self::$container->get(TranslatorInterface::class); + $this->userJobRepository = self::$container->get(UserJobRepositoryInterface::class); + } + + public function getFilter() + { + return new CreatorJobFilter( + $this->translatableStringHelper, + $this->translator, + $this->userJobRepository + ); + } + + public function getFormData() + { + $this->setUp(); + $jobs = $this->userJobRepository->findAll(); + + return [ + ['jobs' => $jobs], + ]; + } + + public function getQueryBuilders() + { + self::setUp(); + + return [ + $this->entityManager->createQueryBuilder() + ->select('count(activity.id)') + ->from(Activity::class, 'activity') + ->join('activity.user', 'actuser'), + ]; + } +} diff --git a/src/Bundle/ChillActivityBundle/config/services/export.yaml b/src/Bundle/ChillActivityBundle/config/services/export.yaml index 6ce5b1d72..cb269b0f4 100644 --- a/src/Bundle/ChillActivityBundle/config/services/export.yaml +++ b/src/Bundle/ChillActivityBundle/config/services/export.yaml @@ -115,6 +115,10 @@ services: # affect all saved exports (unless we write a migration for that) - { name: chill.export_filter, alias: 'activity_userscope_filter' } + Chill\ActivityBundle\Export\Filter\CreatorJobFilter: + tags: + - { name: chill.export_filter, alias: 'activity_creatorjob_filter' } + Chill\ActivityBundle\Export\Filter\UsersJobFilter: tags: - { name: chill.export_filter, alias: 'activity_usersjob_filter' } diff --git a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml index 7bb62435f..56b69fe6c 100644 --- a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml @@ -376,7 +376,11 @@ export: date mismatch: La date de fin de la période doit être supérieure à la date du début by_creator_scope: Filter activity by user scope: Filtrer les échanges par service du créateur de l'échange - 'Filtered activity by user scope: only %scopes%': "Filtré par service du créateur: uniquement %scopes%" + 'Filtered activity by user scope: only %scopes%': "Filtré par service du créateur de l'échange: uniquement %scopes%" + by_creator_job: + job_form_label: Métiers + Filter activity by user job: Filtrer les échanges par métier du créateur de l'échange + 'Filtered activity by user job: only %jobs%': "Filtré par service du créateur de l'échange: uniquement %jobs%" by_persons: Filter activity by persons: Filtrer les échanges par usager participant 'Filtered activity by persons: only %persons%': 'Échanges filtrés par usagers participants: seulement %persons%' diff --git a/src/Bundle/ChillMainBundle/Repository/UserJobRepository.php b/src/Bundle/ChillMainBundle/Repository/UserJobRepository.php index ca3c60e1c..dee7ac9d7 100644 --- a/src/Bundle/ChillMainBundle/Repository/UserJobRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/UserJobRepository.php @@ -12,14 +12,15 @@ declare(strict_types=1); namespace Chill\MainBundle\Repository; use Chill\MainBundle\Entity\UserJob; +use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; -class UserJobRepository implements UserJobRepositoryInterface +readonly class UserJobRepository implements UserJobRepositoryInterface { - private readonly EntityRepository $repository; + private EntityRepository $repository; - public function __construct(EntityManagerInterface $em) + public function __construct(EntityManagerInterface $em, private TranslatableStringHelperInterface $translatableStringHelper) { $this->repository = $em->getRepository(UserJob::class); } @@ -42,6 +43,15 @@ class UserJobRepository implements UserJobRepositoryInterface return $this->repository->findBy(['active' => true]); } + public function findAllOrderedByName(): array + { + $jobs = $this->findAll(); + + usort($jobs, fn (UserJob $a, UserJob $b) => $this->translatableStringHelper->localize($a->getLabel()) <=> $this->translatableStringHelper->localize($b->getLabel())); + + return $jobs; + } + /** * @param mixed|null $limit * @param mixed|null $offset diff --git a/src/Bundle/ChillMainBundle/Repository/UserJobRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/UserJobRepositoryInterface.php index 3c3f16fc1..ef7488042 100644 --- a/src/Bundle/ChillMainBundle/Repository/UserJobRepositoryInterface.php +++ b/src/Bundle/ChillMainBundle/Repository/UserJobRepositoryInterface.php @@ -28,6 +28,13 @@ interface UserJobRepositoryInterface extends ObjectRepository */ public function findAllActive(): array; + /** + * a list of UserJob ordered by name. + * + * @return array + */ + public function findAllOrderedByName(): array; + /** * @param mixed|null $limit * @param mixed|null $offset From bb1602934c5705561290bc012f2ca4d3d001091a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 7 Nov 2023 16:07:24 +0100 Subject: [PATCH 3/3] Fix "group activity by creator job" aggregator --- .changes/unreleased/Fixed-20231107-160702.yaml | 5 +++++ ...copeAggregator.php => CreatorJobAggregator.php} | 14 ++++++-------- ...egatorTest.php => CreatorJobAggregatorTest.php} | 8 ++++---- .../config/services/export.yaml | 2 +- .../translations/messages.fr.yml | 4 ++-- 5 files changed, 18 insertions(+), 15 deletions(-) create mode 100644 .changes/unreleased/Fixed-20231107-160702.yaml rename src/Bundle/ChillActivityBundle/Export/Aggregator/{JobScopeAggregator.php => CreatorJobAggregator.php} (87%) rename src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/{JobScopeAggregatorTest.php => CreatorJobAggregatorTest.php} (81%) diff --git a/.changes/unreleased/Fixed-20231107-160702.yaml b/.changes/unreleased/Fixed-20231107-160702.yaml new file mode 100644 index 000000000..46ac4810d --- /dev/null +++ b/.changes/unreleased/Fixed-20231107-160702.yaml @@ -0,0 +1,5 @@ +kind: Fixed +body: Fix "group activity by creator job" aggregator +time: 2023-11-07T16:07:02.576354444+01:00 +custom: + Issue: "" diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/JobScopeAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/CreatorJobAggregator.php similarity index 87% rename from src/Bundle/ChillActivityBundle/Export/Aggregator/JobScopeAggregator.php rename to src/Bundle/ChillActivityBundle/Export/Aggregator/CreatorJobAggregator.php index e43f430d5..d57670e7f 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/JobScopeAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/CreatorJobAggregator.php @@ -14,18 +14,18 @@ namespace Chill\ActivityBundle\Export\Aggregator; use Chill\ActivityBundle\Export\Declarations; use Chill\MainBundle\Entity\User\UserJobHistory; use Chill\MainBundle\Export\AggregatorInterface; -use Chill\MainBundle\Repository\ScopeRepository; +use Chill\MainBundle\Repository\UserJobRepositoryInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -class JobScopeAggregator implements AggregatorInterface +class CreatorJobAggregator implements AggregatorInterface { private const PREFIX = 'acp_agg_creator_job'; public function __construct( - private readonly ScopeRepository $scopeRepository, + private readonly UserJobRepositoryInterface $userJobRepository, private readonly TranslatableStringHelper $translatableStringHelper ) {} @@ -76,17 +76,15 @@ class JobScopeAggregator implements AggregatorInterface { return function ($value): string { if ('_header' === $value) { - return 'Scope'; + return 'Job'; } - if (null === $value || '' === $value) { + if (null === $value || '' === $value || null === $s = $this->userJobRepository->find($value)) { return ''; } - $s = $this->scopeRepository->find($value); - return $this->translatableStringHelper->localize( - $s->getName() + $s->getLabel() ); }; } diff --git a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/JobScopeAggregatorTest.php b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/CreatorJobAggregatorTest.php similarity index 81% rename from src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/JobScopeAggregatorTest.php rename to src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/CreatorJobAggregatorTest.php index 69d511ace..9a821de75 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/JobScopeAggregatorTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Export/Aggregator/CreatorJobAggregatorTest.php @@ -12,7 +12,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Tests\Export\Aggregator; use Chill\ActivityBundle\Entity\Activity; -use Chill\ActivityBundle\Export\Aggregator\JobScopeAggregator; +use Chill\ActivityBundle\Export\Aggregator\CreatorJobAggregator; use Chill\MainBundle\Test\Export\AbstractAggregatorTest; use Doctrine\ORM\EntityManagerInterface; @@ -21,15 +21,15 @@ use Doctrine\ORM\EntityManagerInterface; * * @coversNothing */ -final class JobScopeAggregatorTest extends AbstractAggregatorTest +final class CreatorJobAggregatorTest extends AbstractAggregatorTest { - private JobScopeAggregator $aggregator; + private CreatorJobAggregator $aggregator; protected function setUp(): void { self::bootKernel(); - $this->aggregator = self::$container->get(JobScopeAggregator::class); + $this->aggregator = self::$container->get(CreatorJobAggregator::class); } public function getAggregator() diff --git a/src/Bundle/ChillActivityBundle/config/services/export.yaml b/src/Bundle/ChillActivityBundle/config/services/export.yaml index cb269b0f4..29f7c2625 100644 --- a/src/Bundle/ChillActivityBundle/config/services/export.yaml +++ b/src/Bundle/ChillActivityBundle/config/services/export.yaml @@ -183,7 +183,7 @@ services: tags: - { name: chill.export_aggregator, alias: activity_creator_scope_aggregator } - Chill\ActivityBundle\Export\Aggregator\JobScopeAggregator: + Chill\ActivityBundle\Export\Aggregator\CreatorJobAggregator: tags: - { name: chill.export_aggregator, alias: activity_creator_job_aggregator } diff --git a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml index 56b69fe6c..759d222c1 100644 --- a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml @@ -416,8 +416,8 @@ export: Group activity by creator scope: Grouper les échanges par service du créateur de l'échange Calc date: Date de calcul du service du créateur de l'échange by_creator_job: - Group activity by creator job: Grouper les échanges par service du créateur de l'échange - Calc date: Date de calcul du service du créateur de l'échange + Group activity by creator job: Grouper les échanges par métier du créateur de l'échange + Calc date: Date de calcul du métier du créateur de l'échange by_persons: Group activity by persons: Grouper les échanges par usager participant Persons: Usagers participants