diff --git a/config/routes/app_routes.yml b/config/routes/app_routes.yml index c7db39d..12bb0cb 100644 --- a/config/routes/app_routes.yml +++ b/config/routes/app_routes.yml @@ -16,3 +16,8 @@ general: resource: "apps/general.yml" prefix: /general name_prefix: general_ + +census: + resource: "apps/census.yml" + prefix: /census + name_prefix: census_ diff --git a/config/routes/apps/census.yml b/config/routes/apps/census.yml new file mode 100644 index 0000000..7ee376e --- /dev/null +++ b/config/routes/apps/census.yml @@ -0,0 +1,14 @@ +preview: + path: /preview + methods: GET + controller: App\Controller\Api\Apps\CensusController::getPreview + +getFilter: + path: /filter + methods: GET + controller: App\Controller\Api\Apps\CensusController:getFilterData + +postFilter: + path: /filter + methods: POST + controller: App\Controller\Api\Apps\CensusController:postFilterData diff --git a/config/routes/apps/widgets.yml b/config/routes/apps/widgets.yml index 3ea75ed..b98fb6d 100644 --- a/config/routes/apps/widgets.yml +++ b/config/routes/apps/widgets.yml @@ -48,3 +48,24 @@ role_overview: path: /role-overview methods: GET controller: App\Controller\Api\Apps\Widgets\RoleOverviewController:getRoleOverview + +census_table: + path: /census-table + methods: GET + controller: App\Controller\Api\Apps\CensusController::getTableData + +census_members: + path: /census-members + methods: GET + controller: App\Controller\Api\Apps\CensusController::getMembersData + +census_development: + path: /census-development + methods: GET + controller: App\Controller\Api\Apps\CensusController::getDevelopmentData + +census_treemap: + path: /census-treemap + methods: GET + controller: App\Controller\Api\Apps\CensusController::getTreemapData + diff --git a/src/Command/FetchCensusCommand.php b/src/Command/FetchCensusCommand.php index 995c1dc..6382700 100644 --- a/src/Command/FetchCensusCommand.php +++ b/src/Command/FetchCensusCommand.php @@ -2,15 +2,33 @@ namespace App\Command; +use App\Entity\Midata\CensusGroup; use App\Model\CommandStatistics; +use App\Repository\Midata\CensusGroupRepository; +use App\Repository\Midata\GroupTypeRepository; +use App\Service\CensusAPIService; use App\Service\GroupStructureAPIService; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; class FetchCensusCommand extends StatisticsCommand { - protected GroupStructureAPIService $apiService; + protected CensusAPIService $apiService; + protected CensusGroupRepository $censusGroupRepository; + protected GroupTypeRepository $groupTypeRepository; - public function __construct() - { + private SymfonyStyle $io; + + public function __construct( + CensusAPIService $apiService, + CensusGroupRepository $censusGroupRepository, + GroupTypeRepository $groupTypeRepository + ) { + $this->apiService = $apiService; + $this->censusGroupRepository = $censusGroupRepository; + $this->groupTypeRepository = $groupTypeRepository; parent::__construct(); } @@ -21,6 +39,69 @@ public function configure() ->setDescription('Not implemented'); } + public function execute(InputInterface $input, OutputInterface $output) + { + $this->io = new SymfonyStyle($input, $output); + + $year = (int) date('Y'); + $minYear = $year - 6; + $groupsToAggregate = []; + // Fetch groups + while ($year > $minYear) { + $this->io->writeln('year ' . $year); + $rawCensusData = $this->apiService->getCensusData($year); + $rawCensusGroups = $rawCensusData->getContent()['census_evaluations']['groups']; + foreach ($rawCensusGroups as $rawCensusGroup) { + $exists = $this->censusGroupRepository->findOneBy(['group_id' => $rawCensusGroup['group_id'], 'year' => $year]); + if (is_null($exists)) { + $groupsToAggregate[] = $rawCensusGroup['group_id']; + $this->mapRawCensusGroupToCensusGroup($rawCensusGroup, $year); + } + } + $year--; + } + // Aggregate Groups + foreach (array_unique($groupsToAggregate) as $groupId) { + } + return Command::SUCCESS; + } + + private function mapRawCensusGroupToCensusGroup(array $rawCensusGroup, int $year) + { + $censusGroup = new CensusGroup(); + $censusGroup->setGroupId($this->sanitizeValue($rawCensusGroup['group_id'])); + $censusGroup->setYear($year); + $censusGroup->setGroupType($this->groupTypeRepository->findOneBy(['groupType' => $rawCensusGroup['group_type']])); + $censusGroup->setName($rawCensusGroup['group_name']); + + $censusGroup->setTotalCount($this->sanitizeValue($rawCensusGroup['total']['total'])); + $censusGroup->setTotalFCount($this->sanitizeValue($rawCensusGroup['total']['f'])); + $censusGroup->setTotalMCount($this->sanitizeValue($rawCensusGroup['total']['m'])); + + $censusGroup->setLeiterFCount($this->sanitizeValue($rawCensusGroup['f']['leiter'])); + $censusGroup->setBiberFCount($this->sanitizeValue($rawCensusGroup['f']['biber'])); + $censusGroup->setWoelfeFCount($this->sanitizeValue($rawCensusGroup['f']['woelfe'])); + $censusGroup->setPfadisFCount($this->sanitizeValue($rawCensusGroup['f']['pfadis'])); + $censusGroup->setPiosFCount($this->sanitizeValue($rawCensusGroup['f']['pios'])); + $censusGroup->setRoverFCount($this->sanitizeValue($rawCensusGroup['f']['rover'])); + $censusGroup->setPtaFCount($this->sanitizeValue($rawCensusGroup['f']['pta'])); + + $censusGroup->setLeiterMCount($this->sanitizeValue($rawCensusGroup['m']['leiter'])); + $censusGroup->setBiberMCount($this->sanitizeValue($rawCensusGroup['m']['biber'])); + $censusGroup->setWoelfeMCount($this->sanitizeValue($rawCensusGroup['m']['woelfe'])); + $censusGroup->setPfadisMCount($this->sanitizeValue($rawCensusGroup['m']['pfadis'])); + $censusGroup->setPiosMCount($this->sanitizeValue($rawCensusGroup['m']['pios'])); + $censusGroup->setRoverMCount($this->sanitizeValue($rawCensusGroup['m']['rover'])); + $censusGroup->setPtaMCount($this->sanitizeValue($rawCensusGroup['m']['pta'])); + $this->censusGroupRepository->add($censusGroup); + } + + private function sanitizeValue($raw): int + { + return is_null($raw) ? 0 : $raw; + } + + // TODO: Implement the statistics public function getStats(): CommandStatistics { diff --git a/src/Command/ImportFromJsonCommand.php b/src/Command/ImportFromJsonCommand.php index 8bf6556..1bde142 100644 --- a/src/Command/ImportFromJsonCommand.php +++ b/src/Command/ImportFromJsonCommand.php @@ -392,7 +392,22 @@ private function importGroups(OutputInterface $output) $group->setId($gr['id']); $metadata = $this->em->getClassMetaData(get_class($group)); $metadata->setIdGenerator(new AssignedGenerator()); - $createGroupSettings = true; + + /** @var GroupType $gt */ + $gt = $this->em->getRepository(GroupType::class)->findOneBy(['groupType' => $gr['type']]); + $group->setGroupType($gt); + + // create group settings + $groupSettings = new GroupSettings(); + $groupSettings->setGroup($group); + if ($group->getGroupType()->getGroupType() === GroupType::DEPARTMENT) { + $groupSettings->setRoleOverviewFilter(GroupSettings::DEFAULT_DEPARMENT_ROLES); + } elseif ($group->getGroupType()->getGroupType() === GroupType::REGION) { + $groupSettings->setRoleOverviewFilter(GroupSettings::DEFAULT_REGION_ROLES); + } elseif ($group->getGroupType()->getGroupType() === GroupType::CANTON) { + $groupSettings->setRoleOverviewFilter(GroupSettings::DEFAULT_CANTONAL_ROLES); + } + $this->em->persist($groupSettings); } $group->setName(trim($gr['name'])); diff --git a/src/Controller/Api/Apps/CensusController.php b/src/Controller/Api/Apps/CensusController.php new file mode 100644 index 0000000..5d9af26 --- /dev/null +++ b/src/Controller/Api/Apps/CensusController.php @@ -0,0 +1,105 @@ +censusDataProvider = $censusDataProvider; + $this->censusFilterDataProvider = $censusFilterDataProvider; + } + + /** + * @param Group $group + * @return JsonResponse + * + * @ParamConverter("group", options={"mapping": {"groupId": "id"}}) + */ + public function getPreview(Group $group) + { + $this->denyAccessUnlessGranted(PermissionVoter::VIEWER, $group); + return $this->json($this->censusDataProvider->getPreviewData($group)); + } + + /** + * @param Group $group + * @return JsonResponse + * + * @ParamConverter("group", options={"mapping": {"groupId": "id"}}) + */ + public function getTableData(Group $group, CensusRequestData $censusRequestData) + { + $this->denyAccessUnlessGranted(PermissionVoter::VIEWER, $group); + return $this->json($this->censusDataProvider->getTableData($group, $censusRequestData)); + } + + /** + * @param Group $group + * @return JsonResponse + * + * @ParamConverter("group", options={"mapping": {"groupId": "id"}}) + */ + public function getDevelopmentData(Group $group, CensusRequestData $censusRequestData) + { + $this->denyAccessUnlessGranted(PermissionVoter::VIEWER, $group); + return $this->json($this->censusDataProvider->getDevelopmentData($group, $censusRequestData)); + } + + + /** + * @param Group $group + * @return JsonResponse + * + * @ParamConverter("group", options={"mapping": {"groupId": "id"}}) + */ + public function getMembersData(Group $group, CensusRequestData $censusRequestData) + { + $this->denyAccessUnlessGranted(PermissionVoter::VIEWER, $group); + return $this->json($this->censusDataProvider->getMembersData($group, $censusRequestData)); + } + + /** + * @param Group $group + * @return JsonResponse + * + * @ParamConverter("group", options={"mapping": {"groupId": "id"}}) + */ + public function getTreemapData(Group $group, CensusRequestData $censusRequestData) + { + $this->denyAccessUnlessGranted(PermissionVoter::VIEWER, $group); + return $this->json($this->censusDataProvider->getTreemapData($group, $censusRequestData)); + } + + /** + * @param Group $group + * @return JsonResponse + * + * @ParamConverter("group", options={"mapping": {"groupId": "id"}}) + */ + public function getFilterData(Group $group) + { + $this->denyAccessUnlessGranted(PermissionVoter::VIEWER, $group); + return $this->json($this->censusFilterDataProvider->getFilterData($group)); + } + + public function postFilterData(Group $group, CensusRequestData $censusRequestData) + { + $this->denyAccessUnlessGranted(PermissionVoter::VIEWER, $group); + return $this->json($this->censusFilterDataProvider->setFilterData($group, $censusRequestData)); + } +} diff --git a/src/Controller/Api/Apps/Widgets/FilterController.php b/src/Controller/Api/Apps/Widgets/FilterController.php index 6042aa4..27d1bd9 100644 --- a/src/Controller/Api/Apps/Widgets/FilterController.php +++ b/src/Controller/Api/Apps/Widgets/FilterController.php @@ -31,4 +31,22 @@ public function getFilterData( return $this->json($data); } + + /** + * @param Request $request + * @param Group $group + * @param FilterDataProvider $filterDataProvider + * @return JsonResponse + * + * @ParamConverter("group", options={"mapping": {"groupId": "id"}}) + */ + public function getGroupTypes( + Request $request, + Group $group, + FilterDataProvider $filterDataProvider + ): JsonResponse { + $this->denyAccessUnlessGranted(PermissionVoter::VIEWER, $group); + $data = $filterDataProvider->getGroupTypes($group, $request->getLocale()); + return $this->json($data); + } } diff --git a/src/Controller/Api/InviteController.php b/src/Controller/Api/InviteController.php index 06f3fe0..75b3286 100644 --- a/src/Controller/Api/InviteController.php +++ b/src/Controller/Api/InviteController.php @@ -78,7 +78,7 @@ public function createInvite( } if ($inviteDTO->getPermissionType() === PermissionVoter::OWNER) { - throw new ApiException(Response::HTTP_FORBIDDEN,'You may not add group Owners.'); + throw new ApiException(Response::HTTP_FORBIDDEN, 'You may not add group Owners.'); } if ($this->inviteService->inviteExists($group, $inviteDTO->getEmail())) { diff --git a/src/DTO/Mapper/CensusMapper.php b/src/DTO/Mapper/CensusMapper.php new file mode 100644 index 0000000..4a9a55a --- /dev/null +++ b/src/DTO/Mapper/CensusMapper.php @@ -0,0 +1,114 @@ +setId($statisticGroup->getId()); + $dto->setName($statisticGroup->getName()); + $dto->setType($statisticGroup->getGroupType()->getGroupType()); + $parent = $statisticGroup->getParentGroup(); + $parentId = !is_null($parent) ? $parent->getId() : null; + $dto->setParentId($parentId); + + if (sizeof($censusGroups) < 1) { + $dto->setMissing(true); + return $dto; + } + $dto->setMissing(false); + + $incomplete = false; + $totalCounts = []; + foreach ($relevantYears as $year) { + $found = false; + foreach ($censusGroups as $censusGroup) { + if ($censusGroup->getYear() == $year) { + $totalCounts[] = $censusGroup->getCalculatedTotal(); + $found = true; + } + } + if (!$found) { + $totalCounts[] = null; + $incomplete = true; + } + } + $dto->setAbsoluteMemberCounts($totalCounts); + + $improvementVsLastYear = null; + $improvementVs3YearsAgo = null; + $improvementVsAvg5Years = null; + if (!is_null($totalCounts[count($totalCounts) - 1])) { + if (!is_null($totalCounts[count($totalCounts) - 2])) { + $improvementVsLastYear = (100 / $totalCounts[count($totalCounts) - 2]) * $totalCounts[count($totalCounts) - 1] - 100; + } + if (!is_null($totalCounts[count($totalCounts) - 4])) { + $improvementVs3YearsAgo = (100 / $totalCounts[count($totalCounts) - 4]) * $totalCounts[count($totalCounts) - 1] - 100; + } + } + if (!$incomplete) { + $improvementVsAvg5Years = (100 / (($totalCounts[0] + $totalCounts[1] + $totalCounts[2] + $totalCounts[3] + $totalCounts[4]) / 5)) * $totalCounts[count($totalCounts) - 1] - 100; + } + $dto->setRelativeMemberCounts([$improvementVsLastYear, $improvementVs3YearsAgo, $improvementVsAvg5Years]); + return $dto; + } + + /** + * @param StatisticGroup $statisticGroup + * @param CensusGroup[] $censusGroups + * @param int[] $relevantYears + */ + public static function MapToLineChart(StatisticGroup $statisticGroup, array $censusGroups, array $relevantYears) + { + $groupData = new DevelopmentWidgetDTO(); + $absolute = []; + $relative = []; + $firstRelevantTotal = null; + foreach ($censusGroups as $censusGroup) { + if ($censusGroup->getYear() == $relevantYears[0]) { + $firstRelevantTotal = $censusGroup->getCalculatedTotal(); + } + } + foreach ($relevantYears as $year) { + $found = false; + foreach ($censusGroups as $censusGroup) { + if ($censusGroup->getYear() == $year) { + $found = true; + $absolute[] = $censusGroup->getCalculatedTotal(); + $relative[] = $firstRelevantTotal ? 100 / $firstRelevantTotal * $censusGroup->getCalculatedTotal() - 100 : null; + } + } + if (!$found) { + $absolute[] = null; + $relative[] = null; + } + } + $absoluteDTO = new LineChartDataDTO(); + $relativeDTO = new LineChartDataDTO(); + + $absoluteDTO->setLabel($statisticGroup->getName()); + $absoluteDTO->setData($absolute); + $relativeDTO->setLabel($statisticGroup->getName()); + $relativeDTO->setData($relative); + + $return = new DevelopmentWidgetDTO(); + $return->setAbsolute([$absoluteDTO]); + $return->setRelative([$relativeDTO]); + return $return; + } +} diff --git a/src/DTO/Mapper/FilterDataMapper.php b/src/DTO/Mapper/FilterDataMapper.php index 1a1689d..e8cbf5e 100644 --- a/src/DTO/Mapper/FilterDataMapper.php +++ b/src/DTO/Mapper/FilterDataMapper.php @@ -25,4 +25,13 @@ public static function createFromEntities(array $groupTypes, array $dates, strin $filterData->setGroupTypes($groupTypeDTOs); return $filterData; } + + public static function createGroupTypes(array $groupTypes, string $locale) + { + $groupTypeDTOs = []; + foreach ($groupTypes as $type) { + $groupTypeDTOs[] = GroupTypeMapper::createGroupTypeFromQueryResult($type, $locale); + } + return $groupTypeDTOs; + } } diff --git a/src/DTO/Model/Apps/Census/CensusFilterDTO.php b/src/DTO/Model/Apps/Census/CensusFilterDTO.php new file mode 100644 index 0000000..585b5c0 --- /dev/null +++ b/src/DTO/Model/Apps/Census/CensusFilterDTO.php @@ -0,0 +1,85 @@ +roles; + } + + /** + * @param array|null $roles + */ + public function setRoles(?array $roles): void + { + $this->roles = $roles; + } + + /** + * @return array|null + */ + public function getGroups(): ?array + { + return $this->groups; + } + + /** + * @param array|null $groups + */ + public function setGroups(?array $groups): void + { + $this->groups = $groups; + } + + + /** + * @return bool + */ + public function isFilterMales(): bool + { + return $this->filterMales; + } + + /** + * @param bool $filterMales + */ + public function setFilterMales(bool $filterMales): void + { + $this->filterMales = $filterMales; + } + + /** + * @return bool + */ + public function isFilterFemales(): bool + { + return $this->filterFemales; + } + + /** + * @param bool $filterFemales + */ + public function setFilterFemales(bool $filterFemales): void + { + $this->filterFemales = $filterFemales; + } +} diff --git a/src/DTO/Model/Apps/Census/DevelopmentWidgetDTO.php b/src/DTO/Model/Apps/Census/DevelopmentWidgetDTO.php new file mode 100644 index 0000000..fc13df7 --- /dev/null +++ b/src/DTO/Model/Apps/Census/DevelopmentWidgetDTO.php @@ -0,0 +1,65 @@ +years; + } + + /** + * @param array $years + */ + public function setYears(array $years): void + { + $this->years = $years; + } + + /** + * @return array + */ + public function getAbsolute(): array + { + return $this->absolute; + } + + /** + * @param array $absolute + */ + public function setAbsolute(array $absolute): void + { + $this->absolute = $absolute; + } + + /** + * @return array + */ + public function getRelative(): array + { + return $this->relative; + } + + /** + * @param array $relative + */ + public function setRelative(array $relative): void + { + $this->relative = $relative; + } +} diff --git a/src/DTO/Model/Apps/Census/GroupCensusDTO.php b/src/DTO/Model/Apps/Census/GroupCensusDTO.php new file mode 100644 index 0000000..8cb0dbc --- /dev/null +++ b/src/DTO/Model/Apps/Census/GroupCensusDTO.php @@ -0,0 +1,296 @@ +year; + } + + /** + * @param string $year + */ + public function setYear(string $year): void + { + $this->year = $year; + } + + /** + * @return int + */ + public function getTotalMCount(): int + { + return $this->total_m_count; + } + + /** + * @param int $total_m_count + */ + public function setTotalMCount(int $total_m_count): void + { + $this->total_m_count = $total_m_count; + } + + /** + * @return int + */ + public function getTotalFCount(): int + { + return $this->total_f_count; + } + + /** + * @param int $total_f_count + */ + public function setTotalFCount(int $total_f_count): void + { + $this->total_f_count = $total_f_count; + } + + /** + * @return int + */ + public function getLeiterMCount(): int + { + return $this->leiter_m_count; + } + + /** + * @param int $leiter_m_count + */ + public function setLeiterMCount(int $leiter_m_count): void + { + $this->leiter_m_count = $leiter_m_count; + } + + /** + * @return int + */ + public function getLeiterFCount(): int + { + return $this->leiter_f_count; + } + + /** + * @param int $leiter_f_count + */ + public function setLeiterFCount(int $leiter_f_count): void + { + $this->leiter_f_count = $leiter_f_count; + } + + /** + * @return int + */ + public function getBiberMCount(): int + { + return $this->biber_m_count; + } + + /** + * @param int $biber_m_count + */ + public function setBiberMCount(int $biber_m_count): void + { + $this->biber_m_count = $biber_m_count; + } + + /** + * @return int + */ + public function getBiberFCount(): int + { + return $this->biber_f_count; + } + + /** + * @param int $biber_f_count + */ + public function setBiberFCount(int $biber_f_count): void + { + $this->biber_f_count = $biber_f_count; + } + + /** + * @return int + */ + public function getWoelfeMCount(): int + { + return $this->woelfe_m_count; + } + + /** + * @param int $woelfe_m_count + */ + public function setWoelfeMCount(int $woelfe_m_count): void + { + $this->woelfe_m_count = $woelfe_m_count; + } + + /** + * @return int + */ + public function getWoelfeFCount(): int + { + return $this->woelfe_f_count; + } + + /** + * @param int $woelfe_f_count + */ + public function setWoelfeFCount(int $woelfe_f_count): void + { + $this->woelfe_f_count = $woelfe_f_count; + } + + /** + * @return int + */ + public function getPfadisMCount(): int + { + return $this->pfadis_m_count; + } + + /** + * @param int $pfadis_m_count + */ + public function setPfadisMCount(int $pfadis_m_count): void + { + $this->pfadis_m_count = $pfadis_m_count; + } + + /** + * @return int + */ + public function getPfadisFCount(): int + { + return $this->pfadis_f_count; + } + + /** + * @param int $pfadis_f_count + */ + public function setPfadisFCount(int $pfadis_f_count): void + { + $this->pfadis_f_count = $pfadis_f_count; + } + + /** + * @return int + */ + public function getPiosMCount(): int + { + return $this->pios_m_count; + } + + /** + * @param int $pios_m_count + */ + public function setPiosMCount(int $pios_m_count): void + { + $this->pios_m_count = $pios_m_count; + } + + /** + * @return int + */ + public function getPiosFCount(): int + { + return $this->pios_f_count; + } + + /** + * @param int $pios_f_count + */ + public function setPiosFCount(int $pios_f_count): void + { + $this->pios_f_count = $pios_f_count; + } + + /** + * @return int + */ + public function getRoverMCount(): int + { + return $this->rover_m_count; + } + + /** + * @param int $rover_m_count + */ + public function setRoverMCount(int $rover_m_count): void + { + $this->rover_m_count = $rover_m_count; + } + + /** + * @return int + */ + public function getRoverFCount(): int + { + return $this->rover_f_count; + } + + /** + * @param int $rover_f_count + */ + public function setRoverFCount(int $rover_f_count): void + { + $this->rover_f_count = $rover_f_count; + } + + /** + * @return int + */ + public function getPtaMCount(): int + { + return $this->pta_m_count; + } + + /** + * @param int $pta_m_count + */ + public function setPtaMCount(int $pta_m_count): void + { + $this->pta_m_count = $pta_m_count; + } + + /** + * @return int + */ + public function getPtaFCount(): int + { + return $this->pta_f_count; + } + + /** + * @param int $pta_f_count + */ + public function setPtaFCount(int $pta_f_count): void + { + $this->pta_f_count = $pta_f_count; + } +} diff --git a/src/DTO/Model/Apps/Census/LineChartDataDTO.php b/src/DTO/Model/Apps/Census/LineChartDataDTO.php new file mode 100644 index 0000000..4310752 --- /dev/null +++ b/src/DTO/Model/Apps/Census/LineChartDataDTO.php @@ -0,0 +1,41 @@ +label; + } + + /** + * @param string $label + */ + public function setLabel(string $label): void + { + $this->label = $label; + } + + /** + * @return array + */ + public function getData(): array + { + return $this->data; + } + + /** + * @param array $data + */ + public function setData(array $data): void + { + $this->data = $data; + } +} diff --git a/src/DTO/Model/Apps/Census/MembersWidgetDTO.php b/src/DTO/Model/Apps/Census/MembersWidgetDTO.php new file mode 100644 index 0000000..25e601e --- /dev/null +++ b/src/DTO/Model/Apps/Census/MembersWidgetDTO.php @@ -0,0 +1,27 @@ +data; + } + + /** + * @param array $data + */ + public function setData(array $data): void + { + $this->data = $data; + } +} diff --git a/src/DTO/Model/Apps/Census/StackedBarElementDTO.php b/src/DTO/Model/Apps/Census/StackedBarElementDTO.php new file mode 100644 index 0000000..a6a2fb6 --- /dev/null +++ b/src/DTO/Model/Apps/Census/StackedBarElementDTO.php @@ -0,0 +1,70 @@ +y = $y; + $this->x = $x; + $this->color = $color; + } + + /** + * @return int + */ + public function getY(): int + { + return $this->y; + } + + /** + * @param int $y + */ + public function setY(int $y): void + { + $this->y = $y; + } + + /** + * @return string + */ + public function getX(): string + { + return $this->x; + } + + /** + * @param string $x + */ + public function setX(string $x): void + { + $this->x = $x; + } + + /** + * @return string + */ + public function getColor(): string + { + return $this->color; + } + + /** + * @param string $color + */ + public function setColor(string $color): void + { + $this->color = $color; + } +} diff --git a/src/DTO/Model/Apps/Census/TableDTO.php b/src/DTO/Model/Apps/Census/TableDTO.php new file mode 100644 index 0000000..b84f9f2 --- /dev/null +++ b/src/DTO/Model/Apps/Census/TableDTO.php @@ -0,0 +1,132 @@ +id; + } + + /** + * @param int $id + */ + public function setId(int $id): void + { + $this->id = $id; + } + + /** + * @return int + */ + public function getParentId(): int + { + return $this->parentId; + } + + /** + * @param int $parentId + */ + public function setParentId(int $parentId): void + { + $this->parentId = $parentId; + } + + /** + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * @param string $type + */ + public function setType(string $type): void + { + $this->type = $type; + } + + /** + * @return bool + */ + public function isMissing(): bool + { + return $this->missing; + } + + /** + * @param bool $missing + */ + public function setMissing(bool $missing): void + { + $this->missing = $missing; + } + + /** + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * @param string $name + */ + public function setName(string $name): void + { + $this->name = $name; + } + + /** + * @return array + */ + public function getAbsoluteMemberCounts(): array + { + return $this->absoluteMemberCounts; + } + + /** + * @param array $absoluteMemberCounts + */ + public function setAbsoluteMemberCounts(array $absoluteMemberCounts): void + { + $this->absoluteMemberCounts = $absoluteMemberCounts; + } + + /** + * @return array + */ + public function getRelativeMemberCounts(): array + { + return $this->relativeMemberCounts; + } + + /** + * @param array $relativeMemberCounts + */ + public function setRelativeMemberCounts(array $relativeMemberCounts): void + { + $this->relativeMemberCounts = $relativeMemberCounts; + } +} diff --git a/src/DTO/Model/Apps/Census/TreemapWidgetDTO.php b/src/DTO/Model/Apps/Census/TreemapWidgetDTO.php new file mode 100644 index 0000000..d68cd19 --- /dev/null +++ b/src/DTO/Model/Apps/Census/TreemapWidgetDTO.php @@ -0,0 +1,77 @@ +name; + } + + /** + * @param string $name + */ + public function setName(string $name): void + { + $this->name = $name; + } + + /** + * @return string + */ + public function getRegion(): string + { + return $this->region; + } + + /** + * @param string $region + */ + public function setRegion(string $region): void + { + $this->region = $region; + } + + /** + * @return string + */ + public function getColor(): string + { + return $this->color; + } + + /** + * @param string $color + */ + public function setColor(string $color): void + { + $this->color = $color; + } + + /** + * @return int + */ + public function getValue(): int + { + return $this->value; + } + + /** + * @param int $value + */ + public function setValue(int $value): void + { + $this->value = $value; + } +} diff --git a/src/DTO/Model/FilterRequestData/CensusRequestData.php b/src/DTO/Model/FilterRequestData/CensusRequestData.php new file mode 100644 index 0000000..5f599aa --- /dev/null +++ b/src/DTO/Model/FilterRequestData/CensusRequestData.php @@ -0,0 +1,105 @@ +group; + } + + /** + * @param Group $group + */ + public function setGroup(Group $group): void + { + $this->group = $group; + } + + /** + * @return array|null + */ + public function getRoles(): ?array + { + return $this->roles; + } + + /** + * @param array|null $roles + */ + public function setRoles(?array $roles): void + { + $this->roles = $roles; + } + + /** + * @return array|null + */ + public function getGroups(): ?array + { + return $this->groups; + } + + /** + * @param array|null $groups + */ + public function setGroups(?array $groups): void + { + $this->groups = $groups; + } + + + /** + * @return bool + */ + public function isFilterMales(): bool + { + return $this->filterMales; + } + + /** + * @param bool $filterMales + */ + public function setFilterMales(bool $filterMales): void + { + $this->filterMales = $filterMales; + } + + /** + * @return bool + */ + public function isFilterFemales(): bool + { + return $this->filterFemales; + } + + /** + * @param bool $filterFemales + */ + public function setFilterFemales(bool $filterFemales): void + { + $this->filterFemales = $filterFemales; + } +} diff --git a/src/Entity/General/GroupSettings.php b/src/Entity/General/GroupSettings.php index 2f9604d..6a22845 100644 --- a/src/Entity/General/GroupSettings.php +++ b/src/Entity/General/GroupSettings.php @@ -36,6 +36,26 @@ class GroupSettings */ private $roleOverviewFilter = []; + /** + * @ORM\Column(type="array", nullable=true) + */ + private $census_roles = []; + + /** + * @ORM\Column(type="array", nullable=true) + */ + private $census_groups = []; + + /** + * @ORM\Column(type="boolean", nullable=true) + */ + private $census_filter_males; + + /** + * @ORM\Column(type="boolean", nullable=true) + */ + private $census_filter_females; + public function getId(): ?int { return $this->id; @@ -64,4 +84,52 @@ public function setRoleOverviewFilter(?array $roleOverviewFilter): self return $this; } + + public function getCensusRoles(): ?array + { + return $this->census_roles; + } + + public function setCensusRoles(?array $census_roles): self + { + $this->census_roles = $census_roles; + + return $this; + } + + public function getCensusGroups(): ?array + { + return $this->census_groups; + } + + public function setCensusGroups(?array $census_groups): self + { + $this->census_groups = $census_groups; + + return $this; + } + + public function getCensusFilterMales(): ?bool + { + return $this->census_filter_males; + } + + public function setCensusFilterMales(?bool $census_filter_males): self + { + $this->census_filter_males = $census_filter_males; + + return $this; + } + + public function getCensusFilterFemales(): ?bool + { + return $this->census_filter_females; + } + + public function setCensusFilterFemales(?bool $census_filter_females): self + { + $this->census_filter_females = $census_filter_females; + + return $this; + } } diff --git a/src/Entity/Midata/CensusGroup.php b/src/Entity/Midata/CensusGroup.php new file mode 100644 index 0000000..409a3b1 --- /dev/null +++ b/src/Entity/Midata/CensusGroup.php @@ -0,0 +1,467 @@ +id; + } + + /** + * @return mixed + */ + public function getGroupType() + { + return $this->group_type; + } + + /** + * @param mixed $group_type + */ + public function setGroupType($group_type): void + { + $this->group_type = $group_type; + } + + /** + * @return mixed + */ + public function getTotalCount() + { + return $this->total_count; + } + + /** + * @param mixed $total_count + */ + public function setTotalCount($total_count): void + { + $this->total_count = $total_count; + } + + /** + * @return mixed + */ + public function getTotalMCount() + { + return $this->total_m_count; + } + + /** + * @param mixed $total_m_count + */ + public function setTotalMCount($total_m_count): void + { + $this->total_m_count = $total_m_count; + } + + /** + * @return mixed + */ + public function getTotalFCount() + { + return $this->total_f_count; + } + + /** + * @param mixed $total_f_count + */ + public function setTotalFCount($total_f_count): void + { + $this->total_f_count = $total_f_count; + } + + /** + * @return mixed + */ + public function getLeiterMCount() + { + return $this->leiter_m_count; + } + + /** + * @param mixed $leiter_m_count + */ + public function setLeiterMCount($leiter_m_count): void + { + $this->leiter_m_count = $leiter_m_count; + } + + /** + * @return mixed + */ + public function getLeiterFCount() + { + return $this->leiter_f_count; + } + + /** + * @param mixed $leiter_f_count + */ + public function setLeiterFCount($leiter_f_count): void + { + $this->leiter_f_count = $leiter_f_count; + } + + /** + * @return mixed + */ + public function getBiberMCount() + { + return $this->biber_m_count; + } + + /** + * @param mixed $biber_m_count + */ + public function setBiberMCount($biber_m_count): void + { + $this->biber_m_count = $biber_m_count; + } + + /** + * @return mixed + */ + public function getBiberFCount() + { + return $this->biber_f_count; + } + + /** + * @param mixed $biber_f_count + */ + public function setBiberFCount($biber_f_count): void + { + $this->biber_f_count = $biber_f_count; + } + + /** + * @return mixed + */ + public function getWoelfeMCount() + { + return $this->woelfe_m_count; + } + + /** + * @param mixed $woelfe_m_count + */ + public function setWoelfeMCount($woelfe_m_count): void + { + $this->woelfe_m_count = $woelfe_m_count; + } + + /** + * @return mixed + */ + public function getWoelfeFCount() + { + return $this->woelfe_f_count; + } + + /** + * @param mixed $woelfe_f_count + */ + public function setWoelfeFCount($woelfe_f_count): void + { + $this->woelfe_f_count = $woelfe_f_count; + } + + /** + * @return mixed + */ + public function getPfadisMCount() + { + return $this->pfadis_m_count; + } + + /** + * @param mixed $pfadis_m_count + */ + public function setPfadisMCount($pfadis_m_count): void + { + $this->pfadis_m_count = $pfadis_m_count; + } + + /** + * @return mixed + */ + public function getPfadisFCount() + { + return $this->pfadis_f_count; + } + + /** + * @param mixed $pfadis_f_count + */ + public function setPfadisFCount($pfadis_f_count): void + { + $this->pfadis_f_count = $pfadis_f_count; + } + + /** + * @return mixed + */ + public function getPiosMCount() + { + return $this->pios_m_count; + } + + /** + * @param mixed $pios_m_count + */ + public function setPiosMCount($pios_m_count): void + { + $this->pios_m_count = $pios_m_count; + } + + /** + * @return mixed + */ + public function getPiosFCount() + { + return $this->pios_f_count; + } + + /** + * @param mixed $pios_f_count + */ + public function setPiosFCount($pios_f_count): void + { + $this->pios_f_count = $pios_f_count; + } + + /** + * @return mixed + */ + public function getRoverMCount() + { + return $this->rover_m_count; + } + + /** + * @param mixed $rover_m_count + */ + public function setRoverMCount($rover_m_count): void + { + $this->rover_m_count = $rover_m_count; + } + + /** + * @return mixed + */ + public function getRoverFCount() + { + return $this->rover_f_count; + } + + /** + * @param mixed $rover_f_count + */ + public function setRoverFCount($rover_f_count): void + { + $this->rover_f_count = $rover_f_count; + } + + /** + * @return mixed + */ + public function getPtaMCount() + { + return $this->pta_m_count; + } + + /** + * @param mixed $pta_m_count + */ + public function setPtaMCount($pta_m_count): void + { + $this->pta_m_count = $pta_m_count; + } + + /** + * @return mixed + */ + public function getPtaFCount() + { + return $this->pta_f_count; + } + + /** + * @param mixed $pta_f_count + */ + public function setPtaFCount($pta_f_count): void + { + $this->pta_f_count = $pta_f_count; + } + + /** + * @return mixed + */ + public function getName() + { + return $this->name; + } + + /** + * @param mixed $name + */ + public function setName($name): void + { + $this->name = $name; + } + + /** + * @return mixed + */ + public function getGroupId() + { + return $this->group_id; + } + + /** + * @param mixed $group_id + */ + public function setGroupId($group_id): void + { + $this->group_id = $group_id; + } + + /** + * @return mixed + */ + public function getYear() + { + return $this->year; + } + + /** + * @param mixed $year + */ + public function setYear($year): void + { + $this->year = $year; + } + + public function getCalculatedTotal(): int + { + $total = 0; + $total += $this->getPiosMCount(); + $total += $this->getPiosFCount(); + $total += $this->getPtaMCount(); + $total += $this->getPtaFCount(); + $total += $this->getBiberMCount(); + $total += $this->getBiberFCount(); + $total += $this->getWoelfeMCount(); + $total += $this->getWoelfeFCount(); + $total += $this->getRoverMCount(); + $total += $this->getRoverFCount(); + $total += $this->getLeiterMCount(); + $total += $this->getLeiterFCount(); + $total += $this->getPfadisMCount(); + $total += $this->getPfadisFCount(); + return $total; + } +} diff --git a/src/EventListener/WidgetControllerListener.php b/src/EventListener/WidgetControllerListener.php index 54d0eea..711a432 100644 --- a/src/EventListener/WidgetControllerListener.php +++ b/src/EventListener/WidgetControllerListener.php @@ -2,6 +2,7 @@ namespace App\EventListener; +use App\DTO\Model\FilterRequestData\CensusRequestData; use App\DTO\Model\FilterRequestData\DateAndDateRangeRequestData; use App\DTO\Model\FilterRequestData\DateRangeRequestData; use App\DTO\Model\FilterRequestData\DateRequestData; @@ -75,7 +76,7 @@ private function bindData(callable $controller, Request $request) if (is_null($argument->getClass())) { continue; } - if (!is_a($argument->getClass()->getName(), FilterRequestData::class, true)) { + if (!(is_a($argument->getClass()->getName(), FilterRequestData::class, true) || is_a($argument->getClass()->getName(), CensusRequestData::class, true))) { continue; } $data = $this->validateRequest($request, $argument); @@ -89,9 +90,9 @@ private function bindData(callable $controller, Request $request) /** * @param Request $request * @param ReflectionParameter $parameter - * @return FilterRequestData|null + * @return FilterRequestData|null|CensusRequestData */ - private function validateRequest(Request $request, ReflectionParameter $parameter): ?FilterRequestData + private function validateRequest(Request $request, ReflectionParameter $parameter) { $groupId = $request->get('groupId'); $group = $this->groupRepository->findOneByIdAndType($groupId, [ @@ -117,6 +118,8 @@ private function validateRequest(Request $request, ReflectionParameter $paramete return $this->validateDateRangeRequest($group, $request); case WidgetRequestData::class: return $this->validateWidgetRequest($group, $request); + case CensusRequestData::class: + return $this->validateCensusRequest($group, $request); } return null; @@ -216,6 +219,36 @@ private function validateWidgetRequest(Group $group, Request $request): WidgetRe return $data; } + private function validateCensusRequest(Group $group, Request $request): CensusRequestData + { + $genders = $request->get('census-filter-genders'); + $groups = $request->get('census-filter-departments'); + $roles = $request->get('census-filter-roles'); + $rolesChoice = new Choice(WidgetDataProvider::CENSUS_ROLES); + $rolesChoice->multiple = true; + $rolesChoice->max = count(WidgetDataProvider::CENSUS_ROLES); + $rolesErrors = $this->validator->validate($roles, $rolesChoice); + + if (count($rolesErrors) > 0) { + $message = $this->translator->trans('api.error.invalidRequest'); + throw new ApiException(Response::HTTP_UNPROCESSABLE_ENTITY, $message); + } + + $data = new CensusRequestData(); + $data->setGroup($group); + $data->setGroups($groups); + $data->setRoles($roles); + if (is_array($genders)) { + if (array_search('m', $genders)) { + $data->setFilterMales(true); + } + if (array_search('f', $genders)) { + $data->setFilterFemales(true); + } + } + return $data; + } + /** * @param $from * @param $to diff --git a/src/Migrations/Version20230720133258.php b/src/Migrations/Version20230720133258.php new file mode 100644 index 0000000..903a267 --- /dev/null +++ b/src/Migrations/Version20230720133258.php @@ -0,0 +1,36 @@ +addSql('CREATE SEQUENCE census_group_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); + $this->addSql('CREATE TABLE census_group (id INT NOT NULL, group_type_id INT NOT NULL, total_count INT NOT NULL, total_m_count INT NOT NULL, total_f_count INT NOT NULL, leiter_m_count INT NOT NULL, leiter_f_count INT NOT NULL, biber_m_count INT NOT NULL, biber_f_count INT NOT NULL, woelfe_m_count INT NOT NULL, woelfe_f_count INT NOT NULL, pfadis_m_count INT NOT NULL, pfadis_f_count INT NOT NULL, pios_m_count INT NOT NULL, pios_f_count INT NOT NULL, rover_m_count INT NOT NULL, rover_f_count INT NOT NULL, pta_m_count INT NOT NULL, pta_f_count INT NOT NULL, name VARCHAR(255) NOT NULL, group_id INT NOT NULL, year VARCHAR(255) NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_6C90D586434CD89F ON census_group (group_type_id)'); + $this->addSql('ALTER TABLE census_group ADD CONSTRAINT FK_6C90D586434CD89F FOREIGN KEY (group_type_id) REFERENCES midata_group_type (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE SCHEMA public'); + $this->addSql('DROP SEQUENCE census_group_id_seq CASCADE'); + $this->addSql('DROP TABLE census_group'); + } +} diff --git a/src/Migrations/Version20230916160452.php b/src/Migrations/Version20230916160452.php new file mode 100644 index 0000000..8378e7d --- /dev/null +++ b/src/Migrations/Version20230916160452.php @@ -0,0 +1,40 @@ +addSql('ALTER TABLE group_settings ADD census_roles TEXT DEFAULT NULL'); + $this->addSql('ALTER TABLE group_settings ADD census_groups TEXT DEFAULT NULL'); + $this->addSql('ALTER TABLE group_settings ADD census_filter_males BOOLEAN DEFAULT NULL'); + $this->addSql('ALTER TABLE group_settings ADD census_filter_females BOOLEAN DEFAULT NULL'); + $this->addSql('COMMENT ON COLUMN group_settings.census_roles IS \'(DC2Type:array)\''); + $this->addSql('COMMENT ON COLUMN group_settings.census_groups IS \'(DC2Type:array)\''); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE SCHEMA public'); + $this->addSql('ALTER TABLE group_settings DROP census_roles'); + $this->addSql('ALTER TABLE group_settings DROP census_groups'); + $this->addSql('ALTER TABLE group_settings DROP census_filter_males'); + $this->addSql('ALTER TABLE group_settings DROP census_filter_females'); + } +} diff --git a/src/Repository/Aggregated/AggregatedPersonRoleRepository.php b/src/Repository/Aggregated/AggregatedPersonRoleRepository.php index 4986b58..c28a264 100644 --- a/src/Repository/Aggregated/AggregatedPersonRoleRepository.php +++ b/src/Repository/Aggregated/AggregatedPersonRoleRepository.php @@ -36,7 +36,7 @@ public function getHighestAggregatedMidataIndex(): int return $this->createQueryBuilder('a') ->select('MAX(a.midata)') ->getQuery() - ->getResult()[0][1]; + ->getResult()[0][1] ?? 0; } /** diff --git a/src/Repository/Midata/CensusGroupRepository.php b/src/Repository/Midata/CensusGroupRepository.php new file mode 100644 index 0000000..f864645 --- /dev/null +++ b/src/Repository/Midata/CensusGroupRepository.php @@ -0,0 +1,79 @@ + + * + * @method CensusGroup|null find($id, $lockMode = null, $lockVersion = null) + * @method CensusGroup|null findOneBy(array $criteria, array $orderBy = null) + * @method CensusGroup[] findAll() + * @method CensusGroup[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class CensusGroupRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, CensusGroup::class); + } + + /** + * @throws ORMException + * @throws OptimisticLockException + */ + public function add(CensusGroup $entity, bool $flush = true): void + { + $this->_em->persist($entity); + if ($flush) { + $this->_em->flush(); + } + } + + /** + * @throws ORMException + * @throws OptimisticLockException + */ + public function remove(CensusGroup $entity, bool $flush = true): void + { + $this->_em->remove($entity); + if ($flush) { + $this->_em->flush(); + } + } + + // /** + // * @return CensusGroup[] Returns an array of CensusGroup objects + // */ + /* + public function findByExampleField($value) + { + return $this->createQueryBuilder('c') + ->andWhere('c.exampleField = :val') + ->setParameter('val', $value) + ->orderBy('c.id', 'ASC') + ->setMaxResults(10) + ->getQuery() + ->getResult() + ; + } + */ + + /* + public function findOneBySomeField($value): ?CensusGroup + { + return $this->createQueryBuilder('c') + ->andWhere('c.exampleField = :val') + ->setParameter('val', $value) + ->getQuery() + ->getOneOrNullResult() + ; + } + */ +} diff --git a/src/Repository/Statistics/StatisticGroupRepository.php b/src/Repository/Statistics/StatisticGroupRepository.php index 1d058d8..911bfc3 100644 --- a/src/Repository/Statistics/StatisticGroupRepository.php +++ b/src/Repository/Statistics/StatisticGroupRepository.php @@ -2,8 +2,11 @@ namespace App\Repository\Statistics; +use App\Entity\Midata\CensusGroup; use App\Entity\Statistics\StatisticGroup; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\ParameterType; use Doctrine\ORM\Id\AssignedGenerator; use Doctrine\ORM\OptimisticLockException; use Doctrine\ORM\ORMException; @@ -64,6 +67,31 @@ public function flush() $this->_em->flush(); } + /** + * Finds all the children of the group that are group type 2,3 or 8. (Kanton, Region, Abteilung) + * @param int $groupId + * @return int[] + * @throws Exception + */ + public function findAllRelevantChildGroups(int $groupId): array + { + $conn = $this->_em->getConnection(); + $query = $conn->executeQuery( + "WITH RECURSIVE parent as ( + SELECT id + FROM statistic_group + WHERE id = (?) + UNION + SELECT child.id + FROM statistic_group child, parent p + Where child.parent_group_id = p.id AND child.group_type_id IN (2,3,8) + ) SELECT * from parent;", + [$groupId], + [ParameterType::INTEGER] + ); + return $query->fetchFirstColumn(); + } + // /** // * @return StatisticGroup[] Returns an array of StatisticGroup objects // */ diff --git a/src/Service/Apps/Census/CensusFilter.php b/src/Service/Apps/Census/CensusFilter.php new file mode 100644 index 0000000..c5fbe64 --- /dev/null +++ b/src/Service/Apps/Census/CensusFilter.php @@ -0,0 +1,76 @@ +getGroups(); + $roles = $censusRequestData->getRoles(); + foreach ($censusGroups as $group) { + if (array_search($group->getId(), $groups)) { + continue; + } + $filteredGroup = clone $group; + if (array_search('leiter', $roles)) { + $filteredGroup->setLeiterMCount(0); + $filteredGroup->setLeiterFCount(0); + } + if (array_search('biber', $roles)) { + $filteredGroup->setBiberMCount(0); + $filteredGroup->setBiberFCount(0); + } + if (array_search('woelfe', $roles)) { + $filteredGroup->setWoelfeMCount(0); + $filteredGroup->setWoelfeFCount(0); + } + if (array_search('pfadis', $roles)) { + $filteredGroup->setPfadisMCount(0); + $filteredGroup->setPfadisFCount(0); + } + if (array_search('rover', $roles)) { + $filteredGroup->setRoverMCount(0); + $filteredGroup->setRoverFCount(0); + } + if (array_search('pio', $roles)) { + $filteredGroup->setPiosMCount(0); + $filteredGroup->setPiosFCount(0); + } + if (array_search('pta', $roles)) { + $filteredGroup->setPtaMCount(0); + $filteredGroup->setPtaFCount(0); + } + if ($censusRequestData->isFilterMales()) { + $filteredGroup->setLeiterMCount(0); + $filteredGroup->setBiberMCount(0); + $filteredGroup->setWoelfeMCount(0); + $filteredGroup->setPfadisMCount(0); + $filteredGroup->setRoverMCount(0); + $filteredGroup->setPiosMCount(0); + $filteredGroup->setPtaMCount(0); + } + if ($censusRequestData->isFilterFemales()) { + $filteredGroup->setLeiterFCount(0); + $filteredGroup->setBiberFCount(0); + $filteredGroup->setWoelfeFCount(0); + $filteredGroup->setPfadisFCount(0); + $filteredGroup->setRoverFCount(0); + $filteredGroup->setPiosFCount(0); + $filteredGroup->setPtaFCount(0); + } + $filteredGroups[] = $filteredGroup; + } + return $filteredGroups; + } +} diff --git a/src/Service/CensusAPIService.php b/src/Service/CensusAPIService.php index 3540295..cc73eea 100644 --- a/src/Service/CensusAPIService.php +++ b/src/Service/CensusAPIService.php @@ -27,15 +27,9 @@ public function __construct(GuzzleWrapper $guzzleWrapper, string $url, string $a } - /** - * Fetch a group from the Group Structure API. - * This can return any group regardless of healthcheck opt-out or any other factor. - * @param int $groupId - * @return Http\CurlResponse - */ - public function getGroup(int $groupId): Http\CurlResponse + public function getCensusData(int $year): Http\CurlResponse { - $endpoint = $this->url . '/de/groups/' . $groupId . '.json?token=' . $this->apiToken; + $endpoint = $this->url . '/group_health/census_evaluations.json?token=' . $this->apiToken . '&year=' . $year; return $this->guzzleWrapper->getJson($endpoint, null, []); } diff --git a/src/Service/DataProvider/CensusDataProvider.php b/src/Service/DataProvider/CensusDataProvider.php new file mode 100644 index 0000000..123510d --- /dev/null +++ b/src/Service/DataProvider/CensusDataProvider.php @@ -0,0 +1,300 @@ +censusGroupRepository = $censusGroupRepository; + $this->statisticGroupRepository = $statisticGroupRepository; + parent::__construct( + $groupRepository, + $groupTypeRepository, + $translator + ); + } + + public function getPreviewData(Group $group) + { + $groups = $this->groupRepository->findAllRelevantSubGroupsByParentGroupId($group->getId(), ['Group::Abteilung', 'Group::Kantonalverband', 'Group::Region']); // Replace with group endpoint + $return = [ + 'm' => [ + 'leiter' => 0, + 'biber' => 0, + 'woelfe' => 0, + 'pfadis' => 0, + 'rover' => 0, + 'pio' => 0, + 'pta' => 0 + ], + 'f' => [ + 'leiter' => 0, + 'biber' => 0, + 'woelfe' => 0, + 'pfadis' => 0, + 'rover' => 0, + 'pio' => 0, + 'pta' => 0 + ] + ]; + foreach ($groups as $group) { + $censusGroup = $this->censusGroupRepository->findOneBy(['group_id' => $group['id'], 'year' => date('Y')]); + if (!is_null($censusGroup)) { + $return['m']['leiter'] += $censusGroup->getLeiterMCount(); + $return['m']['biber'] += $censusGroup->getBiberMCount(); + $return['m']['woelfe'] += $censusGroup->getWoelfeMCount(); + $return['m']['pfadis'] += $censusGroup->getPfadisMCount(); + $return['m']['rover'] += $censusGroup->getRoverMCount(); + $return['m']['pio'] += $censusGroup->getPiosMCount(); + $return['m']['pta'] += $censusGroup->getPtaMCount(); + + $return['f']['leiter'] += $censusGroup->getLeiterFCount(); + $return['f']['biber'] += $censusGroup->getBiberFCount(); + $return['f']['woelfe'] += $censusGroup->getWoelfeFCount(); + $return['f']['pfadis'] += $censusGroup->getPfadisFCount(); + $return['f']['rover'] += $censusGroup->getRoverFCount(); + $return['f']['pio'] += $censusGroup->getPiosFCount(); + $return['f']['pta'] += $censusGroup->getPtaFCount(); + } + } + return $return; + } + + + /** + * Returns a COPY of the group tree that is flattend. + * For example the structure becomes: + * + * Kanton -> Region -> Abteilung + * + * instead of: + * + * Kanton -> Region -> Region -> Abteilung + * + * @param int[] $groups + * @return StatisticGroup[] + */ + public function flattenGroupTree(array $groupIds) + { + $groups = []; + foreach ($groupIds as $groupId) { + $groups[] = $this->statisticGroupRepository->findOneBy(['id' => $groupId]); + } + $flattenedGroups = []; + foreach ($groups as $group) { + $newGroup = $this->getNewGroupWithRelevantParent($group); + if (!is_null($newGroup)) { + $flattenedGroups[] = $newGroup; + } + } + return $flattenedGroups; + } + + /** + * @param StatisticGroup $baseGroup + * @return StatisticGroup|null + */ + public function getNewGroupWithRelevantParent(StatisticGroup $baseGroup) + { + if ($baseGroup->getGroupType()->getGroupType() === GroupType::DEPARTMENT) { + $clonedGroup = clone $baseGroup; + $relevantParent = $this->findHighestRelevantRegion($clonedGroup); + $clonedGroup->setParentGroup($relevantParent); + return $clonedGroup; + } + if ($baseGroup->getGroupType()->getGroupType() === GroupType::REGION) { + if ($baseGroup->getParentGroup()->getGroupType()->getGroupType() === GroupType::REGION) { + return null; + } + } + if ($baseGroup->getGroupType()->getGroupType() === GroupType::CANTON) { + if ($baseGroup->getParentGroup()->getGroupType()->getGroupType() === GroupType::CANTON) { + return null; + } + } + return $baseGroup; + } + + public function findHighestRelevantRegion(StatisticGroup $group) + { + $parentGroup = $group->getParentGroup(); + if (is_null($parentGroup)) { + return $group; + } + if ($parentGroup->getGroupType()->getGroupType() === GroupType::REGION) { + return $this->findHighestRelevantRegion($parentGroup); + } else { + return $group; + } + } + + /** + * @param TableDTO[] $dtos + * @return void + */ + public function sortDTOs(array $dtos) + { + $regions = array_filter($dtos, function ($dto) { + return $dto->getType() === GroupType::REGION; + }); + $departments = array_filter($dtos, function ($dto) { + return $dto->getType() === GroupType::DEPARTMENT; + }); + usort($regions, function (TableDTO $a, TableDTO $b) { + return strcmp($a->getName(), $b->getName()); + }); + usort($departments, function (TableDTO $a, TableDTO $b) { + return strcmp($a->getName(), $b->getName()); + }); + $return = []; + foreach ($regions as $region) { + $return[] = $region; + foreach ($departments as $department) { + if ($department->getParentId() === $region->getId()) { + $return[] = $department; + } + } + } + + foreach ($departments as $department) { + if (!array_search($department, $return)) { + $return[] = $department; + } + } + return $return; + } + + public function getRelevantGroups(Group $group) + { + $groupIds = array_filter($this->statisticGroupRepository->findAllRelevantChildGroups($group->getId()), function ($id) use ($group) { + // We need to filter because the function also returns the group itself + return !($id === $group->getId()); + }); + return $this->flattenGroupTree($groupIds); + } + + public function getTableData(Group $group, CensusRequestData $censusRequestData) + { + $flattenedGroups = $this->getRelevantGroups($group); + + $dataTransferObjects = []; + $relevantYears = range(date('Y') - 5, date('Y')); + foreach ($flattenedGroups as $flattenedGroup) { + $dataTransferObjects[] = CensusMapper::MapToCensusTable($flattenedGroup, $this->censusGroupRepository->findBy(['group_id' => $flattenedGroup->getId()]), $relevantYears); + } + return [ + 'years' => $relevantYears, + 'data' => $this->sortDTOs($dataTransferObjects), + ]; + } + + public function getDevelopmentData(Group $group, CensusRequestData $censusRequestData) + { + $relevantGroups = $this->getRelevantGroups($group); + + $absolute = []; + $relative = []; + $relevantYears = range(date('Y') - 5, date('Y')); + foreach ($relevantGroups as $relevantGroup) { + $data = $this->censusGroupRepository->findBy(['group_id' => $relevantGroup->getId()]); + if (!sizeof($data) == 0) { + $dto = CensusMapper::MapToLineChart($relevantGroup, $data, $relevantYears); + $absolute[] = $dto->getAbsolute()[0]; + $relative[] = $dto->getRelative()[0]; + } + } + + $return = new DevelopmentWidgetDTO(); + $return->setYears($relevantYears); + $return->setAbsolute($absolute); + $return->setRelative($relative); + return $return; + } + + public function getMembersData(Group $group, CensusRequestData $censusRequestData): array + { + $relevantGroups = $this->getRelevantGroups($group); + + $rawResults = []; + foreach ($relevantGroups as $relevantGroup) { + $data = $this->censusGroupRepository->findBy(['group_id' => $relevantGroup->getId(), 'year' => date('Y')]); + if (!sizeof($data) == 0) { + $biber = $data[0]->getBiberMCount() + $data[0]->getBiberFCount(); + $woelfe = $data[0]->getWoelfeMCount() + $data[0]->getWoelfeFCount(); + $pfadi = $data[0]->getPfadisMCount() + $data[0]->getPfadisFCount(); + $pio = $data[0]->getPiosMCount() + $data[0]->getPiosFCount(); + $rover = $data[0]->getRoverMCount() + $data[0]->getRoverFCount(); + $pta = $data[0]->getPtaMCount() + $data[0]->getPtaFCount(); + $leaders = $data[0]->getLeiterMCount() + $data[0]->getLeiterFCount(); + $rawResults[0][] = new StackedBarElementDTO($biber, $data[0]->getName(), '#EEE09F'); + $rawResults[1][] = new StackedBarElementDTO($woelfe, $data[0]->getName(), '#3BB5DC'); + $rawResults[2][] = new StackedBarElementDTO($pfadi, $data[0]->getName(), '#9A7A54'); + $rawResults[4][] = new StackedBarElementDTO($rover, $data[0]->getName(), '#1DA650'); + $rawResults[3][] = new StackedBarElementDTO($pio, $data[0]->getName(), '#DD1F19'); + $rawResults[5][] = new StackedBarElementDTO($pta, $data[0]->getName(), '#d9b826'); + $rawResults[6][] = new StackedBarElementDTO($leaders, $data[0]->getName(), '#929292'); + } + } + $return = []; + foreach ($rawResults as $rawResult) { + $dto = new MembersWidgetDTO(); + $dto->setData($rawResult); + $return[] = $dto; + } + return $return; + } + + public function getTreemapData(Group $group, CensusRequestData $censusRequestData) + { + $relevantGroups = $this->getRelevantGroups($group); + $return = []; + $colors = ['#EEE09F', '#3BB5DC', '#9A7A54', '#1DA650', '#DD1F19', '#d9b826', '#929292']; + $colorIndex = 0; + $usedColors = []; + foreach ($relevantGroups as $relevantGroup) { + $data = $this->censusGroupRepository->findBy(['group_id' => $relevantGroup->getId(), 'year' => date('Y')]); + if (!sizeof($data) == 0) { + $dto = new TreemapWidgetDTO(); + $dto->setName($relevantGroup->getName()); + $parentName = $relevantGroup->getParentGroup()->getName(); + $dto->setRegion($parentName); + $dto->setValue($data[0]->getCalculatedTotal()); + if (is_null($usedColors[$parentName])) { + $usedColors[$parentName] = $colors[$colorIndex]; + $colorIndex = ($colorIndex + 1) % (sizeof($colors) - 1); + } + $dto->setColor($usedColors[$parentName]); + $return[] = $dto; + } + } + return $return; + } +} diff --git a/src/Service/DataProvider/CensusFilterDataProvider.php b/src/Service/DataProvider/CensusFilterDataProvider.php new file mode 100644 index 0000000..747586c --- /dev/null +++ b/src/Service/DataProvider/CensusFilterDataProvider.php @@ -0,0 +1,44 @@ +groupSettingsRepository = $groupSettingsRepository; + } + + public function getFilterData(Group $group): CensusFilterDTO + { + $groupSettings = $this->groupSettingsRepository->find($group->getId()); + return $this->mapGroupSettingsToCensusFilter($groupSettings); + } + + private function mapGroupSettingsToCensusFilter(GroupSettings $groupSettings): CensusFilterDTO + { + $filterData = new CensusFilterDTO(); + $filterData->setFilterFemales($groupSettings->getCensusFilterFemales() ?? false); + $filterData->setFilterMales($groupSettings->getCensusFilterMales() ?? false); + $filterData->setRoles($groupSettings->getCensusRoles() ?? ['rover']); + $filterData->setGroups($groupSettings->getCensusGroups() ?? []); + return $filterData; + } + + public function setFilterData(Group $group, CensusRequestData $censusRequestData): CensusFilterDTO + { + $groupSettings = $this->groupSettingsRepository->find($group->getId()); + $groupSettings->setCensusGroups($censusRequestData->getGroups()); + $groupSettings->setCensusRoles($censusRequestData->getRoles()); + $groupSettings->setCensusFilterFemales($censusRequestData->isFilterFemales()); + $groupSettings->setCensusFilterMales($censusRequestData->isFilterMales()); + return $this->mapGroupSettingsToCensusFilter($groupSettings); + } +} diff --git a/src/Service/DataProvider/FilterDataProvider.php b/src/Service/DataProvider/FilterDataProvider.php index 6cdbc6f..9c4a72e 100644 --- a/src/Service/DataProvider/FilterDataProvider.php +++ b/src/Service/DataProvider/FilterDataProvider.php @@ -31,6 +31,12 @@ public function __construct( $this->widgetDateRepository = $widgetDateRepository; } + public function getGroupTypes(Group $group, string $locale) + { + $groupTypes = $this->groupTypeRepository->findGroupTypesForParentGroup($group->getId()); + return FilterDataMapper::createGroupTypes($groupTypes, $locale); + } + /*** * @param Group $group * @param string $locale diff --git a/src/Service/DataProvider/WidgetDataProvider.php b/src/Service/DataProvider/WidgetDataProvider.php index ffea5ab..be815da 100644 --- a/src/Service/DataProvider/WidgetDataProvider.php +++ b/src/Service/DataProvider/WidgetDataProvider.php @@ -51,6 +51,16 @@ class WidgetDataProvider 'Group::Pta' ]; + public const CENSUS_ROLES = [ + 'biber', + 'woelfe', + 'pfadis', + 'rover', + 'pio', + 'pta', + 'leiter' + ]; + /** @var string */ public const PEOPLE_TYPE_LEADERS = 'leaders'; /** @var string */