Compare commits

...

536 Commits

Author SHA1 Message Date
32187cfe06 Merge remote-tracking branch 'origin/testing' into chill_amli 2022-09-29 10:58:05 +02:00
fdd537b18e Merge branch 'master' into testing 2022-09-29 10:53:48 +02:00
a91b35298a Do use the old name for the table, as it should exists when migrations are executed sequentially 2022-09-28 15:52:19 +02:00
1786fa838f Merge branch '111_exports_suite' into testing 2022-09-28 09:49:33 +02:00
1386ae66de centerDispatchResolver added in construct - was missing 2022-09-27 15:35:45 +02:00
9f2ecff63e exports: ResidentialAddressAt ThirdParty/User Filter, improve query (issue637 et issue638) 2022-09-26 18:52:00 +02:00
78e00b8eba exports: improve translations 2022-09-26 17:40:58 +02:00
e3764f6f91 [export][person] Fixed: use left join on association between works and
goal/results
2022-09-26 17:02:28 +02:00
95f7622923 [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.
2022-09-26 16:45:36 +02:00
2c46886e36 Fix use import and add a "not null" condition on travel time 2022-09-26 14:59:31 +02:00
58eb089d1c Fix use import and add a "not null" condition on travel time 2022-09-26 14:49:51 +02:00
6eff1962df Fix import and add a "not null" condition on duration time 2022-09-26 14:43:04 +02:00
75713af0e0 [export][person] fix alias name for person in CountPersonWithAccompanyingCourse export 2022-09-23 21:58:50 +02:00
6cdb3033db [export][person] Fixed: filter per person's age: use calculatio non year interval 2022-09-23 21:58:38 +02:00
7fe8d0837f [export][person] fix alias name for person in CountPersonWithAccompanyingCourse export 2022-09-23 21:55:00 +02:00
753d6ea481 [export][person] Fixed: filter per person's age: use calculatio non year interval 2022-09-23 21:51:26 +02:00
8cf25415ab Merge branch '111_exports_suite' into testing 2022-09-22 18:16:15 +02:00
89bdc76565 handle unknown in gender aggregator 2022-09-22 18:15:59 +02:00
743e0b9403 Merge branch '111_exports_suite' into testing 2022-09-22 16:58:47 +02:00
b9d4b5650b [Activity][Export] Fixed: use leftJoin on aggregators 2022-09-22 16:58:29 +02:00
4fe4f6a877 DXFeature: ClosingMotiveRepository implements correct interace 2022-09-22 14:02:00 +02:00
4631f04da6 Merge branch 'issue628' into 111_exports_suite 2022-09-22 13:49:00 +02:00
e487bdf7fd fix issue628 when result is null 2022-09-22 13:22:34 +02:00
37662a4187 batch replacing 'actacp' by 'acp' 2022-09-21 18:04:34 +02:00
97d6f35605 Merge branch '111_exports_suite' into testing 2022-09-21 17:31:13 +02:00
cf9b9b3c75 [person][export] Fixed: use left join for related entities in accompanying course aggregators 2022-09-21 17:30:00 +02:00
ca6fde934b [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
2022-09-21 17:28:55 +02:00
01fb93e9e0 [export][activity] count activities only once if the activity is present
multiple times due to JOIN
2022-09-21 17:28:55 +02:00
4e82126bed [activity][export] Fixed: fixed inconsistencies with date filters 2022-09-21 17:28:55 +02:00
8b64933565 [activity][export]: Fixed: rename the alias for accompanying period, to
be suitable for usage with acp filters
2022-09-21 17:28:55 +02:00
6c0715669e change name of échange to activité for amli 2022-09-21 16:45:20 +02:00
58ec294023 Merge branch 'master' into chill_amli 2022-09-21 15:54:21 +02:00
683717e572 Merge branch '111_exports_suite' of gitlab.com:Chill-Projet/chill-bundles into 111_exports_suite 2022-09-21 14:34:04 +02:00
f763a1ed9f Merge branch '111_exports_tests_suite' into 111_exports_suite 2022-09-21 14:33:24 +02:00
3c8dbe56fc exports: create new aggregator tests + minor corrections in aggregators 2022-09-21 14:31:46 +02:00
27306015f4 exports: create new filter tests 2022-09-21 11:29:08 +02:00
c059b7700e 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
2022-09-21 11:11:33 +02:00
5b3cd9eb20 report export test 2022-09-21 10:50:12 +02:00
eb112b8a85 exports: remove duplicate test
cfr commit 18a6a5a7eb
2022-09-21 10:33:49 +02:00
390009b395 export: create new filter tests 2022-09-21 10:17:53 +02:00
37dcbe92c0 export: fix translation 2022-09-21 10:17:53 +02:00
b25a1c3cbb correct import path with filter/aggr 2022-09-21 10:17:53 +02:00
d599792de8 enable commented filter (!?) 2022-09-21 10:17:06 +02:00
42c395ecc9 export: add logger to make feedback on deployed instances easier to debug 2022-09-20 19:09:20 +02:00
052c0e1969 [CS] remove dead code 2022-09-20 18:50:04 +02:00
a72acbe008 Merge branch 'master' of gitlab.com:Chill-Projet/chill-bundles 2022-09-20 18:49:55 +02:00
ca732d89c3 Merge branch 'export_feedback' 2022-09-20 18:49:11 +02:00
5b5470c259 add logger to make prod feedback debug easier 2022-09-20 18:48:13 +02:00
59b22dbb6d [person][export] Fixed: rename the alias for accompanying_period to acp in filter associated with person 2022-09-20 17:02:35 +02:00
35a2d08267 Merge branch 'master' into 111_exports_suite 2022-09-20 16:37:33 +02:00
5af492b2db Merge remote-tracking branch 'origin/111_exports_suite' into testing 2022-09-20 12:18:00 +02:00
70c631b612 Merge remote-tracking branch 'origin/master' into testing 2022-09-20 12:15:29 +02:00
66f7ef8c51 prepare for beta2 release 2022-09-20 12:00:01 +02:00
cdb9d967ff [person] Feature: on evaluation, add an url field on the admin 2022-09-20 11:50:01 +02:00
29b24fa506 [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.
2022-09-20 11:26:05 +02:00
4d192d748f [skeleton] we do not commit composer into repository 2022-09-20 10:03:05 +02:00
6bd0bcff6e 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
2022-09-19 21:27:07 +02:00
1869f44ec6 Merge branch '__111_action_goal_result' into 111_exports_suite 2022-09-19 13:55:16 +02:00
5b148f967f Merge branch 'feature/load-address-bano' into 'master'
Load addresses from bano and best address

See merge request Chill-Projet/chill-bundles!448
2022-09-19 11:51:21 +00:00
91af01336a fix goalResult aggregator 2022-09-19 13:51:03 +02:00
37d49e1123 fix goal aggregator error 2022-09-19 11:46:53 +02:00
d30ac75995 exports: create workAction goalResult aggregator 2022-09-19 11:22:41 +02:00
d907357748 Merge branch '__SocialWorkType_finish' into 111_exports_suite 2022-09-19 09:05:39 +02:00
8129739d27 test for postal code base importer 2022-09-17 10:59:04 +02:00
658e846120 add test for AddressReferenceBaseImporter 2022-09-17 10:44:26 +02:00
0f63548d5a import addresses and postal codes from bestaddress 2022-09-17 09:10:28 +02:00
84cda8845d Feature: command to load addresses from France from bano.openstreetmap.fr 2022-09-17 09:10:28 +02:00
9b3b9f2552 Fixed: possible unexisting variable 2022-09-17 09:10:28 +02:00
58ddf9038d Feature: load french postal code from laposte hexasmal open data 2022-09-17 09:10:28 +02:00
a9b354a6f5 Feature: add constraint to ensure postal code uniqueness and track creation and update of postal code 2022-09-17 09:10:28 +02:00
62ff4998a0 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.
2022-09-17 09:10:28 +02:00
d285e7f875 Merge branch '111_exports_acl' into '111_exports_suite'
Adapt ACL to allow the usage of global ACL

See merge request Chill-Projet/chill-bundles!452
2022-09-17 06:25:09 +00:00
d716e0c2c2 add missing roles and adapt role voter for exports houshold and activity 2022-09-17 08:23:28 +02:00
78ea990189 allow voter to handle export about Accompanying periods on Center 2022-09-17 08:23:28 +02:00
524123c701 [FIX] use AuthorizationHelperInterface instead of implementation in PickCenterType 2022-09-17 08:23:28 +02:00
38cb1fe357 [dev-feature] use an interface for describing CenterRepository (allow mocking in tests 2022-09-17 08:23:28 +02:00
e379d8adb5 [feature] use internal services to check for acl on exports 2022-09-17 08:23:28 +02:00
211a80e9be deprecate chill prophecy train in favor of prophecy-phpunit bridge 2022-09-17 08:23:28 +02:00
91a5db4c14 fix origin alias in qb 2022-09-15 16:07:00 +02:00
5dcd157bd0 export: move Vue component in ChillPersonBundle 2022-09-14 18:16:28 +02:00
fb60808dca export translations: improve title translations in filter/aggrs stack 2022-09-14 17:53:07 +02:00
fceab958bb comment logs 2022-09-14 15:01:28 +02:00
f07847e985 Merge branch '__SocialWorkTypeFilter' into 111_exports_suite 2022-09-14 14:58:35 +02:00
c7e88b3924 select action childrens when selecting parent 2022-09-14 14:57:43 +02:00
424c9239b7 App.vue: improve logs to understand algo 2022-09-14 14:57:23 +02:00
6c191f584f fix bug when member is non-positionné, case not taken into account in if-condition 2022-09-14 12:09:25 +00:00
b2e83892a7 update export alias conventions 2022-09-14 12:34:57 +02:00
478afc893b exports: fix alterQuery and DescribeAction in SocialWorkTypeFilter 2022-09-14 12:34:57 +02:00
4cb6e8e564 exports: fix buildForm in SocialWorkType Filter 2022-09-14 12:34:57 +02:00
9b1e464011 fix form error in App.vue (does not POST from step=export to step=formatter) 2022-09-14 12:33:14 +02:00
71f49db2f4 disable unstable filters/aggregators 2022-09-13 21:05:22 +02:00
6c29638fed exports: fix export_result cell if null (issue 628)
https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/628
2022-09-13 14:54:36 +02:00
18a6a5a7eb exports: move acp ActivityType filter in ChillActivityBundle 2022-09-13 13:27:24 +02:00
d85be8a92e update exports_alias_conventions 2022-09-13 13:09:40 +02:00
42ea1f5813 exports: better lisibility of exports index page 2022-09-13 12:41:03 +02:00
ef7a388f38 exports: fix resetDQLPart('from') issue (632)
https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/632
2022-09-13 12:38:33 +02:00
02b53e23e5 Merge branch '111_exports_suite' into chill_amli 2022-09-12 12:55:43 +02:00
bd45fbc85c merge master into chill amli to have exports 2022-09-12 12:53:26 +02:00
e488d6dadf add CenterResolverDispatcher in construct, was missing 2022-09-12 12:30:57 +02:00
eafe68973a [bug] Temporary bypass Vue component 2022-09-08 14:35:48 +02:00
17494b3e9f Merge branch 'export_vue_multiselect' into 111_exports_suite 2022-09-08 14:27:58 +02:00
661e5458ee bam 2022-09-08 14:17:17 +02:00
307280f595 Merge branch '111_exports_suite' of gitlab.com:Chill-Projet/chill-bundles into 111_exports_suite 2022-09-08 12:50:34 +02:00
d81afb89f2 Revert "rename files for coherence with naming elsewhere"
This reverts commit 5f2622d0d2.
2022-09-08 12:48:19 +02:00
0a0a692eae Revert "rename files for coherence with naming elsewhere"
This reverts commit ff5fab5f50.
2022-09-08 12:46:23 +02:00
8cf9bf4a5f Revert "add querybuilder method to repository"
This reverts commit ebfb030ba6.
2022-09-08 12:46:19 +02:00
b0d77a1656 Revert "adjust property name to make it work with changes calendar bundle"
This reverts commit 822b96f87f.
2022-09-08 12:46:15 +02:00
967c8c62d4 Revert "adjust property name to make it work with changes calendar bundle"
This reverts commit 12c37ddb2c.
2022-09-08 12:45:59 +02:00
12c37ddb2c adjust property name to make it work with changes calendar bundle 2022-09-08 12:07:58 +02:00
822b96f87f adjust property name to make it work with changes calendar bundle 2022-09-08 12:07:58 +02:00
ebfb030ba6 add querybuilder method to repository 2022-09-08 12:05:08 +02:00
ff5fab5f50 rename files for coherence with naming elsewhere 2022-09-08 12:04:11 +02:00
5f2622d0d2 rename files for coherence with naming elsewhere 2022-09-08 12:04:11 +02:00
712c7bc492 add actionRender in construct, it was missing 2022-09-08 12:04:11 +02:00
5c2b2105b2 exports: improve formatter twig template (when multiple order rows) 2022-09-08 11:47:41 +02:00
a817b0bf4c exports: re-enable all modifiers stack with shared filters/aggrs 2022-09-08 11:31:23 +02:00
f10ec3991d exports: put breadcrumb in an include, add a link to go back to the list 2022-09-08 11:04:38 +02:00
e550d64fd4 change class reference 2022-09-08 10:17:22 +02:00
4aaf75a1a4 merge 111_exports_suite into testing branch 2022-09-08 10:03:21 +02:00
1dcff2f23c exports: add condition with distinct alias on join clauses 2022-09-07 21:40:53 +02:00
81359877c4 exports: add condition with distinct alias on join clauses (wip) 2022-09-07 19:45:41 +02:00
3851e65777 fix errors with Role type 2022-09-07 13:38:13 +02:00
482abd3980 remove dump 2022-09-07 12:23:38 +02:00
0f0ec10857 fix rst syntax 2022-09-07 12:18:07 +02:00
d9e602247e fix errors with Role type 2022-09-07 11:09:03 +02:00
9db43f1de3 remove trailing dumps 2022-09-07 10:37:22 +02:00
674629e2bf comment out exports that are not yet ready 2022-09-07 10:37:01 +02:00
1b8b99d5ce Merge remote-tracking branch 'origin/social_action_exports' into 111_exports_suite
# Conflicts:
#	src/Bundle/ChillPersonBundle/Export/Aggregator/PersonAggregators/HouseholdPositionAggregator.php
2022-09-07 10:08:39 +02:00
32c600c155 Merge branch '__geounitstat' into 111_exports_suite (but disable GeoUnit Filter/Aggr) 2022-09-07 10:00:15 +02:00
d3a0c4c283 exports tests: move tests in sub-folder, adapt namespace + fix errors 2022-09-06 16:27:49 +02:00
e42d6c2d77 Merge branch 'master' into testing 2022-09-06 15:33:27 +02:00
da2d68a45d [documentation] new how-to to switch into branch, and add info about gitlab tokens 2022-09-06 15:25:15 +02:00
29cc589bf2 exports: add a new GeographicalUnitStat aggregator 2022-09-06 15:07:28 +02:00
eedfa60bea Merge remote-tracking branch 'origin/111_exports_suite' into 111_exports_suite 2022-09-06 15:04:22 +02:00
1c92f58b3b Merge branch 'tests/fixes-2022-09-05' into 111_exports_suite 2022-09-06 15:02:36 +02:00
d4f3ec368c Merge remote-tracking branch 'origin/social_action_exports' into 111_exports_suite 2022-09-06 14:49:17 +02:00
d245afb009 correct identification of from() 2022-09-06 14:41:40 +02:00
648ec68cfb 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
2022-09-06 14:24:06 +02:00
3295031bcd marital status filter 2022-09-06 10:04:59 +02:00
75bdc335e5 remove usage of deprecated Role into Export 2022-09-05 17:26:36 +02:00
c442529799 fix cs 2022-09-05 14:49:07 +02:00
c4f0ad01d3 disable/comment buildForm in SocialWorkType Filter 2022-09-05 12:37:00 +02:00
8688e4d502 complete maritalStatusFilter 2022-09-05 11:51:17 +02:00
374ad43df6 exports: cleaning code 2022-09-01 10:11:53 +02:00
cae9dddac4 exports: fix acp requestor aggregator 2022-08-31 17:58:37 +02:00
436f1d6c61 exports: add new requestor aggregator 2022-08-31 15:28:11 +02:00
d86b86487f fix some remarks in 111_export review 2022-08-31 15:27:03 +02:00
bee5336c1d fix translation with calendar exports 2022-08-31 15:26:28 +02:00
6dbee02d82 Merge branch 'master' into social_action_exports 2022-08-30 13:23:41 +02:00
7eb75b709c Merge branch '111_exports' into social_action_exports 2022-08-30 13:17:37 +02:00
72b5fcf998 minor changes 2022-08-30 13:14:40 +02:00
nobohan
bf44b6b90d export: geographical unit filter 2022-08-29 16:22:36 +02:00
nobohan
81f04d0184 export: add a geographical unit entity (migration file) 2022-08-29 15:34:33 +02:00
nobohan
f99f6d5240 export: add a geographical unit entity 2022-08-29 15:21:05 +02:00
0d35dfc303 Merge branch 'calendar/finalization' into chill_amli 2022-08-29 11:32:46 +02:00
0784b00793 bugfix for person document 2022-08-29 11:30:48 +02:00
b5139ec460 exports: improve activity DateAggregator
- put orderby at the end
- addSelect with TO_CHAR() function (cancel EXRACT())
- uniformize addSelect cases
- orderBy with variable
- improve getLabels
2022-08-25 14:58:25 +02:00
f5baa2c152 exports: DurationAggregator with approximative(*) date interval
(*) for more precise, we need dql interval function
2022-08-25 13:49:12 +02:00
e1ec2dc25c exports: DurationAggregator: test qb addSelect for compute date interval 2022-08-25 13:49:12 +02:00
2c151c2ec9 exports: add new duration aggregator (wip) 2022-08-25 13:49:12 +02:00
cf642d2783 month year aggregator working 2022-08-25 13:08:24 +02:00
f3ceee7485 Merge branch '111_exports' into social_action_exports 2022-08-25 09:28:01 +02:00
9854fb0664 cancel reason aggregator + start of monthYearAggregator 2022-08-25 09:27:30 +02:00
7173e4be4a exports: fix activity DateAggregator; add customs DQL Date/Time functions 2022-08-24 15:44:18 +02:00
52902e905a exports: test all activity filters/aggregators and fix errors 2022-08-24 11:36:54 +02:00
dea7982ca8 exports: add activity linked_to_acp new aggregators (wip) 2022-08-22 19:04:48 +02:00
c4944cce17 exports: move activity old aggregators in PersonAggregators folder 2022-08-22 17:12:33 +02:00
f8f17a6a06 exports: move activity old filters in PersonFilters folder 2022-08-22 13:37:20 +02:00
95ea3bf30e refactor builder form in a filter 2022-08-22 13:29:22 +02:00
248d56e598 exports: add activity linked_to_acp new filters 2022-08-22 13:28:50 +02:00
9af620ece5 fix translations 2022-08-22 10:17:44 +02:00
4a1615afd5 export, fix merge translations 2022-08-22 10:02:38 +02:00
2bf5e934e9 merge 111_exports 2022-08-18 12:19:01 +02:00
03d098e5e1 aggregators added - monthYearAggregator not finished 2022-08-18 12:15:30 +02:00
1de853a88b fix count appointment indicator 2022-08-18 10:58:05 +02:00
3eb07121ce filters added 2022-08-18 10:57:15 +02:00
05fd35cfb4 all indicators added 2022-08-18 10:56:22 +02:00
e357899d78 Merge branch 'social_action_exports' into 111_exports 2022-08-17 17:48:43 +02:00
62562cc57e rename querybuilder calendar 2022-08-17 17:37:05 +02:00
dfbaaef778 add average duration indicator calendar 2022-08-17 17:36:42 +02:00
d04d758f27 exports: activityDate filter enable to activity linked to acl 2022-08-17 17:14:24 +02:00
c199bb5534 exports: activity improve translations 2022-08-17 17:02:21 +02:00
26cd677501 exports: activity new indicators works + improve translations 2022-08-17 16:07:20 +02:00
3c5d533c58 first indicator for calendar exports 2022-08-17 16:04:46 +02:00
5060906591 more attempts to make dynamic form work 2022-08-17 16:03:58 +02:00
c17036fcda exports: fix activitytype filter/aggregator appliedTo activity_person and too activity_acl 2022-08-17 15:28:51 +02:00
c09c7a9615 exports: add new modifiers declarations to split filter/aggregators between activities linked to person <> acp 2022-08-17 14:58:20 +02:00
df9a5071c7 exports: display group as a breadcrumb in template
add a private method in controller which could be moved maybe in ExportManager
2022-08-17 14:56:24 +02:00
01f6eb1a8f fix merge conflict in translations 2022-08-17 11:57:46 +02:00
27c2fb1c1f merge with 111_exports 2022-08-17 11:56:22 +02:00
d9b668e614 exports: create new activity linked to acp exports indicators + title/descr/groups translations 2022-08-17 11:44:15 +02:00
57d00df460 exports: split namespace between activity linked to person <> acp 2022-08-17 10:12:29 +02:00
94cee14a11 exports: complete modifiers 2022-08-17 09:50:35 +02:00
4ebe064538 exports: improve translations 2022-08-17 09:49:33 +02:00
fb18d9fb56 exports: improve activity translations 2022-08-16 17:12:35 +02:00
b2c1c0ec76 add filter residential address at thirdparty of category 2022-08-16 16:19:39 +02:00
42122f5832 exports: use Declarations in activity exports 2022-08-16 15:37:54 +02:00
8093c532d1 exports: overwrite activity translations 2022-08-16 15:30:12 +02:00
2c53f92a2e small refactorisations 2022-08-16 15:13:47 +02:00
4c91bf5084 household position aggregator for person 2022-08-16 15:07:03 +02:00
c2c409998b exports: precise translation when acp filters/aggreg are combined with others 2022-08-16 14:52:53 +02:00
bde623e806 add second indicator for person exports 2022-08-16 10:47:36 +02:00
a9e83d3dc8 delete double translations 2022-08-16 10:42:13 +02:00
afbedf3a4a Merge branch '111_exports' into social_action_exports 2022-08-16 10:19:04 +02:00
4e12684f98 add condition on join clause to improve combined Filters/Aggregators 2022-08-11 18:09:25 +02:00
619ae4e458 improve social work actions translations 2022-08-11 17:43:20 +02:00
b03950d0f7 improve translations for treating agent 2022-08-11 17:33:58 +02:00
f7993eaf2b remove ACP_SHARED Declaration 2022-08-11 17:17:52 +02:00
d27c52b526 Split Job and Scope Aggregators 2022-08-11 17:13:24 +02:00
312a23e91f split ReferrerAggregator 2022-08-11 16:57:44 +02:00
93eb9220a7 split ReferrerFilter, logiquement ce sont des filtres différents 2022-08-11 16:41:15 +02:00
b614149fab translations chain 2022-08-11 16:39:54 +02:00
c45ef7d74f exports: fix initiateQuery to always begin from acp entity 2022-08-11 15:05:08 +02:00
12cae472d6 Merge branch '111_exports' into social_action_exports 2022-08-11 14:08:33 +02:00
b8d187c82b exports evaluation: fix root query from acp 2022-08-11 14:04:15 +02:00
6e467a62d4 exports: simplify with querybuilder method to getRootAlias 2022-08-11 12:04:41 +02:00
70488a935d exports: test if a join is already loaded
when a specific join has to be shared between combined filters/aggregators, we need to check if it has been already loaded.
we cannot load it into indicator because result is wrong
2022-08-11 12:01:54 +02:00
18c17818f6 exports: add ChildrenNumber Aggregator 2022-08-10 17:17:29 +02:00
bc5d610f80 exports: add on_date parameter on Composition Filter/Aggregator 2022-08-10 16:50:59 +02:00
c693dfde66 a same join() in filter and aggregator make error if we combine them 2022-08-10 16:33:05 +02:00
3463ff8e2e exports: add Composition Filter and Aggregator in Household exports 2022-08-10 16:25:38 +02:00
3f4d4497af exports: add new countHousehold export 2022-08-10 14:35:38 +02:00
0c2c364eb6 trying to get dynamic form to work 2022-08-10 13:58:51 +02:00
8efbf02f64 exports: add new MaxDate Filter for evaluation 2022-08-10 10:57:32 +02:00
208d625258 merge conflict resolved 2022-08-10 10:37:19 +02:00
6eddb420cc fix nationality aggregator 2022-08-10 10:36:32 +02:00
3c451209c7 fix nationality aggregator 2022-08-10 10:35:32 +02:00
955d4a9e7a exports: add new EvaluationType Filter and Aggregator 2022-08-10 10:21:05 +02:00
20949fdb5c Merge branch '111_exports' into social_action_exports 2022-08-10 10:05:26 +02:00
2c7a128348 vendee filters and aggregators moved to root folder 2022-08-09 16:49:38 +02:00
97ec921a0a small adjustments 2022-08-09 16:15:27 +02:00
5b4b7473c5 residential address at user filter done 2022-08-09 16:14:41 +02:00
6010559936 translations added 2022-08-09 16:14:17 +02:00
a3cae28613 residential address at thirdparty filter : not done! 2022-08-09 16:14:01 +02:00
9a2af662c0 use translatablestringhelper 2022-08-09 16:13:24 +02:00
93d0fbead5 exports: create new indicator to count evaluations 2022-08-09 16:06:32 +02:00
72a5a6cd27 hop 2022-08-09 15:22:05 +02:00
b8bb0b1209 exports: improve translations 2022-08-09 15:11:01 +02:00
b1bfb2dd95 raise the similarity matcher percentage 2022-08-09 12:06:25 +02:00
ed1dde4713 exports: share referrer aggregator 2022-08-09 11:30:42 +02:00
f817ca9671 exports: add a new intensity aggregator 2022-08-09 11:11:53 +02:00
5940e2c0b7 exports: add new Confidential and Emergency Aggregators 2022-08-08 17:39:53 +02:00
ace2cb6151 exports: add new AdministrativeLocation aggregator 2022-08-08 16:59:03 +02:00
ec7325ebbd exports: add a new ClosingMotive aggregator 2022-08-08 16:36:01 +02:00
643f37509f exports: share Scope and Job Aggregators 2022-08-08 16:12:34 +02:00
fcd7ae3b8d exports: add new aggregators for acp 2022-08-08 15:22:56 +02:00
f37e7cf393 moving files into separate folders for better overview 2022-08-04 14:49:48 +02:00
faef81a90b moving files into separate folders for better overview 2022-08-04 14:25:23 +02:00
95006b9643 exports: use Declarations for each applyOn 2022-08-04 14:12:25 +02:00
2f8bafa2e2 exports: split yaml services accompanying_period and accompanying_course
* the 3 old filters are applyOn person -> move it in PersonFilters
* there is a conditionnal option to enable them -> split yaml files
2022-08-04 12:46:06 +02:00
25e008f3e2 Merge branch '111_exports' into social_action_exports 2022-08-04 12:17:59 +02:00
43d45a5d04 tests: fix missing use statement 2022-08-04 11:30:52 +02:00
ea1a53ed37 tests: add missing FilterTest 2022-08-04 11:30:41 +02:00
28599adf48 tests on new ActiveOnDate and ActiveOneDayBetweenDates filters 2022-08-04 09:07:45 +02:00
e3743d3593 tests new filters ReferrerFilter and OpenBetweenDatesFilter (wip.. test failed) 2022-08-04 09:07:45 +02:00
2413c986ed exports: add a new GeographicUnitStat Filter (wip) 2022-08-04 09:07:45 +02:00
6921e4a40d exports: add new RequestorFilter 2022-08-03 20:13:29 +02:00
ac0c221267 start activity exports adaptations and new additions 2022-08-03 16:48:33 +02:00
32bb868b2b start with person aggregators 2022-08-03 16:21:57 +02:00
2ff346d7d8 Merge branch '111_exports' into social_action_exports 2022-08-03 15:22:32 +02:00
9e2ef9eae6 fix redundant referrerFilter 2022-08-03 15:18:22 +02:00
cfd590442f moving files into separate folders for better overview 2022-08-03 15:15:52 +02:00
cf7cf664a9 person filters added 2022-08-03 14:35:55 +02:00
ebb6ee4a41 agefilter added for persons 2022-08-02 15:07:56 +02:00
c72dc83c06 adjust countperson to make it work again 2022-08-02 13:44:28 +02:00
ea12c60154 more aggregator tests 2022-08-02 12:31:29 +02:00
40d92d11fc renaming aggregator job and scope for generic use 2022-08-02 11:30:30 +02:00
28dc99ff3f tests added for filters and some aggregators 2022-08-02 11:24:08 +02:00
20c1a287d8 merge with 111_export 2022-08-02 11:23:28 +02:00
6e439adce2 export new administrative location filter 2022-08-01 16:50:02 +02:00
4794e5e7b5 export new ActivityTypeFilter 2022-08-01 15:45:31 +02:00
bc2209319a export new EvaluationFilter 2022-08-01 14:32:11 +02:00
b511517a0f export new SocialActionFilter 2022-08-01 12:45:04 +02:00
758c56482b exports: add new ActiveOnDate filter 2022-08-01 11:30:32 +02:00
c401e34d63 exports: add new ActiveOneDayBetweenDates filter 2022-08-01 11:16:17 +02:00
7677b8aaa0 exports: add new OpenBetweenDates filter 2022-07-31 21:01:12 +02:00
dd06a262a2 remove commented unused 2022-07-31 19:22:39 +02:00
c1f578a811 exports: add new referrer filter, with mechanism to use it in acp or acpw context 2022-07-29 12:13:32 +02:00
635fe819a6 rename more generic for use in other exports 2022-07-28 15:57:02 +02:00
d59a22597d Merge branch '111_exports' into social_action_exports 2022-07-28 15:47:59 +02:00
8fdb2747ac Merge branch 'master' into social_action_exports 2022-07-28 15:45:46 +02:00
e6b66216ae actiontype, goal, and result aggregators created 2022-07-28 15:44:17 +02:00
d6b3ba48c0 fix the possibility to filter with multiple jobs or scopes 2022-07-28 12:05:42 +02:00
13b15f5057 renaming of class to avoid confusion 2022-07-28 11:24:36 +02:00
5c7513acd7 rename translations chains 2022-07-28 10:53:54 +02:00
8085fe2c17 referrer scope aggregator for social work actions 2022-07-27 21:25:13 +02:00
562a5678ef referrer job aggregator for social work actions 2022-07-27 21:19:13 +02:00
30501a68e3 referrer aggregator 2022-07-27 21:07:05 +02:00
b9186ed6e0 tests: write a simple Export test for new indicators + fix depreciation with BrowserKit client
cfr: https://symfony.com/doc/4.4/components/browser_kit.html#creating-a-client
2022-07-27 18:39:58 +02:00
5c82ccc49d user scope and job filter on socialwork actions 2022-07-27 17:08:50 +02:00
455a50d292 Merge branch 'social_action_exports' of gitlab.com:Chill-Projet/chill-bundles into social_action_exports 2022-07-27 16:05:04 +02:00
a40e2de91d rename filters to avoid confusion 2022-07-27 16:04:40 +02:00
357c7db5f1 rename filters to avoid confusion 2022-07-27 16:02:11 +02:00
ab2b2bc235 some advancements on dynamic socialaction filter 2022-07-27 15:50:56 +02:00
287d8f546b add simple test file for each new filter 2022-07-27 14:26:14 +02:00
8bbca12487 fix ProphecyTrait in AbstractFilter to use it with each filter 2022-07-27 14:23:21 +02:00
3dabd031f4 fix tests failed in exports 2022-07-27 14:23:21 +02:00
232cf96cb2 tests: write a simple Filter test for EmergencyFilter 2022-07-27 14:23:21 +02:00
cb42b68c33 filter for agents traitants 2022-07-26 15:46:16 +02:00
d5d38053cd renaming of export to avoid confusion + start of agent traitant filter 2022-07-26 14:43:21 +02:00
b12e5e78b6 reorganize into separate config file 2022-07-26 13:18:15 +02:00
a15c88dd35 merge 111 branch into this one 2022-07-26 11:57:54 +02:00
5f329101e1 title changed back to longer version 2022-07-26 11:44:20 +02:00
3a73af4fd0 Merge branch 'social_action_exports' of gitlab.com:Chill-Projet/chill-bundles into social_action_exports 2022-07-26 11:42:29 +02:00
9798cb8a09 due to excel limit, title can not exceed 31 characters 2022-07-26 11:41:16 +02:00
5ba8c4babc fixes to make count social actions export work 2022-07-26 11:22:17 +02:00
a06a2c9592 start of social actions export 2022-07-26 10:41:24 +02:00
ef827a1322 revert stepFilter to uniq selection
(cfr ddac410b2e)
2022-07-25 18:24:24 +02:00
e728c00671 exports: create intensity filter 2022-07-25 16:51:58 +02:00
da224ea4bc exports: create emergency filter 2022-07-25 16:35:36 +02:00
ddac410b2e exports: fix choice forms builder with multiple selections 2022-07-25 16:20:14 +02:00
e42355c0d1 Merge branch 'chill_amli' of https://gitlab.com/Chill-Projet/chill-bundles into chill_amli 2022-07-25 16:04:52 +02:00
941d7b0352 swiftmailer replaced by mailerinterface 2022-07-25 16:04:32 +02:00
43bedc41a7 exports: create confidential filter 2022-07-25 15:42:51 +02:00
0a06118ac3 swiftmailer replaced by mailerinterface 2022-07-25 15:41:44 +02:00
ef5b3b24e4 exports: create closingmotive filter 2022-07-25 14:47:08 +02:00
057617c1cb separate accompanying_period exports in yaml service definition 2022-07-25 14:04:23 +02:00
e56520f4b1 exports: create origin filter 2022-07-25 12:36:22 +02:00
04ca61be81 rename data attribute 2022-07-25 12:35:35 +02:00
bf0ca7b777 exports: fix logic in userScope and userJob filter 2022-07-25 11:51:23 +02:00
fdd83de233 Merge branch '605_load-bootstrap' into 'master'
issue 605: improve the way chill loads bootstrap module

See merge request Chill-Projet/chill-bundles!446
2022-07-25 08:27:16 +00:00
97e8b3c9c1 fix yarn/webpack error when compiling
(correction on commit 340310be62)
2022-07-25 09:56:15 +02:00
5744a68f5f issue 605: remove old scratch sass framework 2022-07-22 14:13:16 +02:00
ddd0aeb7b4 issue 605: improve the way chill loads bootstrap module
custom variables are loaded before bootstrap variables, custom maps are loaded after
2022-07-22 14:08:43 +02:00
d5d3866122 fix conflict with where clause in indicator ; initiate a filter test 2022-07-20 23:17:05 +02:00
0d38d4df40 social issues filter: improve render with parents, add parents in where clause 2022-07-20 19:33:42 +02:00
bb22317eb1 exports: add new social issues filter (wip) 2022-07-20 16:38:46 +02:00
b608976326 exports: add new step filter 2022-07-20 13:47:15 +02:00
7f1dadc136 user scope/job filters: improve translation in csv 2022-07-20 11:24:13 +02:00
ff8a32a321 user scope filter: change query (TO CHECK) 2022-07-20 11:23:03 +02:00
d7c1498882 english corrected 2022-07-19 14:20:38 +00:00
70f118011b exports: add a new user job filter 2022-07-19 15:04:57 +02:00
da556248d4 Merge branch 'master' of gitlab.com:Chill-Projet/chill-bundles 2022-07-19 13:05:58 +02:00
881e608c87 exports: add a new user scope filter 2022-07-18 18:45:02 +02:00
80df7b2f72 update changelog 2022-07-18 16:37:34 +02:00
e6caeecc50 add composition type to admin menu 2022-07-18 16:36:13 +02:00
juminet
8651d3232a Merge branch 'issue612_add_field_accperiodresource_doc_gen' into 'master'
Issue612 add field accperiodresource doc gen

See merge request Chill-Projet/chill-bundles!445
2022-07-18 13:43:43 +00:00
nobohan
d55ba36619 [person] add comment field to accompanying period resource docgen 2022-07-18 15:39:00 +02:00
34923df43c exports avg_accompanying_course_duration with closingdate parameter 2022-07-18 14:38:48 +02:00
903ac2ff69 exports: init sum_accompanying_course_duration (replace list_accompanying_course) 2022-07-14 14:42:53 +02:00
3d0dd46ea5 fix count_accompanying_course query: no centers, not count DRAFT courses 2022-07-14 14:27:55 +02:00
e323937405 Temporarily disable ACL on new exports 2022-07-14 10:26:26 +02:00
2f42b9411a which acl is CONFIDENTIAL_CRUD ? 2022-07-13 14:19:12 +02:00
fe595860f2 replace 'hideLabel' form option by 'fullWidth'
sometimes we want fullWidth and display label
2022-07-13 13:36:20 +02:00
bda203d11d add accompanying courses exports in a group 2022-07-13 10:41:30 +02:00
3cdcccc037 add activity exports in a group 2022-07-13 10:32:27 +02:00
2ce145cace wip.. setting acl for new accompanying course exports 2022-07-13 10:14:54 +02:00
ec38dc4d21 add person exports in a group 2022-07-13 10:14:54 +02:00
28ed09b9d9 exports: add 2 new exports for accompanying courses 2022-07-12 15:18:37 +02:00
56bed12886 exports: fix errors and depreciations 2022-07-12 14:19:50 +02:00
e9fca1288a exports: fix errors and depreciations 2022-07-12 13:07:48 +02:00
9fa3d596bb split title and description for list activity export 2022-07-12 13:06:29 +02:00
3782cf35ff Merge branch 'master' into testing 2022-07-11 20:03:37 +02:00
68c1833584 fixed: CS 2022-07-11 12:56:48 +02:00
191b8ecf81 fixed: query for index in PersonDocumentACLAwareRepository 2022-07-11 12:56:34 +02:00
e7ba42de1f fixed: voter and permissions in accompanying course document 2022-07-11 12:55:02 +02:00
ef9a155cc1 bootstrap styles in export section 2022-07-11 11:09:46 +02:00
8b145ebc11 fixed: migration of databse with address on same day fails 2022-07-11 03:32:15 +02:00
722cf789ec remove dump mesages 2022-07-11 01:13:17 +02:00
cec1588e91 Merge branch 'master' into calendar/finalization 2022-07-10 22:00:19 +02:00
1fff90d3a7 Merge remote-tracking branch 'origin/master' into calendar/finalization 2022-07-10 20:45:19 +02:00
340310be62 bootstrap styles in export section 2022-07-07 16:08:34 +02:00
7b64269bb5 fix show/hide in exports filter + better styles 2022-07-07 15:20:50 +02:00
3da26a4d45 fix rst syntax 2022-07-07 12:51:59 +02:00
5662609c23 doc for install and synchronization 2022-07-07 12:48:20 +02:00
57277e5b87 deprecate move status, which is not in use 2022-07-07 10:41:33 +02:00
f72c0576ef check for user email before writing msgraph metadata 2022-07-06 21:36:37 +02:00
c8028b8e60 set correct duration for subscribing to remote calendar 2022-07-06 17:09:04 +02:00
989c4e90e5 enable show_hide on export pages 2022-07-05 16:39:48 +02:00
fcac977d13 existing exports, better translations 2022-07-05 15:46:43 +02:00
2b5accaeac override export activity translations 2022-07-05 13:42:32 +02:00
2400bd48d1 fix instruction for installing in dev 2022-07-04 12:46:49 +02:00
c841821ed4 fix loading calendar range fixtures (3) 2022-07-03 22:19:26 +02:00
8830b6dc23 fix loading calendar range fixtures (2) 2022-07-03 22:18:21 +02:00
accee53f5a fix loading calendar range fixtures 2022-07-03 22:11:32 +02:00
20b70f9eed fix typing and package deps to satisfy DI compilation 2022-07-03 21:31:57 +02:00
d375abf593 display locals calendar events in Calendar app 2022-07-03 18:30:00 +02:00
7d281ea50f fix error in handling remove range message 2022-07-02 15:19:50 +02:00
e5aada5561 add locals on MyCalendar app 2022-07-02 15:13:02 +02:00
2d71ba9078 remove existing calendars from proxy remote 2022-07-02 14:42:54 +02:00
3df06e1eba add api endpoint for listing calendars 2022-07-02 14:31:27 +02:00
26a0ba4756 count events on remote calendars 2022-07-01 23:36:30 +02:00
a604902074 add planning view for calendar app 2022-07-01 18:23:46 +02:00
8bbd3b01d9 Add a mention on next calendars on search results 2022-07-01 18:01:42 +02:00
3dcec5d44a layout for filter on the list page 2022-07-01 15:16:21 +02:00
f513749780 fix cs 2022-07-01 15:05:13 +02:00
93c5e83454 add validation on calendar 2022-07-01 15:05:03 +02:00
9c1324e9ec fix transform to activity 2022-07-01 14:09:46 +02:00
87403e509f recreate calendar range when an event is deleted 2022-07-01 13:43:30 +02:00
0276ec1bc7 fix compilation errors in production 2022-07-01 13:19:02 +02:00
2a6974610f associate location on ms calendar remote 2022-07-01 12:12:48 +02:00
014e281d13 add sms on calendar list 2022-06-30 21:40:40 +02:00
18be028a87 display location in calendar list 2022-06-30 20:24:33 +02:00
a5b5eea146 use the location from the calendar range, when associating a range with the calendar 2022-06-30 20:05:28 +02:00
71b6b158ba fix list of locations in calendar 2022-06-30 19:38:32 +02:00
d87c6305fd improve some layout on create calendar 2022-06-30 18:36:02 +02:00
64d7c1fe99 remove unused files 2022-06-30 17:41:56 +02:00
4aa8436399 finalization of my calendar ranges 2022-06-30 17:40:43 +02:00
54ffa999d8 comment out temporarily start of extra menu item admin -take up later 2022-06-30 15:42:51 +02:00
034269b87c remove dumps 2022-06-30 15:41:16 +02:00
8844e3e64a fix use import of scope 2022-06-30 15:40:55 +02:00
e2634b0b0f fix right to create person for amli use 2022-06-30 15:29:30 +02:00
9e93e2a3f9 edit location on existing ranges 2022-06-29 23:47:12 +02:00
adad4313a6 handle remote events 'isAllDay' and fix cs 2022-06-29 15:29:22 +02:00
849e7158e4 adding location to ranges and more control on MyCalendarRanges 2022-06-29 15:28:37 +02:00
809a0a38ab Merge branch 'master' into calendar/my-calendar-fixes-with-ts 2022-06-28 10:33:00 +02:00
13dae00a2c feature: copy ranges 2022-06-27 21:27:37 +02:00
ad63df85c7 Merge branch 'master' of https://gitlab.com/Chill-Projet/chill-bundles into chill_amli 2022-06-27 16:45:36 +02:00
b5d5338002 re-add feature to patch event 2022-06-27 16:32:09 +02:00
922c5c5f5c working with webpack 5 2022-06-27 15:28:35 +02:00
a9bc98738e fix weird bug with ts import 2022-06-27 14:51:48 +02:00
3ea630748a add feature add and delete range 2022-06-24 18:52:28 +02:00
75b2f6419e working calendar ranges with a subset of features 2022-06-24 17:24:56 +02:00
a845fddf2e force authenticating on remote calendar 2022-06-24 15:08:10 +02:00
ca44ebccf3 first reactive calendar 2022-06-23 16:24:54 +02:00
d8f80f3d02 wip on app2 2022-06-23 12:26:48 +02:00
6b3b010631 wip debug 2022-06-21 17:30:50 +02:00
5a5958704b Merge remote-tracking branch 'origin/master' into calendar/my-calendar-fixes-with-ts 2022-06-21 17:28:21 +02:00
8c99fc0089 basic structure for using modules in stores and convert to TS 2022-06-20 09:50:40 +02:00
6c4f116e85 split store into multiple files 2022-06-17 18:04:57 +02:00
5c08abc2f6 Merge branch 'calendar/finalization' into calendar_changes 2022-06-17 17:29:05 +02:00
138d431786 fix duplication action in controller 2022-06-17 17:10:10 +02:00
03d64995d9 Merge remote-tracking branch 'origin/calendar/synchro-msgraph' into calendar/finalization 2022-06-17 17:04:37 +02:00
59e24ff39d Merge remote-tracking branch 'origin/master' into calendar/finalization 2022-06-17 16:53:56 +02:00
b7c3300884 Merge branch 'calendar/synchro-msgraph' into calendar/finalization 2022-06-17 16:52:52 +02:00
f987a6b5e0 modifications to adapt to AMLI 2022-06-14 15:05:09 +02:00
3c685d50dd household composition type added to admin panel 2022-06-14 12:11:29 +02:00
28c952521f command for sending bulk sms with tests 2022-06-14 01:15:58 +02:00
4c0fef4f44 send message from cli through ovh if configured 2022-06-13 21:39:29 +02:00
616be5cc8a bootstrap fake sms from cli 2022-06-13 14:01:07 +02:00
9e4fd6183e more fixes for calendar syncs 2022-06-10 14:24:46 +02:00
c92077926e db constraint with unique remoteId if set, handle sync with tests 2022-06-10 00:26:24 +02:00
f149b24802 small fixes on sync from remote 2022-06-09 17:06:57 +02:00
6e48a042b3 prevent change when syncing from remote 2022-06-09 15:44:48 +02:00
64e07c54fa first bootstrap for handling calendar range sync frommsgraph 2022-06-09 15:17:42 +02:00
d95d97f8fe msgraph: subscription for users 2022-06-08 22:43:49 +02:00
e75b258e44 handling sync for calendar invite 2022-06-08 13:06:31 +02:00
c329862e96 fix loading for ms graph connector 2022-06-08 13:05:28 +02:00
fbf1c4365d fix typing of oauth2client methods 2022-06-08 12:56:04 +02:00
168aff81f4 fixes in template with new route and forms 2022-06-06 22:07:04 +02:00
fc7d2fcca3 fix cs 2022-06-06 17:50:21 +02:00
537fefee15 add private comment on calendar 2022-06-06 17:50:11 +02:00
dc22d18e6a remove timezone from calendar and calendar range 2022-06-06 17:49:51 +02:00
dab9204ec7 fix association mapping for many-to-many SocialAction <-> Evaluation 2022-06-06 17:37:01 +02:00
543d30acb9 display of calendar form 2022-06-06 17:11:43 +02:00
c31886fea3 calendar app: fix error on users on startup 2022-06-06 15:13:36 +02:00
6c3043f6fc some layout for calendar app 2022-06-03 15:25:36 +02:00
3a88ea0b0f answer vue component: use union type instead of enum 2022-06-03 14:39:35 +02:00
c804462f15 list calendar by period: date range and query acl aware 2022-06-01 23:04:59 +02:00
089c92d0ee layout of page calendar list 2022-06-01 12:34:07 +02:00
4217524e63 answer to invite: some layout 2022-06-01 11:25:45 +02:00
4fef8ec46e remove import of default variables bootstrap 2022-06-01 11:25:22 +02:00
76b49d22e7 module in ts to give an answer to calendar invite 2022-05-31 22:45:21 +02:00
840c7e1084 always import base bootstrap variable before customizing them 2022-05-28 01:58:49 +02:00
5dcf7000f1 Merge branch 'master' into calendar/synchro-msgraph 2022-05-28 01:45:33 +02:00
ec536871aa Merge remote-tracking branch 'origin/master' into calendar/synchro-msgraph 2022-05-28 00:30:02 +02:00
1c79e25579 first component for pushing calendar answer 2022-05-27 14:29:10 +02:00
7c0bdc5abe some data in msgraph synchro and answer on calendar invite 2022-05-26 19:25:59 +02:00
59a64e9a62 wip: continue sync to remote 2022-05-26 00:10:25 +02:00
782436ee2e send first calendar on ms graph 2022-05-25 21:32:00 +02:00
f962b7543f fix relation inside calendar, bootstrap messenger for handling create and update calendar entity 2022-05-25 10:22:30 +02:00
b22f361368 fix cs 2022-05-24 23:04:24 +02:00
5ea12a581b calendar app: fix concerned groups 2022-05-24 22:56:08 +02:00
eb8b8c6939 calendar app: set mainUser and options for form 2022-05-24 22:45:43 +02:00
352b5b41b0 reformat to indentation 2 2022-05-24 16:22:05 +02:00
17778ab346 proxy: do not show calendar events when they are present in database 2022-05-23 23:34:33 +02:00
9dba558bef remove unused methods 2022-05-23 22:50:48 +02:00
00e87f8c75 set mainUser event if not set before 2022-05-23 22:50:33 +02:00
a5998ce99d handle moving an event 2022-05-23 22:43:19 +02:00
d8e7a7f1af allow to select a date on a calendar 2022-05-23 22:34:11 +02:00
d32a69a657 prevent loading users calendar multiple times when adding multiple users 2022-05-23 18:27:05 +02:00
00ef58301e remove logs 2022-05-23 17:59:56 +02:00
3fbdcdc431 calendar app: change mainUser if mainUser is selected 2022-05-23 17:59:21 +02:00
7dcd5be735 fixes for editing calendar 2022-05-23 17:30:40 +02:00
a025883a5d create event by selecting a calendar range 2022-05-23 16:48:17 +02:00
38c7c8de60 remove log messages 2022-05-23 15:07:58 +02:00
f825c69ce5 fetch remote calendar and show on FullCalendar frame 2022-05-23 15:00:32 +02:00
e2052fe71d set indent for js on two spaces [ci-skip] 2022-05-20 20:49:20 +02:00
a349089f3d change text color and remove unused consts 2022-05-20 20:44:55 +02:00
461fc1c41f add toggle button to show/add remote and ranges for each user in calendar 2022-05-20 20:40:24 +02:00
2b770036a5 fix behaviour of CalendarType in Controller and add field users to CalendarType 2022-05-20 19:44:28 +02:00
67fad5d764 create generic data transformer with get an object by id, and add test
for CalendarType
2022-05-20 19:26:21 +02:00
b6e0379583 fix cs and add EntityToIdTransformer 2022-05-20 15:52:13 +02:00
dba0e84781 calendar app: fix list for showing users and their colors (not in rgith place in page) 2022-05-18 13:33:56 +02:00
b89edc29af calendar app: do not store events twice in vuex 2022-05-17 17:31:17 +02:00
b50c51bc2a calendar items wit round-robin color 2022-05-17 17:16:48 +02:00
f68c69d443 refactor to use store for storing events 2022-05-17 16:57:31 +02:00
682dc2174e some style changes 2022-05-17 16:26:21 +02:00
c0f9bf35ac fix toggle to show or hide appointments 2022-05-17 16:05:00 +02:00
0c61edd0d6 everything changed to use store for faster re-render of calendar 2022-05-17 15:49:50 +02:00
9ceb66e2da refactor CalendarRange repository and available range action 2022-05-17 12:26:34 +02:00
8b1271a466 use groups annotation with Serializer prefix and serialize mainUser 2022-05-17 12:14:37 +02:00
d921f6c9e5 add true iso8601 format to date normalizer 2022-05-17 12:14:03 +02:00
85f00fdb28 refactor store in calendar 2022-05-16 17:20:15 +02:00
6144f2439a wip store mainUser in both store and input data with event 2022-05-16 15:30:35 +02:00
1eee8c6c49 fix issue when loading category 2022-05-16 15:28:05 +02:00
a4caf733f1 fix error in commit 2022-05-16 13:12:40 +02:00
4be3efc619 work on create calendar 2022-05-13 13:49:24 +02:00
7859439f0b fix remote calendar compiler to allow to compile kernel without any configuration 2022-05-12 13:53:20 +02:00
c60a54eb39 WIP: create and edit calendar 2022-05-12 12:50:57 +02:00
f4b1a25a67 quick fixes to make calendar app work 2022-05-12 12:50:55 +02:00
0ec0708807 add voter for calendar 2022-05-12 12:49:32 +02:00
d150a8251b calendar range: remove on remote after removal in the database 2022-05-12 12:49:32 +02:00
dd13e631ac calendar range: update on remote after an unpdate 2022-05-12 12:49:32 +02:00
c892f7de7b prevent loop when posting calendarrange to remote and updating remoteId 2022-05-12 12:49:32 +02:00
e895da31d7 wip: synchro of calendar range 2022-05-12 12:49:32 +02:00
ba8a2327be fallback on schedule items when no access to calendar 2022-05-12 12:49:32 +02:00
38d828cf36 refactor access to calendar and use real userid 2022-05-12 12:49:32 +02:00
ee4a6e08fb refactor command to acquire admin consent 2022-05-12 12:49:32 +02:00
d570145385 refactor and rename classes 2022-05-12 12:49:32 +02:00
8abce5ab85 fix cs + read remote calendar based on user access 2022-05-12 12:49:32 +02:00
811798e23f wip: synchro 2022-05-12 12:49:32 +02:00
9935af0497 msgraph: add metadata to users to connect with default calendar 2022-05-12 12:49:32 +02:00
5331f1becc [wip] first impl for getting authorization for admin 2022-05-12 12:49:32 +02:00
a7ec843509 remote calendar: authenticate to ms graph api 2022-05-12 12:49:32 +02:00
0212193940 add null remote connector and basic interface 2022-05-12 12:49:32 +02:00
aecdfa6b12 reactive my calendar on new route 2022-05-12 12:49:32 +02:00
88784b7f7e copy ranges from one day to another fixed 2022-05-12 12:23:39 +02:00
c339f7a828 fix merge conflict 2022-05-11 21:31:08 +02:00
5ceddc747d start copy dates 2022-05-11 21:27:47 +02:00
f8a9cafacb add today button again 2022-05-11 15:15:13 +02:00
ce78177ab7 only fetch data for view and adapt all callbacks for header buttons 2022-05-11 15:15:13 +02:00
7c693b7495 change height of calendar for better overview 2022-05-11 15:15:13 +02:00
6631e77df5 more changes to save modified, deleted new plages immediately 2022-05-11 15:15:13 +02:00
c77af0bc4a start refactoring 2022-05-11 15:15:13 +02:00
e28d17a131 ranges are immediately deleted as well as saved (needs refactoring) 2022-05-11 15:15:13 +02:00
2b1c3d1aff comment out console logs 2022-05-11 15:15:13 +02:00
f3444b6a58 change ranges to 15min instead of 30 + save new range immediately 2022-05-11 15:15:13 +02:00
901b496030 quick fixes to make calendar app work 2022-05-09 18:01:09 +02:00
20e1feebf4 add voter for calendar 2022-05-09 18:00:54 +02:00
d1f87718d6 Merge remote-tracking branch 'origin/master' into calendar/finalization 2022-05-09 16:51:42 +02:00
cebdfdaa30 start refactoring 2022-05-06 17:38:08 +02:00
5a1a33b6a4 ranges are immediately deleted as well as saved (needs refactoring) 2022-05-06 16:25:03 +02:00
28c0b8994b comment out console logs 2022-05-06 15:55:05 +02:00
9c070cd8ae change ranges to 15min instead of 30 + save new range immediately 2022-05-06 15:54:35 +02:00
0f6dc3d997 reactive my calendar on new route 2022-04-30 16:21:48 +02:00
718 changed files with 33785 additions and 11408 deletions

View File

@@ -18,10 +18,8 @@ max_line_length = 80
[COMMIT_EDITMSG]
max_line_length = 0
<<<<<<< Updated upstream
=======
[*.{js, vue, ts}]
indent_size = 2
indent_style = space
>>>>>>> Stashed changes

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
.composer/*
composer
composer.phar
composer.lock
docs/build/

View File

@@ -11,14 +11,29 @@ and this project adheres to
## Unreleased
<!-- write down unreleased development here -->
* [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
* [person][export] Fixed: use left join for related entities in accompanying course aggregators
## 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
### 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
* [search]: Order of birthdate fields changed in advanced search to avoid confusion.
* [workflow]: Constraint added to workflow (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/675)
* [household]: Reposition and cut button for enfant hors menage have been deleted (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/620)
## Test releases
* [admin]: Add crud for composition type in admin (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/611)
### 2022-05-30

BIN
composer

Binary file not shown.

View File

@@ -19,10 +19,12 @@
"graylog2/gelf-php": "^1.5",
"knplabs/knp-menu-bundle": "^3.0",
"knplabs/knp-time-bundle": "^1.12",
"knpuniversity/oauth2-client-bundle": "^2.10",
"league/csv": "^9.7.1",
"nyholm/psr7": "^1.4",
"ocramius/package-versions": "^1.10 || ^2",
"odolbeau/phone-number-bundle": "^3.6",
"ovh/ovh": "^3.0",
"phpoffice/phpspreadsheet": "^1.16",
"ramsey/uuid-doctrine": "^1.7",
"sensio/framework-extra-bundle": "^5.5",
@@ -36,6 +38,7 @@
"symfony/http-foundation": "^4.4",
"symfony/intl": "^4.4",
"symfony/mailer": "^5.4",
"symfony/messenger": "^5.4",
"symfony/mime": "^5.4",
"symfony/monolog-bundle": "^3.5",
"symfony/security-bundle": "^4.4",
@@ -49,6 +52,7 @@
"symfony/webpack-encore-bundle": "^1.11",
"symfony/workflow": "^4.4",
"symfony/yaml": "^4.4",
"thenetworg/oauth2-azure": "^2.0",
"twig/extra-bundle": "^3.0",
"twig/intl-extra": "^3.0",
"twig/markdown-extra": "^3.3",

View File

@@ -10,17 +10,17 @@
Exports
*******
Export is an important issue for the Chill software : users should be able to :
Export is an important issue within the Chill software : users should be able to :
- compute statistics about their activity ;
- list "things" which make part of their activities.
- list "things" which are a part of their activities.
The `main bundle`_ provides a powerful framework to build custom queries with re-usable parts across differents bundles.
.. contents:: Table of content
:local:
.. seealso::
.. seealso::
`The issue where this framework was discussed <https://git.framasoft.org/Chill-project/Chill-Main/issues/9>`_
Provides some information about the pursued features and architecture.
@@ -32,37 +32,37 @@ Concepts
Some vocabulary: 3 "Export elements"
------------------------------------
Four terms are used for this framework :
Four terms are used for this framework :
exports
provides some basic operation on the date. Two kind of exports are available :
Exports
provide some basic operation on the data. Two kinds of exports are available :
- computed data : it may be "the number of people", "the number of activities", "the duration of activities", ...
- list data : it may be "the list of people", "the list of activity", ...
- list data : it may be "the list of people", "the list of activities", ...
filters
The filters make a filter on the date: it removes some information the user doesn't want to introduce in the computation done by export. In other word, filters make a filter...
Filters
The filters create a filter on the data: it removes some information the user doesn't want to introduce in the computation done by the export.
Example of filter: "people under 18 years olds", "activities between the 1st of June and the 31st December", ...
Example of a filter: "people under 18 years olds", "activities between the 1st of June and the 31st December", ...
aggregators
The aggregator aggregates the data into some group (some software use the term 'bucket').
Aggregators
The aggregator aggregates the data into some group (some software use the term 'bucket').
Example of aggregator : "group people by gender", "group people by nationality", "group activity by type", ...
Example of an aggregator : "group people by gender", "group people by nationality", "group activity by type", ...
formatters
The formatters format the data into a :class:`Symfony\Component\HttpFoundation\Response`, which will be returned "as is" by the controller to the web client.
Formatters
The formatters format the data into a :class:`Symfony\Component\HttpFoundation\Response`, which will be returned "as is" by the controller to the web client.
Example of formatter: "format data as CSV", "format data as ods spreadsheet", ...
Example of a formatter: "format data as CSV", "format data as an ods spreadsheet", ...
Anatomy of an export
---------------------
An export may be explained as a sentence, where each part of this sentence refers to one or multiple exports element. Examples :
An export can be thought of as a sentence where each part of this sentence refers to one or multiple export elements. Examples :
**Example 1**: Count the number of people having at least one activity in the last 12 month, and group them by nationality and gender, and format them in a CSV spreadsheet.
Here :
Here :
- *count the number of people* is the export part
- *having at least one activity* is the filter part
@@ -72,10 +72,10 @@ Here :
Note that :
- aggregators, filters, exports and aggregators are cross-bundle. Here the bundle *activity* provides a filter which apply on an export provided by the person bundle ;
- there may exists multiple aggregator or filter for one export. Currently, only one export is allowed.
- Aggregators, filters, exports and formatters are cross-bundle. Here the bundle *activity* provides a filter which is applied on an export provided by the person bundle ;
- Multiple aggregator or filter for one export may exist. Currently, only one export is allowed.
The result might be :
The result might be :
+-----------------------+----------------+---------------------------+
| Nationality | Gender | Number of people |
@@ -89,9 +89,9 @@ The result might be :
| France | Female | 150 |
+-----------------------+----------------+---------------------------+
**Example 2**: Count the average duration of an activity with type "meeting", which occurs between the 1st of June and the 31st of December, group them by week, and format the data in a OpenDocument spreadsheet.
**Example 2**: Count the average duration of an activity with type "meeting", which occurs between the 1st of June and the 31st of December, group them by week, and format the data in an OpenDocument spreadsheet.
Here :
Here :
- *count the average duration of an activity* is the export part
- *activity with type meeting* is a filter part
@@ -102,7 +102,7 @@ Here :
The result might be :
+-----------------------+----------------------+
| Week | Number of activities |
| Week | Number of activities |
+=======================+======================+
| 2015-10 | 10 |
+-----------------------+----------------------+
@@ -116,77 +116,77 @@ The result might be :
Authorization and exports
-------------------------
Exports, filters and aggregators should not make see data the user is not allowed to see.
Exports, filters and aggregators should not show data the user is not allowed to see within the application.
In other words, developers are required to take care of user authorization for each export.
It should exists a special role that should be granted to users which are allowed to build exports. For more simplicity, this role should apply on center, and should not requires special circles.
There should be a specific role that grants permission to users who are allowed to build exports. For more simplicity, this role should apply on a center, and should not require special circles.
How does the magic works ?
How does the magic work ?
===========================
To build an export, we rely on the capacity of the database to execute queries with aggregate (i.e. GROUP BY) and filter (i.e. WHERE) instructions.
An export is an SQL query which is initiated by an export, and modified by aggregators and filters.
.. note::
.. note::
**Example**: Count the number of people having at least one activity in the last 12 month, and group them by nationality and gender
1. The report initiate the query
1. The report initiates the query
.. code-block:: SQL
SELECT count(people.*) FROM people
2. The filter add a where and join clause :
2. The filter adds a where and join clause :
.. code-block:: SQL
SELECT count(people.*) FROM people
RIGHT JOIN activity
SELECT count(people.*) FROM people
RIGHT JOIN activity
WHERE activity.date IS BETWEEN now AND 6 month ago
3. The aggregator "nationality" add a GROUP BY clause and a column in the SELECT statement:
3. The aggregator "nationality" adds a GROUP BY clause and a column in the SELECT statement:
.. code-block:: sql
SELECT people.nationality, count(people.*) FROM people
RIGHT JOIN activity
WHERE activity.date IS BETWEEN now AND 6 month ago
SELECT people.nationality, count(people.*) FROM people
RIGHT JOIN activity
WHERE activity.date IS BETWEEN now AND 6 month ago
GROUP BY nationality
4. The aggregator "gender" do the same job as the nationality aggregator : it adds a GROUP BY clause and a column in the SELECT statement :
4. The aggregator "gender" does the same job as the nationality aggregator : it adds a GROUP BY clause and a column in the SELECT statement :
.. code-block:: sql
SELECT people.nationality, people.gender, count(people.*)
FROM people RIGHT JOIN activity
WHERE activity.date IS BETWEEN now AND 6 month ago
SELECT people.nationality, people.gender, count(people.*)
FROM people RIGHT JOIN activity
WHERE activity.date IS BETWEEN now AND 6 month ago
GROUP BY nationality, gender
Each filter, aggregator and filter may collect parameters from the user by providing a form. This form is appended to the export form. Here is an example.
Each filter, aggregator and filter may collect parameters from the user through a form. This form is appended to the export form. Here is an example.
.. figure:: /_static/screenshots/development/export_form-fullpage.png
The screenshot show the export form for ``CountPeople`` (Nombre de personnes). The filter by date of birth is checked (*Filtrer par date de naissance de la personne*), which allow to show a subform, which is provided by the :class:`Chill\PersonBundle\Export\Filter\BirthdateFilter`. The other filter, which are unchecked, does not show the subform.
The screenshot shows the export form for ``CountPeople`` (Nombre de personnes). The filter by date of birth is checked (*Filtrer par date de naissance de la personne*), which triggers a subform, which is provided by the :class:`Chill\PersonBundle\Export\Filter\BirthdateFilter`. The other unchecked filter does not show the subform.
Two aggregators are also checked : by Country of birth (*Aggréger les personnes par pays de naissance*, corresponding class is :class:`Chill\PersonBundle\Export\Aggregator\CountryOfBirthAggregator`, which also open a subform. The aggregator by gender (*Aggréger les personnes par genre*) is also checked, but there is no corresponding subform.
Two aggregators are also checked : by Country of birth (*Aggréger les personnes par pays de naissance*, the corresponding class is :class:`Chill\PersonBundle\Export\Aggregator\CountryOfBirthAggregator`, which also triggers a subform. The aggregator by gender (*Aggréger les personnes par genre*) is also checked, but there is no corresponding subform.
The Export Manager
------------------
The Export manager (:class:`Chill\MainBundle\Export\ExportManager` is the central class which register all exports, aggregators, filters and formatters.
The Export manager (:class:`Chill\MainBundle\Export\ExportManager` is the central class which registers all exports, aggregators, filters and formatters.
The export manager is also responsible for orchestrating the whole export process, producing a :class:`Symfony\FrameworkBundle\HttpFoundation\Request` to each export request.
The export manager is also responsible for orchestrating the whole export process, producing a :class:`Symfony\FrameworkBundle\HttpFoundation\Request` for each export request.
The export form step
--------------------
The form step allow to build a form, aggregating different parts of the module.
The form step allows you to build a form, combining different parts of the module.
The building of forms is separated between different subform, which are responsible for rendering their part of the form (aggregators, filters, and export).
The building of forms is split into different subforms, where each one is responsible for rendering their part of the form (aggregators, filters, and export).
.. figure:: /_static/puml/exports/form_steps.png
:scale: 40%
@@ -194,12 +194,12 @@ The building of forms is separated between different subform, which are responsi
The formatter form step
-----------------------
The formatter form is processed *after* the user filled the export form. It is built the same way, but receive in parameters the data entered by the user on the previous step (i.e. export form). It may then adapt it accordingly (example: show a list of columns selected in aggregators).
The formatter form is processed *after* the user filled the export form. It is built the same way, but receives the data entered by the user on the previous step as parameters (i.e. export form). It may then adapt it accordingly (example: show a list of columns selected in aggregators).
Processing the export
---------------------
The export process may be explained by this schema :
The export process can be explained by this schema :
.. figure:: /_static/puml/exports/processing_export.png
:scale: 40%
@@ -219,20 +219,20 @@ This is an example of the ``CountPerson`` export :
:language: php
:linenos:
* **Line 36**: the ``getType`` function return a string. This string will be used to find the aggregtors and filters which will apply to this export.
* **Line 41**: a simple description to help user to understand what your export does.
* **Line 36**: the ``getType`` function returns a string. This string will be used to find the aggregtors and filters which will apply to this export.
* **Line 41**: a simple description to help users understand what your export does.
* **Line 46**: The title of the export. A summary of what your export does.
* **Line 51**: The list of roles requires to execute this export.
* **Line 51**: The list of roles required to execute this export.
* **Line 56**: We initiate the query here...
* **Line 59**: We have to filter the query with centers the users checked in the form. We process the $acl variable to get all ``Center`` object in one array
* **Line 63**: We create the query, with a query builder.
* **Line 74**: We simply returns the result, but take care of hydrating the results as an array.
* **Line 103**: return the list of formatters types which are allowed to apply on this filter
* **Line 59**: We have to filter the query with centers the users checked in the form. We process the $acl variable to get all ``Center`` objects in one array
* **Line 63**: We create the query with a query builder.
* **Line 74**: We return the result, but make sure to hydrate the results as an array.
* **Line 103**: return the list of formatter types which are allowed to be applied on this filter
Filters
-------
This is an example of the *filter by birthdate*. This filter ask some information in a form (`buildForm` is not empty), and this form must be validated. To performs this validations, we implement a new Interface: :class:`Chill\MainBundle\Export\ExportElementValidatedInterface`:
This is an example of the *filter by birthdate*. This filter asks some information through a form (`buildForm` is not empty), and this form must be validated. To perform this validation, we implement a new Interface: :class:`Chill\MainBundle\Export\ExportElementValidatedInterface`:
.. literalinclude:: /_static/code/exports/BirthdateFilter.php
:language: php

View File

@@ -14,6 +14,13 @@
Installation & Usage
####################
.. toctree::
:maxdepth: 2
prod.rst
prod-calendar-sms-sending.rst
msgraph-configure.rst
Requirements
************
@@ -38,13 +45,36 @@ Clone or download the chill-app 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 your variables
=========================
2. Prepare composer download of sources
=======================================
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:
As you are running in dev, you must configure an auth token for getting the source code.
1. copy the file as ``.env``: ``cp .env.dist .env``
2. you may replace some variables inside ``.env``
.. 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```:
.. 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
Configure your environment variables, by creating a .env.local file and override the desired variables.
**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 <https://docs.openstack.org/swift/latest/api/temporary_url_middleware.html#secret-keys>`_.
@@ -104,6 +134,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 <your token>`
The auth token should appears now in the directory :code:`.composer`:
.. code-block: bash
$ cat .composer/auth.json
{
"gitlab-token": {
"gitlab.com": "<your token>"
}
}
See also "how to switch branch and get new dependencies".
Operations
**********
@@ -211,6 +264,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-<my-branch>@dev"
}
2. mount into the php container, and run `composer update`
Build the documentation API
===========================

View File

@@ -0,0 +1,320 @@
Configure Chill for calendar sync and SSO with Microsoft Graph (Outlook)
========================================================================
Chill offers the possibility to:
* authenticate users using Microsoft Graph, with relatively small adaptations;
* synchronize calendar in both ways (`see the user manual for a large description of the feature <https://gitea.champs-libres.be/Chill-project/manuals>`_).
Both can be configured separately (synchronising calendars without SSO, or SSO without calendar). When calendar sync is configured without SSL, the user's email address is the key to associate Chill's users with Microsoft's ones.
Configure SSO
-------------
On Azure side
*************
Configure an app with the Azure interface, and give it the name of your choice.
Grab the tenant's ID for your app, which is visible on the main tab "Vue d'ensemble":
.. figure:: ./saml_login_id_general.png
This the variable which will be named :code:`SAML_IDP_APP_UUID`.
Go to the "Single sign-on" ("Authentication unique") section. Choose "SAML" as protocol, and fill those values:
.. figure:: ./saml_login_1.png
1. The :code:`entityId` seems to be arbitrary. This will be your variable :code:`SAML_ENTITY_ID`;
2. The url response must be your Chill's URL appended by :code:`/saml/acs`
3. The only used attributes is :code:`emailaddress`, which must match the user's email one.
.. figure:: ./saml_login_2.png
You must download the certificate, as base64. The format for the download is :code:`cer`: you will remove the first and last line (the ones with :code:`-----BEGIN CERTIFICATE-----` and :code:`-----END CERTIFICATE-----`), and remove all the return line. The final result should be something as :code:`MIIAbcdef...XyZA=`.
This certificat will be your :code:`SAML_IDP_X509_CERT` variable.
The url login will be filled automatically with your tenant id.
Do not forget to provider user's accesses to your app, using the "Utilisateurs et groupes" tab:
.. figure:: ./saml_login_appro.png
You must know have gathered all the required variables for SSO:
.. code-block::
SAML_BASE_URL=https://test.chill.be # must be
SAML_ENTITY_ID=https://test.chill.be # must match the one entered
SAML_IDP_APP_UUID=42XXXXXX-xxxx-xxxx-xxxx-xxxxxxxxxxxx
SAML_IDP_X509_CERT: MIIC...E8u3bk # truncated
Configure chill app
*******************
* add the bundle :code:`hslavich/oneloginsaml-bundle`
* add the configuration file (see example above)
* configure the security part (see example above)
* add a user SAML factory into your src, and register it
.. code-block:: yaml
# config/packages/hslavich_onelogin.yaml
parameters:
saml_base_url: '%env(resolve:SAML_BASE_URL)%'
saml_entity_id: '%env(resolve:SAML_ENTITY_ID)%'
saml_idp_x509cert: '%env(resolve:SAML_IDP_X509_CERT)%'
saml_idp_app_uuid: '%env(resolve:SAML_IDP_APP_UUID)%'
hslavich_onelogin_saml:
# Basic settings
idp:
entityId: 'https://sts.windows.net/%saml_idp_app_uuid%/'
singleSignOnService:
url: 'https://login.microsoftonline.com/%saml_idp_app_uuid%/saml2'
binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
singleLogoutService:
url: 'https://login.microsoftonline.com/%saml_idp_app_uuid%/saml2'
binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
x509cert: '%saml_idp_x509cert%'
sp:
entityId: '%saml_entity_id%'
assertionConsumerService:
url: '%saml_base_url%/saml/acs'
binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
singleLogoutService:
url: '%saml_base_url%/saml/'
binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
privateKey: ''
# Optional settings.
baseurl: '%saml_base_url%/saml'
strict: true
debug: true
security:
nameIdEncrypted: false
authnRequestsSigned: false
logoutRequestSigned: false
logoutResponseSigned: false
wantMessagesSigned: false
wantAssertionsSigned: false
wantNameIdEncrypted: false
requestedAuthnContext: true
signMetadata: false
wantXMLValidation: true
signatureAlgorithm: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
digestAlgorithm: 'http://www.w3.org/2001/04/xmlenc#sha256'
contactPerson:
technical:
givenName: 'Tech User'
emailAddress: 'techuser@example.com'
support:
givenName: 'Support User'
emailAddress: 'supportuser@example.com'
organization:
en:
name: 'Example'
displayname: 'Example'
url: 'http://example.com'
.. code-block:: yaml
# config/security.yaml
# merge this with other existing configurations
security:
providers:
saml_provider:
# Loads user from user repository
entity:
class: Chill\MainBundle\Entity\User
property: username
firewalls:
default:
# saml part:
saml:
username_attribute: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
# weird behaviour in dev environment... configuration seems different
# username_attribute: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
# Use the attribute's friendlyName instead of the name
use_attribute_friendly_name: false
user_factory: user_from_saml_factory
persist_user: true
check_path: saml_acs
login_path: saml_login
logout:
path: /saml/logout
.. code-block:: php
// src/Security/SamlFactory.php
namespace App\Security;
use Chill\MainBundle\Entity\User;
use Hslavich\OneloginSamlBundle\Security\Authentication\Token\SamlTokenInterface;
use Hslavich\OneloginSamlBundle\Security\User\SamlUserFactoryInterface;
class UserSamlFactory implements SamlUserFactoryInterface
{
public function createUser(SamlTokenInterface $token)
{
$attributes = $token->getAttributes();
$user = new User();
$user->setUsername($attributes['http://schemas.microsoft.com/identity/claims/displayname'][0]);
$user->setLabel($attributes['http://schemas.microsoft.com/identity/claims/displayname'][0]);
$user->setPassword('');
$user->setEmail($attributes['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'][0]);
$user->setAttributes($attributes);
return $user;
}
}
Configure sync
--------------
The sync processe might be configured in the same app, or into a different app.
The synchronization processes use Oauth2.0 for authentication and authorization.
.. note::
Two flows are in use:
* we authenticate "on behalf of a user", to allow users to see their own calendar or other user's calendar into the web interface.
Typically, when the page is loaded, Chill first check that an authorization token exists. If not, the user is redirected to Microsoft Azure for authentification and a new token is grabbed (most of the times, this is transparent for users).
* Chill also acts "as a machine", to synchronize calendars with a daemon background.
One can access the configuration using this screen (it is quite well hidden into the multiple of tabs):
.. figure:: ./oauth_app_registration.png
You can find the oauth configuration on the "Securité > Autorisations" tab, and click on "application registration" (not translated).
Add a redirection URI for you authentification:
.. figure:: ./oauth_api_authentification.png
The URI must be "your chill public url" with :code:`/connect/azure/check` at the end.
Allow some authorizations for your app:
.. figure:: ./oauth_api_autorisees.png
Take care of the separation between autorization "on behalf of a user" (déléguée), or "for a machine" (application).
Some explanation:
* Users must be allowed to read their user profile (:code:`User.Read`), and the profile of other users (:code:`User.ReadBasicAll`);
* They must be allowed to read their calendar (:code:`Calendars.Read`), and the calendars shared with them (:code:`Calendars.Read.Shared`);
The sync daemon must have write access:
* the daemon must be allowed to read all users and their profile, to establish a link between them and the Chill's users: (:code:`Users.Read.All`);
* it must also be allowed to read and write into the calendars (:code:`Calendars.ReadWrite.All`)
* for sending invitation to other users, the permission (:code:`Mail.Send`) must be granted.
At this step, you might choose to accept those permissions for all users, or let them do it by yourself.
Grab your client id:
.. figure:: ./oauth_api_client_id.png
This will be your :code:`OAUTH_AZURE_CLIENT_ID` variable.
Generate a secret:
.. figure:: ./oauth_api_secret.png
This will be your :code:`OAUTH_AZURE_CLIENT_SECRET` variable.
And get you azure's tenant id, which is the same as the :code:`SAML_IDP_APP_UUID` (see above).
Your variables will be:
.. code-block::
OAUTH_AZURE_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
OAUTH_AZURE_CLIENT_TENANT=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
OAUTH_AZURE_CLIENT_SECRET: 3-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Then, configure chill:
Enable the calendar sync with microsoft azure:
.. code-block:: yaml
# config/packages/chill_calendar.yaml
chill_calendar:
remote_calendars_sync:
microsoft_graph:
enabled: true
and configure the oauth client:
.. code-block:: yaml
# config/packages/knp_oauth2_client.yaml
knpu_oauth2_client:
clients:
azure:
type: azure
client_id: '%env(OAUTH_AZURE_CLIENT_ID)%'
client_secret: '%env(OAUTH_AZURE_CLIENT_SECRET)%'
redirect_route: chill_calendar_remote_connect_azure_check
redirect_params: { }
tenant: '%env(OAUTH_AZURE_CLIENT_TENANT)%'
url_api: 'https://graph.microsoft.com/'
default_end_point_version: '2.0'
You can now process for the first api authorization on the application side, (unless you did it in the Azure interface), and get a first token, by using :
:code:`bin/console chill:calendar:msgraph-grant-admin-consent`
This will generate a url that you can use to grant your app for your tenant. The redirection may fails in the browser, but this is not relevant: if you get an authorization token in the CLI, the authentication works.
Run the processes to synchronize
--------------------------------
The calendar synchronization is processed using symfony messenger. It seems to be intersting to configure a queue (in the postgresql database it is the most simple way), and to run a worker for synchronization, at least in production.
The association between chill's users and Microsoft's users is done by this cli command:
.. code-block::
bin/console chill:calendar:msgraph-user-map-subscribe
This command:
* will associate the Microsoft's user metadata in our database;
* and, most important, create a subscription to get notification when the user alter his calendar, to sync chill's event and ranges in sync.
The subscription least at most 3 days. This command should be runned:
* at least each time a user is added;
* and, at least, every three days.
In production, we advise to run it at least every day to get the sync working.

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

View File

@@ -0,0 +1,27 @@
Send short messages (SMS) with calendar bundle
==============================================
To activate the sending of messages, you should run this command on a regularly basis (using, for instance, a cronjob):
.. code-block:: bash
bin/console chill:calendar:send-short-messages
A transporter must be configured for the message to be effectively sent.
Configure OVH Transporter
-------------------------
Currently, this is the only one transporter available.
For configuring this, simply add this config variable in your environment:
```env
SHORT_MESSAGE_DSN=ovh://applicationKey:applicationSecret@endpoint?consumerKey=xxxx&sender=yyyy&service_name=zzzz
```
In order to generate the application key, secret, and consumerKey, refers to their `documentation <https://docs.ovh.com/gb/en/api/first-steps-with-ovh-api/>`_.
Before to be able to send your first sms, you must enable your account, grab some credits, and configure a sender. The service_name is an internal configuration generated by OVH.

View File

@@ -0,0 +1,48 @@
.. Copyright (C) 2014-2019 Champs Libres Cooperative SCRLFS
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
A copy of the license is included in the section entitled "GNU
Free Documentation License".
Installation for production
###########################
An installation use these services, which are deployed using docker containers:
* a php-fpm image, which run the Php and Symfony code for Chill;
* a nginx image, which serves the assets, and usually proxy the php requests to the fpm image;
* a redis server, which stores the cache, sessions (this is currently hardcoded in the php image), and some useful keys (like wopi locks);
* a postgresql database. The use of postgresql is mandatory;
* a relatorio service, which transform odt templates to full documents (replacing the placeholders);
Some external services:
* (required) an openstack object store, configured with `temporary url <https://docs.openstack.org/swift/latest/api/temporary_url_middleware.html>` configured (no openstack users is required). This is currently the only way to store documents from chill;
* a mailer service (SMTP)
* (optional) a service for verifying phone number. Currently, only Twilio is possible;
* (optional) a service for sending Short Messages (SMS). Currently, only Ovh is possible;
The `docker-compose.yaml` file of chill app is a basis for a production install. The environment variable in the ```.env``` and ```.env.prod``` should be overriden by environment variables, or ```.env.local``` files.
This should be adapted to your needs:
* The image for php and nginx apps are pre-compiled images, with the default configuration and bundle. If they do not fullfill your needs, you should compile your own images.
.. TODO:
As the time of writing (2022-07-03) those images are not published yet.
* Think about how you will backup your database. Some adminsys find easier to store database outside of docker, which might be easier to administrate or replicate.
Tweak symfony messenger
=======================
Calendar sync is processed using symfony messenger.
You can tweak the configuration
Going further:
* Configure the saml login and synchronisation with Outlook api

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

View File

@@ -0,0 +1,63 @@
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
,User::class,acpw.referrers,acpwuser
,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
,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,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
,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
1 Entity Join Attribute Alias
2 AccompanyingPeriod::class acp
3 AccompanyingPeriodWork::class acp.works acpw
4 AccompanyingPeriodParticipation::class acp.participations acppart
5 Location::class acp.administrativeLocation acploc
6 ClosingMotive::class acp.closingMotive acpmotive
7 UserJob::class acp.job acpjob
8 Origin::class acp.origin acporigin
9 Scope::class acp.scopes acpscope
10 SocialIssue::class acp.socialIssues acpsocialissue
11 User::class acp.user acpuser
12 AccompanyingPeriodWork::class acpw
13 AccompanyingPeriodWorkEvaluation::class acpw.accompanyingPeriodWorkEvaluations workeval
14 User::class acpw.referrers acpwuser
15 SocialAction::class acpw.socialAction acpwsocialaction
16 Goal::class acpw.goals goal
17 Result::class acpw.results result
18 AccompanyingPeriodParticipation::class acppart
19 Person::class acppart.person partperson
20 AccompanyingPeriodWorkEvaluation::class workeval
21 Evaluation::class workeval.evaluation eval
22 Goal::class goal
23 Result::class goal.results goalresult
24 Person::class person
25 Center::class person.center center
26 HouseholdMember::class partperson.householdParticipations householdmember
27 MaritalStatus::class person.maritalStatus personmarital
28 VendeePerson::class vp
29 VendeePersonMineur::class vpm
30 ResidentialAddress::class resaddr
31 ThirdParty::class resaddr.hostThirdParty tparty
32 ThirdParty::class tparty
33 ThirdPartyCategory::class tparty.categories tpartycat
34 HouseholdMember::class householdmember
35 Household::class householdmember.household household
36 Person::class householdmember.person memberperson
37 memberperson.center membercenter
38 Household::class household
39 HouseholdComposition::class household.compositions composition
40 Activity::class activity
41 Person::class activity.person actperson
42 AccompanyingPeriod::class activity.accompanyingPeriod acp
43 Person::class activity_person_having_activity.person person_person_having_activity
44 ActivityReason::class activity_person_having_activity.reasons reasons_person_having_activity
45 ActivityType::class activity.activityType acttype
46 Location::class activity.location actloc
47 SocialAction::class activity.socialActions actsocialaction
48 SocialIssue::class activity.socialIssues actsocialssue
49 ThirdParty::class activity.thirdParties acttparty
50 User::class activity.user actuser
51 User::class activity.users actusers
52 ActivityReason::class activity.reasons actreasons
53 Center::class actperson.center actcenter
54 ActivityReason::class actreasons
55 ActivityReasonCategory::class actreason.category actreasoncat
56 Calendar::class cal
57 CancelReason::class cal.cancelReason calcancel
58 Location::class cal.location calloc
59 User::class cal.user caluser
60 VendeePerson::class vp
61 SituationProfessionelle::class vp.situationProfessionelle vpprof
62 StatutLogement::class vp.statutLogement vplog
63 TempsDeTravail::class vp.tempsDeTravail vptt

View File

@@ -0,0 +1,71 @@
# Export conventions
Add condition with distinct alias on each export join clauses (Indicators + Filters + Aggregators)
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 |
| | User::class | acpw.referrers | acpwuser |
| | 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 |
| | 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 | 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 |
| | 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 |

View File

@@ -17,10 +17,11 @@ 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;
use Chill\MainBundle\Repository\UserRepositoryInterface;
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
@@ -55,7 +56,7 @@ final class ActivityController extends AbstractController
private ActivityTypeCategoryRepository $activityTypeCategoryRepository;
private ActivityTypeRepository $activityTypeRepository;
private ActivityTypeRepositoryInterface $activityTypeRepository;
private CenterResolverManagerInterface $centerResolver;
@@ -73,9 +74,11 @@ final class ActivityController extends AbstractController
private ThirdPartyRepository $thirdPartyRepository;
private UserRepositoryInterface $userRepository;
public function __construct(
ActivityACLAwareRepositoryInterface $activityACLAwareRepository,
ActivityTypeRepository $activityTypeRepository,
ActivityTypeRepositoryInterface $activityTypeRepository,
ActivityTypeCategoryRepository $activityTypeCategoryRepository,
PersonRepository $personRepository,
ThirdPartyRepository $thirdPartyRepository,
@@ -86,6 +89,7 @@ final class ActivityController extends AbstractController
EventDispatcherInterface $eventDispatcher,
LoggerInterface $logger,
SerializerInterface $serializer,
UserRepositoryInterface $userRepository,
CenterResolverManagerInterface $centerResolver
) {
$this->activityACLAwareRepository = $activityACLAwareRepository;
@@ -100,6 +104,7 @@ final class ActivityController extends AbstractController
$this->eventDispatcher = $eventDispatcher;
$this->logger = $logger;
$this->serializer = $serializer;
$this->userRepository = $userRepository;
$this->centerResolver = $centerResolver;
}
@@ -371,7 +376,7 @@ final class ActivityController extends AbstractController
if ($request->query->has('activityData')) {
$activityData = $request->query->get('activityData');
if (array_key_exists('durationTime', $activityData)) {
if (array_key_exists('durationTime', $activityData) && $activityType->getDurationTimeVisible() > 0) {
$durationTimeInMinutes = $activityData['durationTime'];
$hours = floor($durationTimeInMinutes / 60);
$minutes = $durationTimeInMinutes % 60;
@@ -390,26 +395,36 @@ final class ActivityController extends AbstractController
}
}
if (array_key_exists('personsId', $activityData)) {
if (array_key_exists('personsId', $activityData) && $activityType->getPersonsVisible() > 0) {
foreach ($activityData['personsId'] as $personId) {
$concernedPerson = $this->personRepository->find($personId);
$entity->addPerson($concernedPerson);
}
}
if (array_key_exists('professionalsId', $activityData)) {
if (array_key_exists('professionalsId', $activityData) && $activityType->getThirdPartiesVisible() > 0) {
foreach ($activityData['professionalsId'] as $professionalsId) {
$professional = $this->thirdPartyRepository->find($professionalsId);
$entity->addThirdParty($professional);
}
}
if (array_key_exists('location', $activityData)) {
if (array_key_exists('usersId', $activityData) && $activityType->getUsersVisible() > 0) {
foreach ($activityData['usersId'] as $userId) {
$user = $this->userRepository->find($userId);
if (null !== $user) {
$entity->addUser($user);
}
}
}
if (array_key_exists('location', $activityData) && $activityType->getLocationVisible() > 0) {
$location = $this->locationRepository->find($activityData['location']);
$entity->setLocation($location);
}
if (array_key_exists('comment', $activityData)) {
if (array_key_exists('comment', $activityData) && $activityType->getCommentVisible() > 0) {
$comment = new CommentEmbeddable();
$comment->setComment($activityData['comment']);
$comment->setUserId($this->getUser()->getid());

View File

@@ -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.

View File

@@ -0,0 +1,87 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
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;
class BySocialActionAggregator implements AggregatorInterface
{
private SocialActionRender $actionRender;
private SocialActionRepository $actionRepository;
public function __construct(
SocialActionRender $actionRender,
SocialActionRepository $actionRepository
) {
$this->actionRender = $actionRender;
$this->actionRepository = $actionRepository;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('actsocialaction', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.socialActions', 'actsocialaction');
}
$qb->addSelect('actsocialaction.id AS socialaction_aggregator');
$qb->addGroupBy('socialaction_aggregator');
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getLabels($key, array $values, $data)
{
return function ($value) {
if ('_header' === $value) {
return 'Social action';
}
if (null === $value) {
return '';
}
$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';
}
}

View File

@@ -0,0 +1,87 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
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;
class BySocialIssueAggregator implements AggregatorInterface
{
private SocialIssueRender $issueRender;
private SocialIssueRepository $issueRepository;
public function __construct(
SocialIssueRepository $issueRepository,
SocialIssueRender $issueRender
) {
$this->issueRepository = $issueRepository;
$this->issueRender = $issueRender;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('actsocialissue', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.socialIssues', 'actsocialissue');
}
$qb->addSelect('actsocialissue.id AS socialissue_aggregator');
$qb->addGroupBy('socialissue_aggregator');
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ('_header' === $value) {
return 'Social issues';
}
if (null === $value) {
return '';
}
$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';
}
}

View File

@@ -0,0 +1,87 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository;
use Chill\ThirdPartyBundle\Templating\Entity\ThirdPartyRender;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class ByThirdpartyAggregator implements AggregatorInterface
{
private ThirdPartyRender $thirdPartyRender;
private ThirdPartyRepository $thirdPartyRepository;
public function __construct(
ThirdPartyRepository $thirdPartyRepository,
ThirdPartyRender $thirdPartyRender
) {
$this->thirdPartyRepository = $thirdPartyRepository;
$this->thirdPartyRender = $thirdPartyRender;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('acttparty', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.thirdParties', 'acttparty');
}
$qb->addSelect('acttparty.id AS thirdparty_aggregator');
$qb->addGroupBy('thirdparty_aggregator');
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ('_header' === $value) {
return 'Accepted thirdparty';
}
if (null === $value) {
return '';
}
$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';
}
}

View File

@@ -0,0 +1,87 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\UserRepository;
use Chill\MainBundle\Templating\Entity\UserRender;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class ByUserAggregator implements AggregatorInterface
{
private UserRender $userRender;
private UserRepository $userRepository;
public function __construct(
UserRepository $userRepository,
UserRender $userRender
) {
$this->userRepository = $userRepository;
$this->userRender = $userRender;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('actusers', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.users', 'actusers');
}
$qb->addSelect('actusers.id AS users_aggregator');
$qb->addGroupBy('users_aggregator');
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ('_header' === $value) {
return 'Accepted users';
}
if (null === $value) {
return '';
}
$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';
}
}

View File

@@ -0,0 +1,123 @@
<?php
/**
* Chill is a software for social workers
*
* 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\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;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class DateAggregator implements AggregatorInterface
{
private const CHOICES = [
'by month' => 'month',
'by week' => 'week',
'by year' => 'year',
];
private const DEFAULT_CHOICE = 'year';
private TranslatorInterface $translator;
public function __construct(
TranslatorInterface $translator
) {
$this->translator = $translator;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$order = null;
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:
throw new RuntimeException(sprintf("The frequency data '%s' is invalid.", $data['frequency']));
}
$qb->addSelect(sprintf("TO_CHAR(activity.date, '%s') AS date_aggregator", $fmt));
$qb->addGroupBy('date_aggregator');
$qb->addOrderBy('date_aggregator', $order);
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
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'];
}
if (null === $value) {
return '';
}
switch ($data['frequency']) {
case 'month':
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';
}
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\LocationTypeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class LocationTypeAggregator implements AggregatorInterface
{
private LocationTypeRepository $locationTypeRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
LocationTypeRepository $locationTypeRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->locationTypeRepository = $locationTypeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('actloc', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.location', 'actloc');
}
$qb->addSelect('IDENTITY(actloc.locationType) AS locationtype_aggregator');
$qb->addGroupBy('locationtype_aggregator');
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ('_header' === $value) {
return 'Accepted locationtype';
}
if (null === $value) {
return '';
}
$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';
}
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\ScopeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class UserScopeAggregator implements AggregatorInterface
{
private ScopeRepository $scopeRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
ScopeRepository $scopeRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->scopeRepository = $scopeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('actuser', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.user', 'actuser');
}
$qb->addSelect('IDENTITY(actuser.mainScope) AS userscope_aggregator');
$qb->addGroupBy('userscope_aggregator');
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ('_header' === $value) {
return 'Scope';
}
if (null === $value) {
return '';
}
$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';
}
}

View File

@@ -11,49 +11,50 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\ActivityBundle\Export\Declarations;
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 Symfony\Component\Security\Core\Role\Role;
use function in_array;
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;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole()
public function addRole(): ?string
{
return new Role(ActivityStatsVoter::STATS);
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
// add select element
$qb->addSelect(sprintf('IDENTITY(activity.type) AS %s', self::KEY));
if (!in_array('acttype', $qb->getAllAliases(), true)) {
$qb->join('activity.activityType', 'acttype');
}
// add the "group by" part
$qb->addSelect(sprintf('IDENTITY(activity.activityType) AS %s', self::KEY));
$qb->addGroupBy(self::KEY);
}
public function applyOn()
public function applyOn(): string
{
return 'activity';
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
@@ -71,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());
@@ -86,23 +91,4 @@ class ActivityTypeAggregator implements AggregatorInterface
{
return 'Aggregate by activity type';
}
/**
* Check if a join between Activity and another alias.
*
* @param Join[] $joins
* @param string $alias the alias to search for
*
* @return bool
*/
private function checkJoinAlreadyDefined(array $joins, $alias)
{
foreach ($joins as $join) {
if ($join->getAlias() === $alias) {
return true;
}
}
return false;
}
}

View File

@@ -11,29 +11,33 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\UserRepository;
use Chill\MainBundle\Templating\Entity\UserRender;
use Closure;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
class ActivityUserAggregator implements AggregatorInterface
{
public const KEY = 'activity_user_id';
private UserRender $userRender;
private UserRepository $userRepository;
public function __construct(
UserRepository $userRepository
UserRepository $userRepository,
UserRender $userRender
) {
$this->userRepository = $userRepository;
$this->userRender = $userRender;
}
public function addRole()
public function addRole(): ?string
{
return new Role(ActivityStatsVoter::STATS);
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
@@ -47,7 +51,7 @@ class ActivityUserAggregator implements AggregatorInterface
public function applyOn(): string
{
return 'activity';
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
@@ -57,15 +61,18 @@ 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';
return 'Activity user';
}
return $this->userRepository->find($value)->getUsername();
if (null === $value) {
return '';
}
$u = $this->userRepository->find($value);
return $this->userRender->renderString($u, []);
};
}

View File

@@ -9,11 +9,11 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator;
namespace Chill\ActivityBundle\Export\Aggregator\PersonAggregators;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityReasonCategoryRepository;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
@@ -23,7 +23,6 @@ use Doctrine\ORM\QueryBuilder;
use RuntimeException;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use function array_key_exists;
@@ -47,19 +46,19 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole()
public function addRole(): ?string
{
return new Role(ActivityStatsVoter::STATS);
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
// 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.');
@@ -68,29 +67,15 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
$qb->addSelect($elem . ' as ' . $alias);
// make a jointure only if needed
$join = $qb->getDQLPart('join');
if (
(
array_key_exists('activity', $join)
&& !$this->checkJoinAlreadyDefined($join['activity'], 'reasons')
)
|| (!array_key_exists('activity', $join))
) {
$qb->add(
'join',
[
'activity' => new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons'),
],
true
);
if (!in_array( 'actreasons', $qb->getAllAliases(), true)) {
$qb->innerJoin('activity.reasons', 'actreasons');
}
// join category if necessary
if ('activity_categories_id' === $alias) {
// add join only if needed
if (!$this->checkJoinAlreadyDefined($qb->getDQLPart('join')['activity'], 'category')) {
$qb->join('reasons.category', 'category');
if (!in_array('actreasoncat', $qb->getAllAliases(), true)) {
$qb->join('actreasons.category', 'actreasoncat');
}
}
@@ -104,9 +89,9 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
}
}
public function applyOn()
public function applyOn(): string
{
return 'activity';
return Declarations::ACTIVITY_PERSON;
}
public function buildForm(FormBuilderInterface $builder)
@@ -194,23 +179,4 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
->addViolation();
}
}
/**
* Check if a join between Activity and another alias.
*
* @param Join[] $joins
* @param string $alias the alias to search for
*
* @return bool
*/
private function checkJoinAlreadyDefined(array $joins, $alias)
{
foreach ($joins as $join) {
if ($join->getAlias() === $alias) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Export;
/**
* This class declare constants used for the export framework.
*/
abstract class Declarations
{
public const ACTIVITY = 'activity';
public const ACTIVITY_ACP = 'activity_linked_to_acp';
public const ACTIVITY_PERSON = 'activity_linked_to_person';
}

View File

@@ -0,0 +1,109 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
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;
use Symfony\Component\Form\FormBuilderInterface;
class AvgActivityDuration implements ExportInterface, GroupedExportInterface
{
protected EntityRepository $repository;
public function __construct(
EntityManagerInterface $em
) {
$this->repository = $em->getRepository(Activity::class);
}
public function buildForm(FormBuilderInterface $builder)
{
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription(): string
{
return 'Average activities linked to an accompanying period duration 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_avg_activity_duration' !== $key) {
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;
}
public function getQueryKeys($data): array
{
return ['export_avg_activity_duration'];
}
public function getResult($qb, $data)
{
return $qb->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;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$qb = $this->repository->createQueryBuilder('activity');
$qb
->join('activity.accompanyingPeriod', 'acp')
->select('AVG(activity.durationTime) as export_avg_activity_duration')
->andWhere($qb->expr()->isNotNull('activity.durationTime'));
return $qb;
}
public function requiredRole(): string
{
return ActivityStatsVoter::STATS;
}
public function supportsModifiers(): array
{
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_ACP,
PersonDeclarations::ACP_TYPE,
];
}
}

View File

@@ -0,0 +1,111 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
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;
use Symfony\Component\Form\FormBuilderInterface;
class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterface
{
protected EntityRepository $repository;
public function __construct(
EntityManagerInterface $em
) {
$this->repository = $em->getRepository(Activity::class);
}
public function buildForm(FormBuilderInterface $builder)
{
// TODO: Implement buildForm() method.
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription(): string
{
return 'Average activities linked to an accompanying period visit duration 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_avg_activity_visit_duration' !== $key) {
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;
}
public function getQueryKeys($data): array
{
return ['export_avg_activity_visit_duration'];
}
public function getResult($qb, $data)
{
return $qb->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;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$qb = $this->repository->createQueryBuilder('activity');
$qb
->join('activity.accompanyingPeriod', 'acp')
->select('AVG(activity.travelTime) as export_avg_activity_visit_duration')
->andWhere($qb->expr()->isNotNull('activity.travelTime'))
;
return $qb;
}
public function requiredRole(): string
{
return ActivityStatsVoter::STATS;
}
public function supportsModifiers(): array
{
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_ACP,
PersonDeclarations::ACP_TYPE,
];
}
}

View File

@@ -0,0 +1,111 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
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;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class CountActivity implements ExportInterface, GroupedExportInterface
{
protected EntityRepository $repository;
public function __construct(
EntityManagerInterface $em
) {
$this->repository = $em->getRepository(Activity::class);
}
public function buildForm(FormBuilderInterface $builder)
{
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription(): string
{
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) {
throw new LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Number of activities linked to an accompanying period' : $value;
}
public function getQueryKeys($data): array
{
return ['export_count_activity'];
}
public function getResult($qb, $data)
{
return $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle(): string
{
return 'Count activities linked to an accompanying period';
}
public function getType(): string
{
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$qb = $this->repository->createQueryBuilder('activity');
if (!in_array('acp', $qb->getAllAliases(), true)) {
$qb->join('activity.accompanyingPeriod', 'acp');
}
$qb->select('COUNT(DISTINCT activity.id) as export_count_activity');
return $qb;
}
public function requiredRole(): string
{
return ActivityStatsVoter::STATS;
}
public function supportsModifiers(): array
{
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_ACP,
PersonDeclarations::ACP_TYPE,
];
}
}

View File

@@ -0,0 +1,112 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
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;
use Symfony\Component\Form\FormBuilderInterface;
class SumActivityDuration implements ExportInterface, GroupedExportInterface
{
protected EntityRepository $repository;
public function __construct(
EntityManagerInterface $em
) {
$this->repository = $em->getRepository(Activity::class);
}
public function buildForm(FormBuilderInterface $builder)
{
// TODO: Implement buildForm() method.
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription(): string
{
return 'Sum activities linked to an accompanying period duration 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_sum_activity_duration' !== $key) {
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;
}
public function getQueryKeys($data): array
{
return ['export_sum_activity_duration'];
}
public function getResult($qb, $data)
{
return $qb->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;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$qb = $this->repository->createQueryBuilder('activity');
if (!in_array('acp', $qb->getAllAliases(), true)) {
$qb->join('activity.accompanyingPeriod', 'acp');
}
$qb->select('SUM(activity.durationTime) as export_sum_activity_duration')
->andWhere($qb->expr()->isNotNull('activity.durationTime'));
return $qb;
}
public function requiredRole(): string
{
return ActivityStatsVoter::STATS;
}
public function supportsModifiers(): array
{
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_ACP,
PersonDeclarations::ACP_TYPE,
];
}
}

View File

@@ -0,0 +1,112 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
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;
use Symfony\Component\Form\FormBuilderInterface;
class SumActivityVisitDuration implements ExportInterface, GroupedExportInterface
{
protected EntityRepository $repository;
public function __construct(
EntityManagerInterface $em
) {
$this->repository = $em->getRepository(Activity::class);
}
public function buildForm(FormBuilderInterface $builder)
{
// TODO: Implement buildForm() method.
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription(): string
{
return 'Sum activities linked to an accompanying period visit duration 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_sum_activity_visit_duration' !== $key) {
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;
}
public function getQueryKeys($data): array
{
return ['export_sum_activity_visit_duration'];
}
public function getResult($qb, $data)
{
return $qb->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;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$qb = $this->repository->createQueryBuilder('activity');
if (!in_array('acp', $qb->getAllAliases(), true)) {
$qb->join('activity.accompanyingPeriod', 'acp');
}
$qb->select('SUM(activity.travelTime) as export_sum_activity_visit_duration')
->andWhere($qb->expr()->isNotNull('activity.travelTime'));
return $qb;
}
public function requiredRole(): string
{
return ActivityStatsVoter::STATS;
}
public function supportsModifiers(): array
{
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_ACP,
PersonDeclarations::ACP_TYPE,
];
}
}

View File

@@ -9,18 +9,20 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Export;
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\PersonBundle\Export\Declarations as PersonDeclarations;
use Doctrine\ORM\Query;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
class CountActivity implements ExportInterface
class CountActivity implements ExportInterface, GroupedExportInterface
{
protected ActivityRepository $activityRepository;
@@ -41,7 +43,12 @@ class CountActivity implements ExportInterface
public function getDescription()
{
return 'Count activities by various parameters.';
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)
@@ -50,7 +57,7 @@ class CountActivity implements ExportInterface
throw new LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Number of activities' : $value;
return static fn ($value) => '_header' === $value ? 'Number of activities linked to a person' : $value;
}
public function getQueryKeys($data)
@@ -65,23 +72,25 @@ class CountActivity implements ExportInterface
public function getTitle()
{
return 'Count activities';
return 'Count activities linked to a person';
}
public function getType()
public function getType(): string
{
return 'activity';
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this
->activityRepository
->createQueryBuilder('activity')
->select('COUNT(activity.id) as export_count_activity')
->join('activity.person', 'person');
$qb = $this->activityRepository->createQueryBuilder('activity');
if (!in_array('person', $qb->getAllAliases(), true)) {
$qb->join('activity.person', 'person');
}
$qb->select('COUNT(activity.id) as export_count_activity');
$qb
->where($qb->expr()->in('person.center', ':centers'))
@@ -90,13 +99,17 @@ class CountActivity implements ExportInterface
return $qb;
}
public function requiredRole()
public function requiredRole(): string
{
return new Role(ActivityStatsVoter::STATS);
return ActivityStatsVoter::STATS;
}
public function supportsModifiers()
{
return ['person', 'activity'];
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_PERSON,
PersonDeclarations::PERSON_TYPE,
];
}
}

View File

@@ -9,21 +9,23 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Export;
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;
use Doctrine\ORM\Query;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
@@ -32,7 +34,7 @@ use function array_key_exists;
use function count;
use function in_array;
class ListActivity implements ListInterface
class ListActivity implements ListInterface, GroupedExportInterface
{
protected EntityManagerInterface $entityManager;
@@ -94,7 +96,12 @@ class ListActivity implements ListInterface
public function getDescription()
{
return 'List activities';
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)
@@ -180,12 +187,12 @@ class ListActivity implements ListInterface
public function getTitle()
{
return 'List activities';
return 'List activity linked to a person';
}
public function getType()
public function getType(): string
{
return 'activity';
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
@@ -204,8 +211,8 @@ class ListActivity implements ListInterface
$qb
->from('ChillActivityBundle:Activity', 'activity')
->join('activity.person', 'person')
->join('person.center', 'center')
->andWhere('center IN (:authorized_centers)')
->join('actperson.center', 'actcenter')
->andWhere('actcenter IN (:authorized_centers)')
->setParameter('authorized_centers', $centers);
foreach ($this->fields as $f) {
@@ -232,8 +239,8 @@ class ListActivity implements ListInterface
break;
case 'user_username':
$qb->join('activity.user', 'user');
$qb->addSelect('user.username AS user_username');
$qb->join('activity.user', 'actuser');
$qb->addSelect('actuser.username AS user_username');
break;
@@ -267,13 +274,17 @@ class ListActivity implements ListInterface
return $qb;
}
public function requiredRole()
public function requiredRole(): string
{
return new Role(ActivityStatsVoter::LISTS);
return ActivityStatsVoter::LISTS;
}
public function supportsModifiers()
{
return ['activity', 'person'];
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_PERSON,
//PersonDeclarations::PERSON_TYPE,
];
}
}

View File

@@ -9,23 +9,26 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Export;
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;
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\Query;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
/**
* This export allow to compute stats on activity duration.
*
* The desired stat must be given in constructor.
*/
class StatActivityDuration implements ExportInterface
class StatActivityDuration implements ExportInterface, GroupedExportInterface
{
public const SUM = 'sum';
@@ -59,17 +62,22 @@ class StatActivityDuration implements ExportInterface
public function getDescription()
{
if (self::SUM === $this->action) {
return 'Sum activities duration by various parameters.';
return 'Sum activities linked to a person duration by various parameters.';
}
}
public function getGroup(): string
{
return 'Exports of activities linked to a person';
}
public function getLabels($key, array $values, $data)
{
if ('export_stat_activity' !== $key) {
throw new LogicException(sprintf('The key %s is not used by this export', $key));
}
$header = self::SUM === $this->action ? 'Sum of activities duration' : false;
$header = self::SUM === $this->action ? 'Sum activities linked to a person duration' : false;
return static fn (string $value) => '_header' === $value ? $header : $value;
}
@@ -87,19 +95,19 @@ class StatActivityDuration implements ExportInterface
public function getTitle()
{
if (self::SUM === $this->action) {
return 'Sum activity duration';
return 'Sum activity linked to a person duration';
}
}
public function getType()
public function getType(): string
{
return 'activity';
return Declarations::ACTIVITY;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(
static fn (array $el): string => $el['center'],
static fn (array $el): Center => $el['center'],
$acl
);
@@ -113,18 +121,22 @@ class StatActivityDuration implements ExportInterface
return $qb->select($select)
->join('activity.person', 'person')
->join('person.center', 'center')
->where($qb->expr()->in('center', ':centers'))
->join('actperson.center', 'actcenter')
->where($qb->expr()->in('actcenter', ':centers'))
->setParameter(':centers', $centers);
}
public function requiredRole()
public function requiredRole(): string
{
return new Role(ActivityStatsVoter::STATS);
return ActivityStatsVoter::STATS;
}
public function supportsModifiers()
{
return ['person', 'activity'];
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_PERSON,
//PersonDeclarations::PERSON_TYPE,
];
}
}

View File

@@ -0,0 +1,95 @@
<?php
/**
* Chill is a software for social workers
*
* 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\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\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;
class ActivityTypeFilter implements FilterInterface
{
private ActivityTypeRepositoryInterface $activityTypeRepository;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
ActivityTypeRepositoryInterface $activityTypeRepository,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->activityTypeRepository = $activityTypeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('activity', $qb->getAllAliases(), true)) {
$qb->join(Activity::class, 'activity', Expr\Join::WITH, 'activity.accompanyingPeriod = acp');
}
$clause = $qb->expr()->in('activity.activityType', ':selected_activity_types');
$qb->andWhere($clause);
$qb->setParameter('selected_activity_types', $data['types']);
}
public function applyOn()
{
return Declarations::ACP_TYPE;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add('accepted_activitytypes', EntityType::class, [
'class' => ActivityType::class,
'choices' => $this->activityTypeRepository->findAllActive(),
'choice_label' => function (ActivityType $aty) {
return
($aty->hasCategory() ? $this->translatableStringHelper->localize($aty->getCategory()->getName()) . ' > ' : '')
.
$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(', ', $types),
]];
}
public function getTitle(): string
{
return 'Filter accompanying course by activity type';
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\FilterInterface;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Chill\PersonBundle\Templating\Entity\SocialActionRender;
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 BySocialActionFilter implements FilterInterface
{
private SocialActionRender $actionRender;
public function __construct(SocialActionRender $actionRender)
{
$this->actionRender = $actionRender;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
if (!in_array('actsocialaction', $qb->getAllAliases(), true)) {
$qb->join('activity.socialActions', 'actsocialaction');
}
$clause = $qb->expr()->in('actsocialaction.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::ACTIVITY_ACP;
}
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';
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\FilterInterface;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
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 BySocialIssueFilter implements FilterInterface
{
private SocialIssueRender $issueRender;
public function __construct(SocialIssueRender $issueRender)
{
$this->issueRender = $issueRender;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
if (!in_array('actsocialissue', $qb->getAllAliases(), true)) {
$qb->join('activity.socialIssues', 'actsocialissue');
}
$clause = $qb->expr()->in('actsocialissue.id', ':socialissues');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('socialissues', $data['accepted_socialissues']);
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
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';
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\Entity\UserRender;
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 ByUserFilter implements FilterInterface
{
private UserRender $userRender;
public function __construct(UserRender $userRender)
{
$this->userRender = $userRender;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
if (!in_array('actusers', $qb->getAllAliases(), true)) {
$qb->join('activity.users', 'actusers');
}
$clause = $qb->expr()->in('actusers.id', ':users');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('users', $data['accepted_users']);
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
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';
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\FilterInterface;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class EmergencyFilter implements FilterInterface
{
private const CHOICES = [
'activity is emergency' => true,
'activity is not emergency' => false,
];
private const DEFAULT_CHOICE = false;
private TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->eq('activity.emergency', ':emergency');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('emergency', $data['accepted_emergency']);
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
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';
}
}

View File

@@ -0,0 +1,93 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Entity\LocationType;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
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 LocationTypeFilter implements FilterInterface
{
private TranslatableStringHelper $translatableStringHelper;
public function __construct(TranslatableStringHelper $translatableStringHelper)
{
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('actloc', $qb->getAllAliases(), true)) {
$qb->join('activity.location', 'actloc');
}
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('actloc.locationType', ':locationtype');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('locationtype', $data['accepted_locationtype']);
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
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';
}
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\FilterInterface;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class SentReceivedFilter implements FilterInterface
{
private const CHOICES = [
'is sent' => Activity::SENTRECEIVED_SENT,
'is received' => Activity::SENTRECEIVED_RECEIVED,
];
private const DEFAULT_CHOICE = Activity::SENTRECEIVED_SENT;
private TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->eq('activity.sentReceived', ':sentreceived');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('sentreceived', $data['accepted_sentreceived']);
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
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';
}
}

View File

@@ -0,0 +1,88 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\Entity\UserRender;
use Doctrine\ORM\Query\Expr\Andx;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
class UserFilter implements FilterInterface
{
private UserRender $userRender;
public function __construct(UserRender $userRender)
{
$this->userRender = $userRender;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('activity.user', ':users');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('users', $data['accepted_users']);
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
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';
}
}

View File

@@ -0,0 +1,96 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
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 UserScopeFilter implements FilterInterface
{
private TranslatableStringHelper $translatableStringHelper;
public function __construct(TranslatableStringHelper $translatableStringHelper)
{
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('actuser', $qb->getAllAliases(), true)) {
$qb->join('activity.user', 'actuser');
}
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('actuser.mainScope', ':userscope');
if ($where instanceof Andx) {
$where->add($clause);
} else {
$where = $qb->expr()->andX($clause);
}
$qb->add('where', $where);
$qb->setParameter('userscope', $data['accepted_userscope']);
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
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';
}
}

View File

@@ -11,12 +11,13 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\Export\FilterType;
use DateTime;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent;
@@ -32,7 +33,7 @@ class ActivityDateFilter implements FilterInterface
$this->translator = $translator;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
@@ -57,36 +58,22 @@ class ActivityDateFilter implements FilterInterface
$qb->setParameter('date_to', $data['date_to']);
}
public function applyOn()
public function applyOn(): string
{
return 'activity';
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add(
'date_from',
DateType::class,
[
$builder
->add('date_from', ChillDateType::class, [
'label' => 'Activities after this date',
'data' => new DateTime(),
'attr' => ['class' => 'datepicker'],
'widget' => 'single_text',
'format' => 'dd-MM-yyyy',
]
);
$builder->add(
'date_to',
DateType::class,
[
])
->add('date_to', ChillDateType::class, [
'label' => 'Activities before this date',
'data' => new DateTime(),
'attr' => ['class' => 'datepicker'],
'widget' => 'single_text',
'format' => 'dd-MM-yyyy',
]
);
]);
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
/** @var \Symfony\Component\Form\FormInterface $filterForm */

View File

@@ -12,8 +12,8 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
use Chill\ActivityBundle\Entity\ActivityType;
use Chill\ActivityBundle\Repository\ActivityTypeRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
@@ -22,78 +22,79 @@ 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\Security\Core\Role\Role;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
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;
}
public function addRole()
public function addRole(): ?string
{
return new Role(ActivityStatsVoter::STATS);
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$where = $qb->getDQLPart('where');
$clause = $qb->expr()->in('activity.type', ':selected_activity_types');
$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']);
}
public function applyOn()
public function applyOn(): string
{
return 'activity';
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
{
$builder->add(
'types',
EntityType::class,
[
'class' => ActivityType::class,
'choice_label' => fn (ActivityType $type) => $this->translatableStringHelper->localize($type->getName()),
'multiple' => true,
'expanded' => false,
$builder->add('types', EntityType::class, [
'choices' => $this->activityTypeRepository->findAllActive(),
'class' => ActivityType::class,
'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'
]
);
]);
}
public function describeAction($data, $format = 'string')
{
// collect all the reasons'name used in this filter in one array
$reasonsNames = array_map(
fn (ActivityType $t): string => '"' . $this->translatableStringHelper->localize($t->getName()) . '"',
fn (ActivityType $t): string => $this->translatableStringHelper->localize($t->getName()),
$this->activityTypeRepository->findBy(['id' => $data['types']->toArray()])
);
return [
'Filtered by activity type: only %list%',
[
'%list%' => implode(', ', $reasonsNames),
],
];
return ['Filtered by activity type: only %list%', [
'%list%' => implode(', ou ', $reasonsNames),
]];
}
public function getTitle()
@@ -109,23 +110,4 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
->addViolation();
}
}
/**
* Check if a join between Activity and Reason is already defined.
*
* @param Join[] $joins
* @param mixed $alias
*
* @return bool
*/
private function checkJoinAlreadyDefined(array $joins, $alias)
{
foreach ($joins as $join) {
if ($join->getAlias() === $alias) {
return true;
}
}
return false;
}
}

View File

@@ -9,11 +9,11 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
namespace Chill\ActivityBundle\Export\Filter\PersonFilters;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
@@ -23,7 +23,6 @@ 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\Security\Core\Role\Role;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use function array_key_exists;
@@ -43,9 +42,9 @@ class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInt
$this->activityReasonRepository = $activityReasonRepository;
}
public function addRole()
public function addRole(): ?string
{
return new Role(ActivityStatsVoter::STATS);
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
@@ -53,20 +52,9 @@ class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInt
$where = $qb->getDQLPart('where');
$join = $qb->getDQLPart('join');
$clause = $qb->expr()->in('reasons', ':selected_activity_reasons');
//dump($join);
// add a join to reasons only if needed
if (
(
array_key_exists('activity', $join)
&& !$this->checkJoinAlreadyDefined($join['activity'], 'reasons')
)
|| (!array_key_exists('activity', $join))
) {
$qb->add(
'join',
['activity' => new Join(Join::INNER_JOIN, 'activity.reasons', 'reasons')],
true
);
if (!in_array('actreasons', $qb->getAllAliases(), true)) {
$qb->join('activity.reasons', 'actreasons');
}
if ($where instanceof Expr\Andx) {
@@ -79,9 +67,9 @@ class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInt
$qb->setParameter('selected_activity_reasons', $data['reasons']);
}
public function applyOn()
public function applyOn(): string
{
return 'activity';
return Declarations::ACTIVITY_PERSON;
}
public function buildForm(FormBuilderInterface $builder)
@@ -124,21 +112,4 @@ class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInt
->addViolation();
}
}
/**
* Check if a join between Activity and Reason is already defined.
*
* @param Join[] $joins
* @param mixed $alias
*/
private function checkJoinAlreadyDefined(array $joins, $alias): bool
{
foreach ($joins as $join) {
if ($join->getAlias() === $alias) {
return true;
}
}
return false;
}
}

View File

@@ -9,8 +9,9 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Filter;
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;
@@ -52,17 +53,17 @@ class PersonHavingActivityBetweenDateFilter implements ExportElementValidatedInt
$this->translator = $translator;
}
public function addRole()
public function addRole(): ?string
{
return null;
}
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
@@ -102,7 +103,7 @@ class PersonHavingActivityBetweenDateFilter implements ExportElementValidatedInt
$qb->setParameter('person_having_activity_reasons', $data['reasons']);
}
public function applyOn()
public function applyOn(): string
{
return Declarations::PERSON_IMPLIED_IN;
}
@@ -197,7 +198,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)

View File

@@ -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());
},

View File

@@ -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;
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\ActivityType;
use Doctrine\Persistence\ObjectRepository;
interface ActivityTypeRepositoryInterface extends ObjectRepository
{
/**
* @return array|ActivityType[]
*/
public function findAllActive(): array;
}

View File

@@ -17,7 +17,7 @@ const getLocations = () => fetchResults('/api/1.0/main/location.json');
const getLocationTypes = () => fetchResults('/api/1.0/main/location-type.json');
const getUserCurrentLocation =
const getUserCurrentLocation =
() => fetch('/api/1.0/main/user-current-location.json')
.then(response => {
if (response.ok) { return response.json(); }
@@ -35,6 +35,13 @@ const getLocationTypeByDefaultFor = (entity) => {
);
};
/**
* Post a location
*
* **NOTE**: also in use for Calendar
* @param body
* @returns {Promise<T>}
*/
const postLocation = (body) => {
const url = `/api/1.0/main/location.json`;
return fetch(url, {

View File

@@ -55,7 +55,7 @@ const makeAccompanyingPeriodLocation = (locationType, store) => {
export default function prepareLocations(store) {
// find the locations
// find the locations
let allLocations = getLocations().then(
(results) => {
store.commit('addAvailableLocationGroup', {
@@ -111,7 +111,7 @@ export default function prepareLocations(store) {
if (window.default_location_id) {
for (let group of store.state.availableLocations) {
let location = group.locations.find((l) => l.id === window.default_location_id);
if (location !== undefined & store.state.activity.location === null) {
if (location !== undefined && store.state.activity.location === null) {
store.dispatch('updateLocation', location);
break;
}

View File

@@ -1,3 +1,14 @@
{#
WARNING: this file is in use in both ActivityBundle and CalendarBundle.
Take care when editing this file.
Maybe should we think about abstracting this file a bit more ? Moving it to PersonBundle ?
#}
{% if context == 'calendar_accompanyingCourse' %}
{% import "@ChillCalendar/_invite.html.twig" as invite %}
{% endif %}
{% macro href(pathname, key, value) %}
{% set parms = { (key): value } %}
{{ path(pathname, parms) }}
@@ -18,7 +29,7 @@
{% endmacro %}
{% set blocks = [] %}
{% if entity.activityType.personsVisible %}
{% if context == 'calendar_accompanyingCourse' or entity.activityType.personsVisible %}
{% if context == 'person' %}
{% set blocks = blocks|merge([{
'title': 'Others persons'|trans,
@@ -43,7 +54,7 @@
}]) %}
{% endif %}
{% endif %}
{% if entity.activityType.thirdPartiesVisible %}
{% if context == 'calendar_accompanyingCourse' or entity.activityType.thirdPartiesVisible %}
{% set blocks = blocks|merge([{
'title': 'Third parties'|trans,
'items': entity.thirdParties,
@@ -52,7 +63,7 @@
'key' : 'id',
}]) %}
{% endif %}
{% if entity.activityType.usersVisible %}
{% if context == 'calendar_accompanyingCourse' or entity.activityType.usersVisible %}
{% set blocks = blocks|merge([{
'title': 'Users concerned'|trans,
'items': entity.users,
@@ -132,6 +143,12 @@
{% if bloc.type == 'user' %}
<span class="badge-user">
{{ item|chill_entity_render_box({'render': 'raw', 'addAltNames': false }) }}
{%- if context == 'calendar_accompanyingCourse' %}
{% set invite = entity.inviteForUser(item) %}
{% if invite is not null %}
{{ invite.invite_span(invite) }}
{% endif %}
{%- endif -%}
</span>
{% else %}
{{ _self.insert_onthefly(bloc.type, item) }}

View File

@@ -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()

View File

@@ -0,0 +1,59 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Tests\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\BySocialActionAggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
use Doctrine\ORM\EntityManagerInterface;
final class BySocialActionAggregatorTest extends AbstractAggregatorTest
{
private BySocialActionAggregator $aggregator;
protected function setUp(): void
{
self::bootKernel();
$this->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', 'acp')
->join('activity.socialActions', 'actsocialaction')
,
];
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Tests\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\BySocialIssueAggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
use Doctrine\ORM\EntityManagerInterface;
final class BySocialIssueAggregatorTest extends AbstractAggregatorTest
{
private BySocialIssueAggregator $aggregator;
protected function setUp(): void
{
self::bootKernel();
$this->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', 'acp')
->join('activity.socialIssues', 'actsocialissue')
,
];
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Tests\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByThirdpartyAggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
use Doctrine\ORM\EntityManagerInterface;
final class ByThirdpartyAggregatorTest extends AbstractAggregatorTest
{
private ByThirdpartyAggregator $aggregator;
protected function setUp(): void
{
self::bootKernel();
$this->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', 'acp')
->join('activity.thirdParties', 'acttparty')
,
];
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Tests\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByUserAggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
use Doctrine\ORM\EntityManagerInterface;
final class ByUserAggregatorTest extends AbstractAggregatorTest
{
private ByUserAggregator $aggregator;
protected function setUp(): void
{
self::bootKernel();
$this->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', 'acp')
->join('activity.users', 'actusers')
,
];
}
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Tests\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\DateAggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
use Doctrine\ORM\EntityManagerInterface;
final class DateAggregatorTest extends AbstractAggregatorTest
{
private DateAggregator $aggregator;
protected function setUp(): void
{
self::bootKernel();
$this->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', 'acp')
,
];
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Tests\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\LocationTypeAggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
use Doctrine\ORM\EntityManagerInterface;
final class LocationTypeAggregatorTest extends AbstractAggregatorTest
{
private LocationTypeAggregator $aggregator;
protected function setUp(): void
{
self::bootKernel();
$this->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', 'acp')
->join('activity.location', 'actloc')
,
];
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Tests\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Aggregator\ACPAggregators\UserScopeAggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
use Doctrine\ORM\EntityManagerInterface;
final class UserScopeAggregatorTest extends AbstractAggregatorTest
{
private UserScopeAggregator $aggregator;
protected function setUp(): void
{
self::bootKernel();
$this->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', 'acp')
->join('activity.user', 'actuser')
,
];
}
}

View File

@@ -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
{

View File

@@ -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
{

View File

@@ -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
{

View File

@@ -0,0 +1,65 @@
<?php
/**
* Chill is a software for social workers
*
* 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\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;
final class ActivityReasonAggregatorTest extends AbstractAggregatorTest
{
private ActivityReasonAggregator $aggregator;
protected function setUp(): void
{
self::bootKernel();
$this->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')
,
];
}
}

View File

@@ -0,0 +1,85 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Tests\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Entity\ActivityType;
use Chill\ActivityBundle\Export\Filter\ACPFilters\ActivityTypeFilter;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query\Expr;
/**
* @internal
* @coversNothing
*/
final class ActivityTypeFilterTest extends AbstractFilterTest
{
private ActivityTypeFilter $filter;
protected function setUp(): void
{
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');
}
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'),
];
}
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Tests\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Filter\ACPFilters\BySocialActionFilter;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Doctrine\ORM\EntityManagerInterface;
/**
* @internal
* @coversNothing
*/
final class BySocialActionFilterTest extends AbstractFilterTest
{
private BySocialActionFilter $filter;
protected function setUp(): void
{
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');
}
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'),
];
}
}

View File

@@ -0,0 +1,83 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Tests\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Filter\ACPFilters\BySocialIssueFilter;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Doctrine\ORM\EntityManagerInterface;
/**
* @internal
* @coversNothing
*/
final class BySocialIssueFilterTest extends AbstractFilterTest
{
private BySocialIssueFilter $filter;
protected function setUp(): void
{
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');
}
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')
,
];
}
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Tests\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Filter\ACPFilters\ByUserFilter;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Doctrine\ORM\EntityManagerInterface;
/**
* @internal
* @coversNothing
*/
final class ByUserFilterTest extends AbstractFilterTest
{
private ByUserFilter $filter;
protected function setUp(): void
{
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');
}
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'),
];
}
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Tests\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Filter\ACPFilters\EmergencyFilter;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Doctrine\ORM\EntityManagerInterface;
/**
* @internal
* @coversNothing
*/
final class EmergencyFilterTest extends AbstractFilterTest
{
private EmergencyFilter $filter;
protected function setUp(): void
{
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');
}
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'),
];
}
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Tests\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Filter\ACPFilters\LocationTypeFilter;
use Chill\MainBundle\Entity\LocationType;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Doctrine\ORM\EntityManagerInterface;
/**
* @internal
* @coversNothing
*/
final class LocationTypeFilterTest extends AbstractFilterTest
{
private LocationTypeFilter $filter;
protected function setUp(): void
{
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');
}
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'),
];
}
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Tests\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Filter\ACPFilters\SentReceivedFilter;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Doctrine\ORM\EntityManagerInterface;
/**
* @internal
* @coversNothing
*/
final class SentReceivedFilterTest extends AbstractFilterTest
{
private SentReceivedFilter $filter;
protected function setUp(): void
{
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');
}
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'),
];
}
}

View File

@@ -0,0 +1,81 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Tests\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Filter\ACPFilters\UserFilter;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Doctrine\ORM\EntityManagerInterface;
/**
* @internal
* @coversNothing
*/
final class UserFilterTest extends AbstractFilterTest
{
private UserFilter $filter;
protected function setUp(): void
{
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');
}
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'),
];
}
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Tests\Export\Filter\ACPFilters;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Filter\ACPFilters\UserScopeFilter;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Doctrine\ORM\EntityManagerInterface;
/**
* @internal
* @coversNothing
*/
final class UserScopeFilterTest extends AbstractFilterTest
{
private UserScopeFilter $filter;
protected function setUp(): void
{
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');
}
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'),
];
}
}

View File

@@ -0,0 +1,69 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Tests\Export\Filter;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Filter\ActivityDateFilter;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Doctrine\ORM\EntityManagerInterface;
/**
* @internal
* @coversNothing
*/
final class ActivityDateFilterTest extends AbstractFilterTest
{
private ActivityDateFilter $filter;
protected function setUp(): void
{
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');
}
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'),
];
}
}

View File

@@ -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
{

View File

@@ -0,0 +1,81 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Tests\Export\Filter;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Entity\ActivityType;
use Chill\ActivityBundle\Export\Filter\ActivityTypeFilter;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Doctrine\ORM\EntityManagerInterface;
/**
* @internal
* @coversNothing
*/
final class ActivityTypeFilterTest extends AbstractFilterTest
{
private ActivityTypeFilter $filter;
protected function setUp(): void
{
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');
}
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'),
];
}
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Tests\Export\Filter\PersonFilters;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Export\Filter\PersonFilters\ActivityReasonFilter;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Doctrine\ORM\EntityManagerInterface;
/**
* @internal
* @coversNothing
*/
final class ActivityReasonFilterTest extends AbstractFilterTest
{
private ActivityReasonFilter $filter;
protected function setUp(): void
{
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');
}
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'),
];
}
}

View File

@@ -0,0 +1,83 @@
<?php
/**
* Chill is a software for social workers
*
* 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\ActivityBundle\Tests\Export\Filter\PersonFilters;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Export\Filter\PersonFilters\PersonHavingActivityBetweenDateFilter;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Doctrine\ORM\EntityManagerInterface;
/**
* @internal
* @coversNothing
*/
final class PersonHavingActivityBetweenDateFilterTest extends AbstractFilterTest
{
private PersonHavingActivityBetweenDateFilter $filter;
protected function setUp(): void
{
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');
}
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'),
];
}
}

View File

@@ -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
{

View File

@@ -3,26 +3,48 @@ services:
autowire: true
autoconfigure: true
chill.activity.export.count_activity:
class: Chill\ActivityBundle\Export\Export\CountActivity
## Indicators
chill.activity.export.count_activity_linked_to_person:
class: Chill\ActivityBundle\Export\Export\LinkedToPerson\CountActivity
tags:
- { name: chill.export, alias: 'count_activity' }
- { name: chill.export, alias: 'count_activity_linked_to_person' }
chill.activity.export.sum_activity_duration:
class: Chill\ActivityBundle\Export\Export\StatActivityDuration
chill.activity.export.sum_activity_duration_linked_to_person:
class: Chill\ActivityBundle\Export\Export\LinkedToPerson\StatActivityDuration
tags:
- { name: chill.export, alias: 'sum_activity_duration' }
- { name: chill.export, alias: 'sum_activity_duration_linked_to_person' }
chill.activity.export.list_activity:
class: Chill\ActivityBundle\Export\Export\ListActivity
chill.activity.export.list_activity_linked_to_person:
class: Chill\ActivityBundle\Export\Export\LinkedToPerson\ListActivity
tags:
- { name: chill.export, alias: 'list_activity' }
- { name: chill.export, alias: 'list_activity_linked_to_person' }
chill.activity.export.reason_filter:
class: Chill\ActivityBundle\Export\Filter\ActivityReasonFilter
chill.activity.export.count_activity_linked_to_acp:
class: Chill\ActivityBundle\Export\Export\LinkedToACP\CountActivity
tags:
- { name: chill.export_filter, alias: 'activity_reason_filter' }
- { name: chill.export, alias: 'count_activity_linked_to_acp' }
chill.activity.export.sum_activity_duration_linked_to_acp:
class: Chill\ActivityBundle\Export\Export\LinkedToACP\SumActivityDuration
tags:
- { name: chill.export, alias: 'sum_activity_duration_linked_to_acp' }
chill.activity.export.sum_activity_visit_duration_linked_to_acp:
class: Chill\ActivityBundle\Export\Export\LinkedToACP\SumActivityVisitDuration
tags:
- { name: chill.export, alias: 'sum_activity_visit_duration_linked_to_acp' }
chill.activity.export.avg_activity_duration_linked_to_acp:
class: Chill\ActivityBundle\Export\Export\LinkedToACP\AvgActivityDuration
tags:
- { name: chill.export, alias: 'avg_activity_duration_linked_to_acp' }
chill.activity.export.avg_activity_visit_duration_linked_to_acp:
class: Chill\ActivityBundle\Export\Export\LinkedToACP\AvgActivityVisitDuration
tags:
- { name: chill.export, alias: 'avg_activity_visit_duration_linked_to_acp' }
## Filters
chill.activity.export.type_filter:
class: Chill\ActivityBundle\Export\Filter\ActivityTypeFilter
tags:
@@ -33,15 +55,66 @@ services:
tags:
- { name: chill.export_filter, alias: 'activity_date_filter' }
chill.activity.export.reason_filter:
class: Chill\ActivityBundle\Export\Filter\PersonFilters\ActivityReasonFilter
tags:
- { name: chill.export_filter, alias: 'activity_reason_filter' }
chill.activity.export.person_having_an_activity_between_date_filter:
class: Chill\ActivityBundle\Export\Filter\PersonHavingActivityBetweenDateFilter
class: Chill\ActivityBundle\Export\Filter\PersonFilters\PersonHavingActivityBetweenDateFilter
tags:
- #0 register as a filter
name: chill.export_filter
alias: 'activity_person_having_ac_bw_date_filter'
chill.activity.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:
- { name: chill.export_filter, alias: 'activity_locationtype_filter' }
chill.activity.export.byuser_filter: # TMS (M2M)
class: Chill\ActivityBundle\Export\Filter\ACPFilters\ByUserFilter
tags:
- { name: chill.export_filter, alias: 'activity_byuser_filter' }
chill.activity.export.emergency_filter:
class: Chill\ActivityBundle\Export\Filter\ACPFilters\EmergencyFilter
tags:
- { name: chill.export_filter, alias: 'activity_emergency_filter' }
chill.activity.export.sentreceived_filter:
class: Chill\ActivityBundle\Export\Filter\ACPFilters\SentReceivedFilter
tags:
- { name: chill.export_filter, alias: 'activity_sentreceived_filter' }
chill.activity.export.bysocialaction_filter:
class: Chill\ActivityBundle\Export\Filter\ACPFilters\BySocialActionFilter
tags:
- { name: chill.export_filter, alias: 'activity_bysocialaction_filter' }
chill.activity.export.bysocialissue_filter:
class: Chill\ActivityBundle\Export\Filter\ACPFilters\BySocialIssueFilter
tags:
- { name: chill.export_filter, alias: 'activity_bysocialissue_filter' }
chill.activity.export.user_filter: # Creator (M2O)
class: Chill\ActivityBundle\Export\Filter\ACPFilters\UserFilter
tags:
- { name: chill.export_filter, alias: 'activity_user_filter' }
chill.activity.export.userscope_filter:
class: Chill\ActivityBundle\Export\Filter\ACPFilters\UserScopeFilter
tags:
- { name: chill.export_filter, alias: 'activity_userscope_filter' }
## Aggregators
chill.activity.export.reason_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ActivityReasonAggregator
class: Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityReasonAggregator
tags:
- { name: chill.export_aggregator, alias: activity_reason_aggregator }
@@ -54,3 +127,38 @@ services:
class: Chill\ActivityBundle\Export\Aggregator\ActivityUserAggregator
tags:
- { name: chill.export_aggregator, alias: activity_user_aggregator }
chill.activity.export.locationtype_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\LocationTypeAggregator
tags:
- { name: chill.export_aggregator, alias: activity_locationtype_aggregator }
chill.activity.export.date_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\DateAggregator
tags:
- { name: chill.export_aggregator, alias: activity_date_aggregator }
chill.activity.export.byuser_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByUserAggregator
tags:
- { name: chill.export_aggregator, alias: activity_byuser_aggregator }
chill.activity.export.bythirdparty_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByThirdpartyAggregator
tags:
- { name: chill.export_aggregator, alias: activity_bythirdparty_aggregator }
chill.activity.export.bysocialaction_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\BySocialActionAggregator
tags:
- { name: chill.export_aggregator, alias: activity_bysocialaction_aggregator }
chill.activity.export.bysocialissue_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\BySocialIssueAggregator
tags:
- { name: chill.export_aggregator, alias: activity_bysocialissue_aggregator }
chill.activity.export.userscope_aggregator:
class: Chill\ActivityBundle\Export\Aggregator\ACPAggregators\UserScopeAggregator
tags:
- { name: chill.export_aggregator, alias: activity_userscope_aggregator }

View File

@@ -203,18 +203,39 @@ Are you sure you want to remove the activity about "%name%" ?: Êtes-vous sûr d
The activity has been successfully removed.: L'activité a été supprimée.
# exports
Count activities: Nombre d'activités
Count activities by various parameters.: Compte le nombre d'activités enregistrées en fonction de différents paramètres.
Sum activity duration: Total de la durée des activités
Sum activities duration by various parameters.: Additionne la durée des activités en fonction de différents paramètres.
List activities: Liste les activités
Number of activities: Nombre d'activités
Exports of activities linked to a person: Exports des activités liées à une personne
Number of activities linked to a person: Nombre d'activités liées à une personne
Count activities linked to a person: Nombre d'activités
Count activities linked to a person by various parameters.: Compte le nombre d'activités enregistrées et liées à une personne en fonction de différents paramètres.
Sum activity linked to a person duration: Durée des activités
Sum activities linked to a person duration: Durée des activités liés à un usager
Sum activities linked to a person duration by various parameters.: Additionne la durée des activités en fonction de différents paramètres.
List activity linked to a person: Liste les activités
List activities linked to a person: Liste des activités liés à un usager
List activities linked to a person description: Crée la liste des activités en fonction de différents paramètres.
Exports of activities linked to an accompanying period: Exports des activités liées à un parcours
Number of activities linked to an accompanying period: Nombre d'activités liées à un parcours
Count activities linked to an accompanying period: Nombre d'activités
Count activities linked to an accompanying period by various parameters.: Compte le nombre d'activités enregistrées et liées à un parcours en fonction de différents paramètres.
Sum activity linked to an accompanying period duration: Somme de la durée des activités
Sum activities linked to an accompanying period duration: Somme de la durée des activités liées à un parcours
Sum activities linked to an accompanying period duration by various parameters.: Additionne la durée des activités en fonction de différents paramètres.
Sum activity linked to an accompanying period visit duration: Somme de la durée de déplacement des activités
Sum activities linked to an accompanying period visit duration: Somme de la durée de déplacement des activités liées à un parcours
Sum activities linked to an accompanying period visit duration by various parameters.: Additionne la durée de déplacement des activités en fonction de différents paramètres.
Average activity linked to an accompanying period duration: Moyenne de la durée des activités
Average activities linked to an accompanying period duration: Moyenne de la durée des activités liées à un parcours
Average activities linked to an accompanying period duration by various parameters.: Moyenne de la durée des activités en fonction de différents paramètres.
Average activity linked to an accompanying period visit duration: Moyenne de la durée de déplacement des activités
Average activities linked to an accompanying period visit duration: Moyenne de la durée de déplacement des activités liées à un parcours
Average activities linked to an accompanying period visit duration by various parameters.: Moyenne de la durée de déplacement des activités en fonction de différents paramètres.
#filters
Filter by reason: Filtrer par sujet d'activité
Filter by reason: Filtrer les activités par sujet
'Filtered by reasons: only %list%': 'Filtré par sujet: seulement %list%'
'Filtered by activity type: only %list%': "Filtré par type d'activity: seulement %list%"
Filtered by date activity: Filtrer par date d'activité
'Filtered by activity type: only %list%': "Filtré par type d'activité: uniquement %list%"
Filtered by date activity: Filtrer les activités par date
Activities after this date: Activités après cette date
Activities before this date: Activités avant cette date
"Filtered by date of activity: only between %date_from% and %date_to%": "Filtré par date de l'activité: uniquement entre %date_from% et %date_to%"
@@ -226,18 +247,59 @@ Implied in an activity before this date: Impliqué dans une activité avant cett
Filtered by person having an activity between %date_from% and %date_to% with reasons %reasons_name%: Filtré par personnes associées à une activité entre %date_from% et %date_to% avec les sujets %reasons_name%
Activity reasons for those activities: Sujets de ces activités
Filter by activity type: Filtrer par type d'activité
Filter by activity type: Filtrer les activités par type
Filter activity by locationtype: Filtrer les activités par type de localisation
'Filtered activity by locationtype: only %types%': "Filtré par type de localisation: uniquement %types%"
Accepted locationtype: Types de localisation
Filter activity by linked users: Filtrer les activités par TMS
'Filtered activity by linked users: only %users%': "Filtré par TMS: uniquement %users%"
Accepted users: TMS(s)
Filter activity by emergency: Filtrer les activités par urgence
'Filtered activity by emergency: only %emergency%': "Filtré par urgence: uniquement si %emergency%"
activity is emergency: l'activité est urgente
activity is not emergency: l'activité n'est pas urgente
Filter activity by sentreceived: Filtrer les activités par envoyé/reçu
'Filtered activity by sentreceived: only %sentreceived%': "Filtré par envoyé/reçu: uniquement %sentreceived%"
Accepted sentreceived: ''
is sent: envoyé
is received: reçu
Filter activity by linked socialaction: Filtrer les activités par action liée
'Filtered activity by linked socialaction: only %actions%': "Filtré par action liée: uniquement %actions%"
Filter activity by linked socialissue: Filtrer les activités par problématique liée
'Filtered activity by linked socialissue: only %issues%': "Filtré par problématique liée: uniquement %issues%"
Filter activity by user: Filtrer les activités par créateur
'Filtered activity by user: only %users%': "Filtré par créateur: uniquement %users%"
Creators: Créateurs
Filter activity by userscope: Filtrer les activités par service du créateur
'Filtered activity by userscope: only %scopes%': "Filtré par service du créateur: uniquement %scopes%"
Accepted userscope: Services
#aggregators
Activity type: Type d'activité
Activity user: Utilisateur lié à l'activity
Activity user: Utilisateur lié à l'activité
By reason: Par sujet
By category of reason: Par catégorie de sujet
Reason's level: Niveau du sujet
Group by reasons: Sujet d'activité
Aggregate by activity user: Aggréger par utilisateur lié à l'activité
Aggregate by activity type: Aggréger par type d'activité
Aggregate by activity reason: Aggréger par sujet de l'activité
Aggregate by activity user: Grouper les activités par utilisateur
Aggregate by activity type: Grouper les activités par type
Aggregate by activity reason: Grouper les activités par sujet
Group activity by locationtype: Grouper les activités par type de localisation
Group activity by date: Grouper les activités par date
Frequency: Fréquence
by month: Par mois
by week: Par semaine
for week: Semaine
by year: Par année
in year: En
Group activity by linked users: Grouper les activités par TMS impliqué
Group activity by linked thirdparties: Grouper les activités par tiers impliqué
Accepted thirdparty: Tiers impliqué
Group activity by linked socialaction: Grouper les activités par action liée
Group activity by linked socialissue: Grouper les activités par problématique liée
Group activity by userscope: Grouper les activités par service du créateur
Last activities: Les dernières activités
@@ -249,6 +311,6 @@ This is the minimal activity data: Activité n°
docgen:
Activity basic: Echange
A basic context for activity: Contexte pour les échanges
Accompanying period with a list of activities: Parcours d'accompagnement avec liste des échanges
Accompanying period with a list of activities description: Ce contexte reprend les informations du parcours, et tous les échanges pour un parcours. Les échanges ne sont pas filtrés.
A basic context for activity: Contexte pour les activités
Accompanying period with a list of activities: Parcours d'accompagnement avec liste des activités
Accompanying period with a list of activities description: Ce contexte reprend les informations du parcours, et tous les activités pour un parcours. Les activités ne sont pas filtrés.

View File

@@ -195,7 +195,7 @@ Number of activities: Nombre d'activités
#filters
Filter by reason: Filtrer par sujet d'activité
'Filtered by reasons: only %list%': 'Filtré par sujet: seulement %list%'
'Filtered by activity type: only %list%': "Filtré par type d'activity: seulement %list%"
'Filtered by activity type: only %list%': "Filtré par type d'activity: uniquement %list%"
Filtered by date activity: Filtrer par date d'activité
Activities after this date: Activités après cette date
Activities before this date: Activités avant cette date
@@ -217,9 +217,9 @@ By reason: Par sujet
By category of reason: Par catégorie de sujet
Reason's level: Niveau du sujet
Group by reasons: Sujet d'activité
Aggregate by activity user: Aggréger par utilisateur lié à l'activité
Aggregate by activity type: Aggréger par type d'activité
Aggregate by activity reason: Aggréger par sujet de l'activité
Aggregate by activity user: Grouper par utilisateur lié à l'activité
Aggregate by activity type: Grouper par type d'activité
Aggregate by activity reason: Grouper par sujet de l'activité
Last activities: Les dernières activités
@@ -231,4 +231,4 @@ This is the minimal activity data: Activité n°
docgen:
Activity basic: Echange
A basic context for activity: Contexte pour les échanges
A basic context for activity: Contexte pour les activités

View File

@@ -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 = [];

View File

@@ -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()

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -11,8 +11,16 @@ declare(strict_types=1);
namespace Chill\CalendarBundle;
use Chill\CalendarBundle\RemoteCalendar\DependencyInjection\RemoteCalendarCompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class ChillCalendarBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new RemoteCalendarCompilerPass());
}
}

View File

@@ -0,0 +1,77 @@
<?php
/**
* Chill is a software for social workers
*
* 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\CalendarBundle\Command;
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\MachineTokenStorage;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\FormatterHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use TheNetworg\OAuth2\Client\Provider\Azure;
class AzureGrantAdminConsentAndAcquireToken extends Command
{
private Azure $azure;
private ClientRegistry $clientRegistry;
private MachineTokenStorage $machineTokenStorage;
public function __construct(Azure $azure, ClientRegistry $clientRegistry, MachineTokenStorage $machineTokenStorage)
{
parent::__construct('chill:calendar:msgraph-grant-admin-consent');
$this->azure = $azure;
$this->clientRegistry = $clientRegistry;
$this->machineTokenStorage = $machineTokenStorage;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
/** @var FormatterHelper $formatter */
$formatter = $this->getHelper('formatter');
$this->azure->scope = ['https://graph.microsoft.com/.default'];
$authorizationUrl = explode('?', $this->azure->getAuthorizationUrl(['prompt' => 'admin_consent']));
// replace the first part by the admin consent authorization url
$authorizationUrl[0] = strtr('https://login.microsoftonline.com/{tenant}/adminconsent', ['{tenant}' => $this->azure->tenant]);
$output->writeln('Go to the url');
$output->writeln(implode('?', $authorizationUrl));
$output->writeln('Authenticate as admin, and grant admin consent');
// not necessary ?
$helper = $this->getHelper('question');
$question = new ConfirmationQuestion('Access granted ?');
if (!$helper->ask($input, $output, $question)) {
$messages = ['No problem, we will wait for you', 'Grant access and come back here'];
$output->writeln($formatter->formatBlock($messages, 'warning'));
return 0;
}
$token = $this->machineTokenStorage->getToken();
$messages = ['Token acquired!', 'We could acquire a machine token successfully'];
$output->writeln($formatter->formatBlock($messages, 'success'));
$output->writeln('Token information:');
$output->writeln($token->getToken());
$output->writeln('Expires at: ' . $token->getExpires());
$output->writeln('To inspect the token content, go to https://jwt.ms/#access_token=' . urlencode($token->getToken()));
return 0;
}
}

View File

@@ -0,0 +1,164 @@
<?php
/**
* Chill is a software for social workers
*
* 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\CalendarBundle\Command;
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\EventsOnUserSubscriptionCreator;
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\MapCalendarToUser;
use Chill\CalendarBundle\RemoteCalendar\Connector\MSGraph\MSGraphUserRepository;
use DateInterval;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class MapAndSubscribeUserCalendarCommand extends Command
{
private EntityManagerInterface $em;
private EventsOnUserSubscriptionCreator $eventsOnUserSubscriptionCreator;
private LoggerInterface $logger;
private MapCalendarToUser $mapCalendarToUser;
private MSGraphUserRepository $userRepository;
public function __construct(
EntityManagerInterface $em,
EventsOnUserSubscriptionCreator $eventsOnUserSubscriptionCreator,
LoggerInterface $logger,
MapCalendarToUser $mapCalendarToUser,
MSGraphUserRepository $userRepository
) {
parent::__construct('chill:calendar:msgraph-user-map-subscribe');
$this->em = $em;
$this->eventsOnUserSubscriptionCreator = $eventsOnUserSubscriptionCreator;
$this->logger = $logger;
$this->mapCalendarToUser = $mapCalendarToUser;
$this->userRepository = $userRepository;
}
public function execute(InputInterface $input, OutputInterface $output): int
{
$this->logger->info(__CLASS__ . ' execute command');
$limit = 50;
$offset = 0;
/** @var DateInterval $interval the interval before the end of the expiration */
$interval = new DateInterval('P1D');
$expiration = (new DateTimeImmutable('now'))->add(new DateInterval($input->getOption('subscription-duration')));
$total = $this->userRepository->countByMostOldSubscriptionOrWithoutSubscriptionOrData($interval);
$created = 0;
$renewed = 0;
$this->logger->info(__CLASS__ . ' the number of user to get - renew', [
'total' => $total,
'expiration' => $expiration->format(DateTimeImmutable::ATOM),
]);
while ($offset < ($total - 1)) {
$users = $this->userRepository->findByMostOldSubscriptionOrWithoutSubscriptionOrData(
$interval,
$limit,
$offset
);
foreach ($users as $user) {
if (!$this->mapCalendarToUser->hasUserId($user)) {
$this->mapCalendarToUser->writeMetadata($user);
}
if ($this->mapCalendarToUser->hasUserId($user)) {
// we first try to renew an existing subscription, if any.
// if not, or if it fails, we try to create a new one
if ($this->mapCalendarToUser->hasActiveSubscription($user)) {
$this->logger->debug(__CLASS__ . ' renew a subscription for', [
'userId' => $user->getId(),
'username' => $user->getUsernameCanonical(),
]);
['secret' => $secret, 'id' => $id, 'expiration' => $expirationTs]
= $this->eventsOnUserSubscriptionCreator->renewSubscriptionForUser($user, $expiration);
$this->mapCalendarToUser->writeSubscriptionMetadata($user, $expirationTs, $id, $secret);
if (0 !== $expirationTs) {
++$renewed;
} else {
$this->logger->warning(__CLASS__ . ' could not renew subscription for a user', [
'userId' => $user->getId(),
'username' => $user->getUsernameCanonical(),
]);
}
}
if (!$this->mapCalendarToUser->hasActiveSubscription($user)) {
$this->logger->debug(__CLASS__ . ' create a subscription for', [
'userId' => $user->getId(),
'username' => $user->getUsernameCanonical(),
]);
['secret' => $secret, 'id' => $id, 'expiration' => $expirationTs]
= $this->eventsOnUserSubscriptionCreator->createSubscriptionForUser($user, $expiration);
$this->mapCalendarToUser->writeSubscriptionMetadata($user, $expirationTs, $id, $secret);
if (0 !== $expirationTs) {
++$created;
} else {
$this->logger->warning(__CLASS__ . ' could not create subscription for a user', [
'userId' => $user->getId(),
'username' => $user->getUsernameCanonical(),
]);
}
}
}
++$offset;
}
$this->em->flush();
$this->em->clear();
}
$this->logger->warning(__CLASS__ . ' process executed', [
'created' => $created,
'renewed' => $renewed,
]);
return 0;
}
protected function configure()
{
parent::configure();
$this
->setDescription('MSGraph: collect user metadata and create subscription on events for users')
->addOption(
'renew-before-end-interval',
'r',
InputOption::VALUE_OPTIONAL,
'delay before renewing subscription',
'P1D'
)
->addOption(
'subscription-duration',
's',
InputOption::VALUE_OPTIONAL,
'duration for the subscription',
'PT4230M'
);
}
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* Chill is a software for social workers
*
* 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\CalendarBundle\Command;
use Chill\CalendarBundle\Service\ShortMessageNotification\BulkCalendarShortMessageSender;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class SendShortMessageOnEligibleCalendar extends Command
{
private BulkCalendarShortMessageSender $messageSender;
public function __construct(BulkCalendarShortMessageSender $messageSender)
{
parent::__construct();
$this->messageSender = $messageSender;
}
public function getName()
{
return 'chill:calendar:send-short-messages';
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->messageSender->sendBulkMessageToEligibleCalendars();
return 0;
}
}

View File

@@ -0,0 +1,200 @@
<?php
/**
* Chill is a software for social workers
*
* 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\CalendarBundle\Command;
use Chill\CalendarBundle\Entity\Calendar;
use Chill\CalendarBundle\Service\ShortMessageNotification\ShortMessageForCalendarBuilderInterface;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Phonenumber\PhoneNumberHelperInterface;
use Chill\MainBundle\Repository\UserRepositoryInterface;
use Chill\MainBundle\Service\ShortMessage\ShortMessageTransporterInterface;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Repository\PersonRepository;
use DateInterval;
use DateTimeImmutable;
use libphonenumber\PhoneNumber;
use libphonenumber\PhoneNumberFormat;
use libphonenumber\PhoneNumberType;
use libphonenumber\PhoneNumberUtil;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use UnexpectedValueException;
use function count;
class SendTestShortMessageOnCalendarCommand extends Command
{
private ShortMessageForCalendarBuilderInterface $messageForCalendarBuilder;
private PersonRepository $personRepository;
private PhoneNumberHelperInterface $phoneNumberHelper;
private PhoneNumberUtil $phoneNumberUtil;
private ShortMessageTransporterInterface $transporter;
private UserRepositoryInterface $userRepository;
public function __construct(
PersonRepository $personRepository,
PhoneNumberUtil $phoneNumberUtil,
PhoneNumberHelperInterface $phoneNumberHelper,
ShortMessageForCalendarBuilderInterface $messageForCalendarBuilder,
ShortMessageTransporterInterface $transporter,
UserRepositoryInterface $userRepository
) {
parent::__construct();
$this->personRepository = $personRepository;
$this->phoneNumberUtil = $phoneNumberUtil;
$this->phoneNumberHelper = $phoneNumberHelper;
$this->messageForCalendarBuilder = $messageForCalendarBuilder;
$this->transporter = $transporter;
$this->userRepository = $userRepository;
}
public function getName()
{
return 'chill:calendar:test-send-short-message';
}
protected function configure()
{
$this->setDescription('Test sending a SMS for a dummy calendar appointment');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$calendar = new Calendar();
$calendar->setSendSMS(true);
/** @var QuestionHelper $helper */
$helper = $this->getHelper('question');
// start date
$question = new Question('When will start the appointment ? (default: "1 hour") ', '1 hour');
$startDate = new DateTimeImmutable($helper->ask($input, $output, $question));
if (false === $startDate) {
throw new UnexpectedValueException('could not create a date with this date and time');
}
$calendar->setStartDate($startDate);
// end date
$question = new Question('How long will last the appointment ? (default: "PT30M") ', 'PT30M');
$interval = new DateInterval($helper->ask($input, $output, $question));
if (false === $interval) {
throw new UnexpectedValueException('could not create the interval');
}
$calendar->setEndDate($calendar->getStartDate()->add($interval));
// a person
$question = new Question('Who will participate ? Give an id for a person. ');
$question
->setValidator(function ($answer): Person {
if (!is_numeric($answer)) {
throw new UnexpectedValueException('the answer must be numeric');
}
if (0 >= (int) $answer) {
throw new UnexpectedValueException('the answer must be greater than zero');
}
$person = $this->personRepository->find((int) $answer);
if (null === $person) {
throw new UnexpectedValueException('The person is not found');
}
return $person;
});
$person = $helper->ask($input, $output, $question);
$calendar->addPerson($person);
// a main user
$question = new Question('Who will be the main user ? Give an id for a user. ');
$question
->setValidator(function ($answer): User {
if (!is_numeric($answer)) {
throw new UnexpectedValueException('the answer must be numeric');
}
if (0 >= (int) $answer) {
throw new UnexpectedValueException('the answer must be greater than zero');
}
$user = $this->userRepository->find((int) $answer);
if (null === $user) {
throw new UnexpectedValueException('The user is not found');
}
return $user;
});
$user = $helper->ask($input, $output, $question);
$calendar->setMainUser($user);
// phonenumber
$phonenumberFormatted = null !== $person->getMobilenumber() ?
$this->phoneNumberUtil->format($person->getMobilenumber(), PhoneNumberFormat::E164) : '';
$question = new Question(
sprintf('To which number are we going to send this fake message ? (default to: %s)', $phonenumberFormatted),
$phonenumberFormatted
);
$question->setNormalizer(function ($answer): PhoneNumber {
if (null === $answer) {
throw new UnexpectedValueException('The person is not found');
}
$phone = $this->phoneNumberUtil->parse($answer, 'BE');
if (!$this->phoneNumberUtil->isPossibleNumberForType($phone, PhoneNumberType::MOBILE)) {
throw new UnexpectedValueException('Phone number si not a mobile');
}
return $phone;
});
$phone = $helper->ask($input, $output, $question);
$question = new ConfirmationQuestion('really send the message to the phone ?');
$reallySend = (bool) $helper->ask($input, $output, $question);
$messages = $this->messageForCalendarBuilder->buildMessageForCalendar($calendar);
if (0 === count($messages)) {
$output->writeln('no message to send to this user');
}
foreach ($messages as $key => $message) {
$output->writeln("The short message for SMS {$key} will be: ");
$output->writeln($message->getContent());
$message->setPhoneNumber($phone);
if ($reallySend) {
$this->transporter->send($message);
}
}
return 0;
}
}

Some files were not shown because too many files have changed in this diff Show More