diff --git a/src/Bundle/ChillPersonBundle/Export/Declarations.php b/src/Bundle/ChillPersonBundle/Export/Declarations.php index dc5c3f971..72d702aa2 100644 --- a/src/Bundle/ChillPersonBundle/Export/Declarations.php +++ b/src/Bundle/ChillPersonBundle/Export/Declarations.php @@ -23,4 +23,6 @@ abstract class Declarations public const ACP_TYPE = 'accompanying_period'; public const SOCIAL_WORK_ACTION_TYPE = 'social_actions'; + + public const ACP_SHARED = 'accompanying_period_shared'; } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php index b7b57d3cd..6d2c0c6f6 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php @@ -105,7 +105,7 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface public function supportsModifiers(): array { - return [Declarations::ACP_TYPE]; + return [Declarations::ACP_TYPE, Declarations::ACP_SHARED]; } public function getGroup(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php b/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php index a7086271a..7cba36904 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountSocialWorkActions.php @@ -99,7 +99,7 @@ class CountSocialWorkActions implements ExportInterface, GroupedExportInterface public function supportsModifiers(): array { - return [Declarations::SOCIAL_WORK_ACTION_TYPE]; + return [Declarations::SOCIAL_WORK_ACTION_TYPE, Declarations::ACP_SHARED]; } public function getGroup(): string diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/ActiveOnDateFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/ActiveOnDateFilter.php new file mode 100644 index 000000000..0d83bce13 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/ActiveOnDateFilter.php @@ -0,0 +1,87 @@ +add('on_date', ChillDateType::class, [ + 'data' => new DateTime(), + ]) + ; + } + + /** + * @inheritDoc + */ + public function getTitle(): string + { + return 'Filtered 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'); + + $clause = $qb->expr()->andX( + $qb->expr()->lte('acp.openingDate', ':ondate'), + $qb->expr()->orX( + $qb->expr()->gt('acp.closingDate', ':ondate'), + $qb->expr()->isNull('acp.closingDate') + ) + ); + + if ($where instanceof Andx) { + $where->add($clause); + } else { + $where = $qb->expr()->andX($clause); + } + + $qb->add('where', $where); + $qb->setParameter('ondate', $data['on_date'], Types::DATE_MUTABLE); + } + + /** + * @inheritDoc + */ + public function applyOn(): string + { + return Declarations::ACP_TYPE; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/ActiveOneDayBetweenDatesFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/ActiveOneDayBetweenDatesFilter.php new file mode 100644 index 000000000..0054018d1 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/ActiveOneDayBetweenDatesFilter.php @@ -0,0 +1,93 @@ +add('date_from', ChillDateType::class, [ + 'data' => new DateTime(), + ]) + ->add('date_to', ChillDateType::class, [ + 'data' => new DateTime(), + ]) + ; + } + + /** + * @inheritDoc + */ + public function getTitle(): string + { + return 'Filtered 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'); + + $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->setParameter('datefrom', $data['date_from'], Types::DATE_MUTABLE); + $qb->setParameter('dateto', $data['date_to'], Types::DATE_MUTABLE); + } + + /** + * @inheritDoc + */ + public function applyOn(): string + { + return Declarations::ACP_TYPE; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/ActivityTypeFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/ActivityTypeFilter.php new file mode 100644 index 000000000..f019394d4 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/ActivityTypeFilter.php @@ -0,0 +1,111 @@ +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 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 ! + // we replace indicator 'from' clause by 'act', and put 'acp' in a join + + $qb->resetDQLPart('from'); + $qb->from('ChillActivityBundle:Activity', 'act'); + + $qb + ->join('act.accompanyingPeriod', 'acp') + ->join('act.activityType', 'aty') + ; + + $where = $qb->getDQLPart('where'); + $clause = $qb->expr()->in('aty.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']); + + } + + /** + * @inheritDoc + */ + public function applyOn() + { + return Declarations::ACP_TYPE; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AdministrativeLocationFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AdministrativeLocationFilter.php new file mode 100644 index 000000000..b52a2abe1 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AdministrativeLocationFilter.php @@ -0,0 +1,99 @@ +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'); + $clause = $qb->expr()->in('acp.administrativeLocation', ':locations'); + + if ($where instanceof Andx) { + $where->add($clause); + } else { + $where = $qb->expr()->andX($clause); + } + + $qb->add('where', $where); + $qb->setParameter('locations', $data['accepted_locations']); + } + + /** + * @inheritDoc + */ + public function applyOn(): string + { + return Declarations::ACP_TYPE; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilter.php new file mode 100644 index 000000000..7dd2a683d --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilter.php @@ -0,0 +1,105 @@ +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') + ; + + $where = $qb->getDQLPart('where'); + $clause = $qb->expr()->in('ev.id', ':evaluations'); + + if ($where instanceof Andx) { + $where->add($clause); + } else { + $where = $qb->expr()->andX($clause); + } + + $qb->add('where', $where); + $qb->setParameter('evaluations', $data['accepted_evaluations']); + } + + /** + * @inheritDoc + */ + public function applyOn(): string + { + return Declarations::ACP_TYPE; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/OpenBetweenDatesFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/OpenBetweenDatesFilter.php new file mode 100644 index 000000000..2c3514f92 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/OpenBetweenDatesFilter.php @@ -0,0 +1,89 @@ +add('date_from', ChillDateType::class, [ + 'data' => new DateTime(), + ]) + ->add('date_to', ChillDateType::class, [ + 'data' => new DateTime(), + ]) + ; + } + + /** + * @inheritDoc + */ + public function getTitle(): string + { + return 'Filtered 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'); + + $clause = $qb->expr()->andX( + $qb->expr()->lt('acp.openingDate', ':datefrom'), + $qb->expr()->gt('acp.closingDate', ':dateto') + ); + + if ($where instanceof Andx) { + $where->add($clause); + } else { + $where = $qb->expr()->andX($clause); + } + + $qb->add('where', $where); + $qb->setParameter('datefrom', $data['date_from'], Types::DATE_MUTABLE); + $qb->setParameter('dateto', $data['date_to'], Types::DATE_MUTABLE); + } + + /** + * @inheritDoc + */ + public function applyOn(): string + { + return Declarations::ACP_TYPE; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/ReferrerFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/ReferrerFilter.php index d79027035..e46b20592 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/ReferrerFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/ReferrerFilter.php @@ -4,12 +4,13 @@ namespace Chill\PersonBundle\Export\Filter; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Templating\Entity\UserRender; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\Query\Expr\Andx; +use Doctrine\ORM\Query\Expr\From; use Doctrine\ORM\QueryBuilder; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; -use Chill\MainBundle\Templating\Entity\UserRender; class ReferrerFilter implements FilterInterface { @@ -20,9 +21,12 @@ class ReferrerFilter implements FilterInterface $this->userRender = $userRender; } + /** + * @inheritDoc + */ public function buildForm(FormBuilderInterface $builder) { - $builder->add('referrers', EntityType::class, [ + $builder->add('accepted_referrers', EntityType::class, [ 'class' => User::class, 'choice_label' => function (User $u) { return $this->userRender->renderString($u, []); @@ -30,37 +34,66 @@ class ReferrerFilter implements FilterInterface 'multiple' => true, 'expanded' => true ]); + } + /** + * @inheritDoc + */ public function getTitle(): string { - return 'Filter by referrers'; + return 'Filtered by referrers'; } - public function describeAction($data, $format = 'string'): array + /** + * @inheritDoc + */ + public function describeAction($data, $format = 'string') { - $referrers = []; + $users = []; - foreach ($data['referrers'] as $r) { - $referrers[] = $this->userRender->renderString($r, []); + foreach ($data['accepted_referrers'] as $r) { + $users[] = $r; } - return ['Filtered by referrers: only %referrers%', [ - '%referrers%' => implode(', ou ', $referrers) + return [ + 'Filtered by referrer: only %referrers%', [ + '%referrers' => implode(", ou ", $users) ]]; } + /** + * @inheritDoc + */ public function addRole() { return null; } + /** + * @inheritDoc + */ public function alterQuery(QueryBuilder $qb, $data) { - $qb->join('acpw.referrers', 'r'); $where = $qb->getDQLPart('where'); - $clause = $qb->expr()->in('r', ':referrers'); + + $from_alias = $this->getEntityFromQB($qb); + + // Use querybuilder from alias to find which export context (indicator) + switch ($from_alias) { + case 'acp': + $clause = $qb->expr()->in('acp.user', ':referrers'); + break; + + case 'acpw': + $qb->join('acpw.referrers', 'r'); + $clause = $qb->expr()->in('r', ':referrers'); + break; + + default: + throw new \Exception("Referrer filter doesn't apply on that entity"); + } if ($where instanceof Andx) { $where->add($clause); @@ -69,11 +102,22 @@ class ReferrerFilter implements FilterInterface } $qb->add('where', $where); - $qb->setParameter('referrers', $data['referrers']); + $qb->setParameter('referrers', $data['accepted_referrers']); } - public function applyOn(): string + private function getEntityFromQB(QueryBuilder $qb): string { - return Declarations::SOCIAL_WORK_ACTION_TYPE; + /** @var From $from */ + $from = $qb->getDQLPart('from'); + + return $from[0]->getAlias(); + } + + /** + * @inheritDoc + */ + public function applyOn() + { + return Declarations::ACP_SHARED; } } \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialActionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialActionFilter.php new file mode 100644 index 000000000..d421044a2 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialActionFilter.php @@ -0,0 +1,101 @@ +translator = $translator; + $this->translatableStringHelper = $translatableStringHelper; + $this->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; + } + + public function alterQuery(QueryBuilder $qb, $data) + { + $qb->join('acp.works', 'acpw') + ->join('acpw.socialAction', 'sa') + ; + + $where = $qb->getDQLPart('where'); + $clause = $qb->expr()->in('sa.id', ':socialactions'); + + if ($where instanceof Andx) { + $where->add($clause); + } else { + $where = $qb->expr()->andX($clause); + } + + $qb->add('where', $where); + $qb->setParameter('socialactions', $data['accepted_socialactions']); + } + + public function applyOn(): string + { + return Declarations::ACP_TYPE; + } +} \ No newline at end of file diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_period.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_period.yaml index 262d07a82..62ad99b97 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_period.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_accompanying_period.yaml @@ -60,6 +60,27 @@ services: tags: - { name: chill.export_filter, alias: accompanyingcourse_step_filter } + chill.person.export.filter_socialaction: + class: Chill\PersonBundle\Export\Filter\SocialActionFilter + autowire: true + autoconfigure: true + tags: + - { name: chill.export_filter, alias: accompanyingcourse_socialaction_filter } + + chill.person.export.filter_evaluation: + class: Chill\PersonBundle\Export\Filter\EvaluationFilter + autowire: true + autoconfigure: true + tags: + - { name: chill.export_filter, alias: accompanyingcourse_evaluation_filter } + + chill.person.export.filter_activitytype: + class: Chill\PersonBundle\Export\Filter\ActivityTypeFilter + autowire: true + autoconfigure: true + tags: + - { name: chill.export_filter, alias: accompanyingcourse_activitytype_filter } + chill.person.export.filter_origin: class: Chill\PersonBundle\Export\Filter\OriginFilter autowire: true @@ -74,6 +95,13 @@ services: tags: - { name: chill.export_filter, alias: accompanyingcourse_closingmotive_filter } + chill.person.export.filter_administrative_location: + class: Chill\PersonBundle\Export\Filter\AdministrativeLocationFilter + autowire: true + autoconfigure: true + tags: + - { name: chill.export_filter, alias: accompanyingcourse_administrative_location_filter } + chill.person.export.filter_confidential: class: Chill\PersonBundle\Export\Filter\ConfidentialFilter autowire: true @@ -95,4 +123,32 @@ services: tags: - { name: chill.export_filter, alias: accompanyingcourse_intensity_filter } + chill.person.export.filter_activeondate: + class: Chill\PersonBundle\Export\Filter\ActiveOnDateFilter + autowire: true + autoconfigure: true + tags: + - { name: chill.export_filter, alias: accompanyingcourse_activeondate_filter } + + chill.person.export.filter_activeonedaybetweendates: + class: Chill\PersonBundle\Export\Filter\ActiveOneDayBetweenDatesFilter + autowire: true + autoconfigure: true + tags: + - { name: chill.export_filter, alias: accompanyingcourse_activeonedaybetweendates_filter } + + chill.person.export.filter_referrer: + class: Chill\PersonBundle\Export\Filter\ReferrerFilter + autowire: true + autoconfigure: true + tags: + - { name: chill.export_filter, alias: accompanyingcourse_referrer_filter } + + chill.person.export.filter_openbetweendates: + class: Chill\PersonBundle\Export\Filter\OpenBetweenDatesFilter + autowire: true + autoconfigure: true + tags: + - { name: chill.export_filter, alias: accompanyingcourse_openbetweendates_filter } + ## Aggregators \ 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 773024823..f613b09da 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -394,6 +394,18 @@ Filter by step: Filtrer par statut du parcours Accepted steps: Statuts "Filtered by steps: only %step%": "Filtré par statut du parcours: uniquement %step%" +Filter by socialaction: Filtrer par action d'accompagnement +Accepted socialactions: Actions d'accompagnement +"Filtered by socialactions: only %socialactions%": "Filtré par action d'accompagnement: uniquement %socialactions%" + +Filter by evaluation: Filtrer par évaluation +Accepted evaluations: Évaluations +"Filtered by evaluations: only %evals%": "Filtré par évaluation: uniquement %evals%" + +Filter by activity type: Filtrer par type d'activité +Accepted activitytypes: Types d'activités +"Filtered by activity types: only %activitytypes%": "Filtré par type d'activité: seulement %activitytypes%" + Filter by origin: Filtrer par origine du parcours Accepted origins: Origines "Filtered by origins: only %origins%": "Filtré par origine du parcours: uniquement %origins%" @@ -402,6 +414,10 @@ Filter by closing motive: Filtrer par motif de fermeture Accepted closingmotives: Motifs de clôture "Filtered by closingmotive: only %closingmotives%": "Filtré par motif de clôture: uniquement %closingmotives%" +Filter by administrative location: Filtrer par localisation administrative +Accepted locations: Localisations administratives +"Filtered by administratives locations: only %locations%": "Filtré par localisation administrative: uniquement %locations%" + Filter by confidential: Filtrer par confidentialité Accepted confidentials: '' is confidential: le parcours est confidentiel @@ -420,8 +436,22 @@ is occasional: le parcours est ponctuel is regular: le parcours est régulier "Filtered by intensity: only %intensity%": "Filtré par intensité: uniquement si %intensity%" +Filtered by active on date: Filtrer les parcours actifs à une date +On date: Actifs à cette date +"Filtered by actives courses: active on %ondate%": "Filtrer les parcours actifs: actifs le %ondate%" + +Filtered by active at least one day between dates: Filtrer les parcours actifs au moins un jour dans la période +"Filtered by actives courses: at least one day between %datefrom% and %dateto%": "Filtrer les parcours actifs: au moins un jour entre le %datefrom% et le %dateto%" +Filtered by referrers: Filtrer par référent +Accepted referrers: Référents +"Filtered by referrer: only %referrers%": "Filtré par référent: uniquement %referrers%" + +Filtered by opened between dates: Filtrer les parcours ouverts entre deux dates +Date from: Date de début +Date to: Date de fin +"Filtered by opening dates: between %datefrom% and %dateto%": "Filtrer les parcours ouverts entre deux dates: entre le %datefrom% et le %dateto%" ## aggregators Group people by nationality: Aggréger les personnes par nationalités