Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Abteilungen im Vergleich #102

Merged
merged 27 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1bc30d5
partial implementation without backend filters
SebastianStorz Sep 1, 2023
5011724
enable reflections for census
SebastianStorz Sep 7, 2023
dfedf01
fix: wrong parameter order
SebastianStorz Sep 19, 2023
7932db5
fix: move groupsetting creation to end of group creation
SebastianStorz Sep 19, 2023
db9fdba
Merge pull request #94 from digio-ch/bugfix
c0depwn Sep 25, 2023
6e54d13
WIP: All widgets without filter
SebastianStorz Sep 26, 2023
67eadb5
Merge branch 'stage' into issue/hc-235
SebastianStorz Sep 26, 2023
42f6c00
lint
SebastianStorz Sep 26, 2023
73d96d7
Merge pull request #95 from digio-ch/issue/hc-235
SebastianStorz Sep 26, 2023
0d6733e
lint
SebastianStorz Sep 26, 2023
d764bcf
Merge pull request #96 from digio-ch/issue/hc-235
SebastianStorz Sep 26, 2023
e5f1f19
Add filter
SebastianStorz Sep 27, 2023
d5af65a
lint
SebastianStorz Sep 27, 2023
455ea41
Merge branch 'stage' into issue/hc-235
SebastianStorz Sep 27, 2023
50aeeb4
Merge pull request #97 from digio-ch/issue/hc-235
SebastianStorz Sep 27, 2023
d3c3d5b
fix query, fix colors
SebastianStorz Sep 29, 2023
1f9329d
Merge pull request #98 from digio-ch/issue/hc-235
SebastianStorz Sep 29, 2023
909379b
lint
SebastianStorz Sep 29, 2023
8d96886
Merge pull request #99 from digio-ch/issue/hc-235
SebastianStorz Sep 29, 2023
c0dd1a9
various fixes
SebastianStorz Oct 6, 2023
7c507b8
lint
SebastianStorz Oct 6, 2023
92a93e8
Merge pull request #100 from digio-ch/issue/hc-235
SebastianStorz Oct 6, 2023
f4db67f
fix: reduce census API computation duration by ~93%
SebastianStorz Oct 27, 2023
b5ceb1b
fix: prevent 0 division and null errors
SebastianStorz Oct 27, 2023
8de0204
fix: use proper entities for preview
SebastianStorz Oct 27, 2023
445e429
add crontab and finish census command
SebastianStorz Oct 27, 2023
732e11d
Merge pull request #101 from digio-ch/issue/hc-235
SebastianStorz Oct 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions config/routes/app_routes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,8 @@ general:
resource: "apps/general.yml"
prefix: /general
name_prefix: general_

census:
resource: "apps/census.yml"
prefix: /census
name_prefix: census_
14 changes: 14 additions & 0 deletions config/routes/apps/census.yml
Original file line number Diff line number Diff line change
@@ -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
21 changes: 21 additions & 0 deletions config/routes/apps/widgets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

3 changes: 3 additions & 0 deletions crontab
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@

# Run every 3 months at 00:00 (AM)
0 0 1 */3 * /usr/local/bin/php /srv/bin/console app:import-geo-addresses

# Run once a year on the first of february at 01:00 (AM)
0 1 1 2 * /usr/local/bin/php /srv/bin/console app:fetch-census
89 changes: 83 additions & 6 deletions src/Command/FetchCensusCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,105 @@

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;
private $start;

public function __construct(
CensusAPIService $apiService,
CensusGroupRepository $censusGroupRepository,
GroupTypeRepository $groupTypeRepository
) {
$this->apiService = $apiService;
$this->censusGroupRepository = $censusGroupRepository;
$this->groupTypeRepository = $groupTypeRepository;
parent::__construct();
}


public function configure()
{
$this->setName('app:fetch-census')
->setDescription('Not implemented');
->setDescription('Fetch and aggregate census data');
}

public function execute(InputInterface $input, OutputInterface $output)
{
$this->start = microtime(true);
$this->io = new SymfonyStyle($input, $output);

$year = (int) date('Y');
$minYear = $year - 6;
// 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)) {
$this->mapRawCensusGroupToCensusGroup($rawCensusGroup, $year);
}
}
$year--;
}
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);
}

// TODO: Implement the statistics
private function sanitizeValue($raw): int
{
return is_null($raw) ? 0 : $raw;
}


public function getStats(): CommandStatistics
{
return new CommandStatistics(0, 'Statistics not yet implemented.');
return new CommandStatistics(microtime(true) - $this->start, '');
}
}
19 changes: 19 additions & 0 deletions src/Command/ImportFromJsonCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -386,12 +386,17 @@ private function importGroups(OutputInterface $output)
$i = 0;
foreach ($groups as $gr) {
$group = $this->em->getRepository(Group::class)->findOneBy(['id' => $gr['id']]);
$createGroupSettings = false;
if (!$group) {
$group = new Group();
$group->setId($gr['id']);
$metadata = $this->em->getClassMetaData(get_class($group));
$metadata->setIdGenerator(new AssignedGenerator());

/** @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);
Expand All @@ -417,6 +422,20 @@ private function importGroups(OutputInterface $output)
$gt = $this->em->getRepository(GroupType::class)->findOneBy(['groupType' => $gr['type']]);
$group->setGroupType($gt);

if ($createGroupSettings) {
// 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);
}

if ($gr['parent_id'] !== null) {
$pg = $this->em->getRepository(Group::class)->find($gr['parent_id']);
$group->setParentGroup($pg);
Expand Down
116 changes: 116 additions & 0 deletions src/Controller/Api/Apps/CensusController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php

namespace App\Controller\Api\Apps;

use App\DTO\Model\FilterRequestData\CensusRequestData;
use App\Entity\Midata\Group;
use App\Service\DataProvider\CensusDataProvider;
use App\Service\DataProvider\CensusFilterDataProvider;
use App\Service\Security\PermissionVoter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\HttpFoundation\JsonResponse;

class CensusController extends AbstractController
{
private CensusDataProvider $censusDataProvider;
private CensusFilterDataProvider $censusFilterDataProvider;

public function __construct(
CensusDataProvider $censusDataProvider,
CensusFilterDataProvider $censusFilterDataProvider
) {
$this->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);
$data = $this->censusDataProvider->getTableData($group, $censusRequestData);
return $this->json($data);
}

/**
* @param Group $group
* @return JsonResponse
*
* @ParamConverter("group", options={"mapping": {"groupId": "id"}})
*/
public function getDevelopmentData(Group $group, CensusRequestData $censusRequestData)
{
$this->denyAccessUnlessGranted(PermissionVoter::VIEWER, $group);
$data = $this->censusDataProvider->getDevelopmentData($group, $censusRequestData);
return $this->json($data);
}


/**
* @param Group $group
* @return JsonResponse
*
* @ParamConverter("group", options={"mapping": {"groupId": "id"}})
*/
public function getMembersData(Group $group, CensusRequestData $censusRequestData)
{
$this->denyAccessUnlessGranted(PermissionVoter::VIEWER, $group);
$data = $this->censusDataProvider->getMembersData($group, $censusRequestData);
return $this->json($data);
}

/**
* @param Group $group
* @return JsonResponse
*
* @ParamConverter("group", options={"mapping": {"groupId": "id"}})
*/
public function getTreemapData(Group $group, CensusRequestData $censusRequestData)
{
$this->denyAccessUnlessGranted(PermissionVoter::VIEWER, $group);
$data = $this->censusDataProvider->getTreemapData($group, $censusRequestData);
return $this->json($data);
}

/**
* @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));
}

/**
* @param Group $group
* @param CensusRequestData $censusRequestData
* @return JsonResponse
*
* @ParamConverter("group", options={"mapping": {"groupId": "id"}})
*/
public function postFilterData(Group $group, CensusRequestData $censusRequestData)
{
$this->denyAccessUnlessGranted(PermissionVoter::VIEWER, $group);
return $this->json($this->censusFilterDataProvider->setFilterData($group, $censusRequestData));
}
}
18 changes: 18 additions & 0 deletions src/Controller/Api/Apps/Widgets/FilterController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
13 changes: 7 additions & 6 deletions src/Controller/Api/InviteController.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
Expand Down Expand Up @@ -63,27 +64,27 @@ public function createInvite(
]);
} catch (\Exception $exception) {
throw new ApiException(
$this->translator->trans('api.error.invalidRequest'),
JsonResponse::HTTP_NOT_ACCEPTABLE
Response::HTTP_NOT_ACCEPTABLE,
$this->translator->trans('api.error.invalidRequest')
);
}

$errors = $validator->validate($inviteDTO);
if (count($errors) > 0) {
throw new ApiException(
$this->translator->trans('api.error.invalidEntries'),
JsonResponse::HTTP_UNPROCESSABLE_ENTITY
Response::HTTP_UNPROCESSABLE_ENTITY,
$this->translator->trans('api.error.invalidEntries')
);
}

if ($inviteDTO->getPermissionType() === PermissionVoter::OWNER) {
throw new ApiException('You may not add group Owners.', JsonResponse::HTTP_FORBIDDEN);
throw new ApiException(Response::HTTP_FORBIDDEN, 'You may not add group Owners.');
}

if ($this->inviteService->inviteExists($group, $inviteDTO->getEmail())) {
$invite = $this->translator->trans('api.entity.invite');
$message = $this->translator->trans('api.error.exists', ['entityName' => $invite]);
throw new ApiException($message, JsonResponse::HTTP_UNPROCESSABLE_ENTITY);
throw new ApiException(Response::HTTP_UNPROCESSABLE_ENTITY, $message);
}

$createdInviteDTO = $this->inviteService->createInvite($group, $inviteDTO);
Expand Down
Loading
Loading