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

Workload report expansion #178

Closed
wants to merge 51 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
dd87423
Split findBillableWorklogs into two, billable and billed worklogs for…
jeppekroghitk Nov 13, 2024
f335ddc
Removed dependence on DataProviderRepository and updated form handlin…
jeppekroghitk Nov 13, 2024
fb34154
Removed DataProvider related fields from WorkloadReport form
jeppekroghitk Nov 13, 2024
e04ad78
Removed DataProvider property from WorkloadReportFormData model
jeppekroghitk Nov 13, 2024
8d0c664
Added new BILLED option to WorkloadReportViewModeEnum
jeppekroghitk Nov 13, 2024
f8cd581
Added handling for BILLED view mode in WorkloadReportService
jeppekroghitk Nov 13, 2024
b5d2703
Added translation for the new BILLED view mode
jeppekroghitk Nov 13, 2024
dae849b
Added new tag entity
jeppekroghitk Nov 14, 2024
5d12b83
Added tag repo
jeppekroghitk Nov 14, 2024
376324c
Added isBillable property to Version entity
jeppekroghitk Nov 14, 2024
b5fcc2b
Modified getWorklogDataCollection method signature
jeppekroghitk Nov 14, 2024
a9c212e
Added properties for periodAverages and totalAverage in WorkloadRepor…
jeppekroghitk Nov 14, 2024
56ff53f
Implement synchronization of tags in DataSynchronizationService
jeppekroghitk Nov 14, 2024
c545967
Changed method for getting paged Worklog data in LeantimeApiService
jeppekroghitk Nov 14, 2024
c5a537c
Added calculations for average in WorkloadReportService
jeppekroghitk Nov 14, 2024
4d9cff0
Updated template to display average in workload report
jeppekroghitk Nov 14, 2024
67dc1cd
Updated Epic entity, added many-to-many association with Issue, remov…
jeppekroghitk Nov 22, 2024
9c35e56
Introduced many-to-many association with Epic in Issue entity
jeppekroghitk Nov 22, 2024
2eaaacd
Introduced new Entity IssueEpic
jeppekroghitk Nov 22, 2024
032d555
Removed isBillable property from Version entity
jeppekroghitk Nov 22, 2024
255c9d0
Minor formatting improvements in NonBillableEpicsEnum
jeppekroghitk Nov 22, 2024
79f5d9a
Introduced new repository EpicRepository with save and remove methods
jeppekroghitk Nov 22, 2024
b086832
Refactored findBillableWorklogsByWorkerAndDateRange, added filter for…
jeppekroghitk Nov 22, 2024
da9eeac
Added example SQL query for fetching billable worklogs
jeppekroghitk Nov 22, 2024
37b1ed4
Refactored data synchronization service, added Epic creation and asso…
jeppekroghitk Nov 22, 2024
5b810cb
Minor formatting improvements in WorkloadReportService
jeppekroghitk Nov 22, 2024
428479c
Deleted tag related files
jeppekroghitk Nov 22, 2024
15d96ad
Created migration
jeppekroghitk Nov 22, 2024
103139e
Improve memory usage output and progress logic
jeppekroghitk Nov 22, 2024
0a859ed
Add new collection property for epics
jeppekroghitk Nov 22, 2024
bcd7823
Add isBillable property to Version entity
jeppekroghitk Nov 22, 2024
59c0101
Create new VersionFilterType form
jeppekroghitk Nov 22, 2024
d512522
Update VersionFilterData properties
jeppekroghitk Nov 22, 2024
7eb3995
Add method to get paginated and filtered versions
jeppekroghitk Nov 22, 2024
91c7332
Add billability check to worklog repository
jeppekroghitk Nov 22, 2024
e32c277
Modify the method for syncing worklogs to process in batches
jeppekroghitk Nov 22, 2024
6164635
Remove unused method from LeantimeApiService
jeppekroghitk Nov 22, 2024
3a4e5d3
Add navigation link to versions in the template
jeppekroghitk Nov 22, 2024
acc3a14
Added version controller
jeppekroghitk Nov 22, 2024
e2684e6
Added version templates
jeppekroghitk Nov 22, 2024
134654f
Minor adjustments
jeppekroghitk Nov 25, 2024
961c412
Added check for nonbillable epics and versions to worklog repo
jeppekroghitk Dec 17, 2024
f44fde4
Removed ui for versions
jeppekroghitk Dec 17, 2024
51c507d
Removed version page from navigation and fixed key value in worklaod …
jeppekroghitk Dec 17, 2024
11cb9aa
Updated changelog
jeppekroghitk Dec 17, 2024
e455a4d
Coding standards
jeppekroghitk Dec 17, 2024
46a0a68
Coding standards
jeppekroghitk Dec 17, 2024
5ab770e
Removed migration
jeppekroghitk Dec 17, 2024
2a9d4b5
Added migration
jeppekroghitk Dec 17, 2024
0c2681a
Added nobillableversionsenum
jeppekroghitk Dec 18, 2024
a01ca64
Removed sql example
jeppekroghitk Jan 2, 2025
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

* [PR-178](https://github.com/itk-dev/economics/pull/178)
2597: Workload report expansion.
* [PR-175](https://github.com/itk-dev/economics/pull/175)
2617: Added forecast report.
* [PR-173](https://github.com/itk-dev/economics/pull/173)
Expand Down
39 changes: 39 additions & 0 deletions migrations/Version20241217124134.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20241217124134 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}

public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE epic (id INT AUTO_INCREMENT NOT NULL, title VARCHAR(255) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE issue_epic (issue_id INT NOT NULL, epic_id INT NOT NULL, INDEX IDX_412E98BD5E7AA58C (issue_id), INDEX IDX_412E98BD6B71E00E (epic_id), PRIMARY KEY(issue_id, epic_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('ALTER TABLE issue_epic ADD CONSTRAINT FK_412E98BD5E7AA58C FOREIGN KEY (issue_id) REFERENCES issue (id) ON DELETE CASCADE');
$this->addSql('ALTER TABLE issue_epic ADD CONSTRAINT FK_412E98BD6B71E00E FOREIGN KEY (epic_id) REFERENCES epic (id) ON DELETE CASCADE');
$this->addSql('ALTER TABLE version ADD is_billable TINYINT(1) NOT NULL');
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE issue_epic DROP FOREIGN KEY FK_412E98BD5E7AA58C');
$this->addSql('ALTER TABLE issue_epic DROP FOREIGN KEY FK_412E98BD6B71E00E');
$this->addSql('DROP TABLE epic');
$this->addSql('DROP TABLE issue_epic');
$this->addSql('ALTER TABLE version DROP is_billable');
}
}
10 changes: 7 additions & 3 deletions src/Command/SyncWorklogsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$this->dataSynchronizationService->syncWorklogsForProject($project->getId(), function ($i, $length) use ($io) {
if (0 == $i) {
$io->progressStart($length);
} elseif ($i >= $length - 1) {
$io->progressFinish();
} else {
}

if ($i < $length) {
$io->progressAdvance();
}

if ($i == $length - 1) {
$io->progressFinish();
}
}, $dataProvider);

$io->writeln('');
Expand Down
25 changes: 7 additions & 18 deletions src/Controller/WorkloadReportController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
use App\Model\Reports\WorkloadReportFormData;
use App\Model\Reports\WorkloadReportPeriodTypeEnum as PeriodTypeEnum;
use App\Model\Reports\WorkloadReportViewModeEnum as ViewModeEnum;
use App\Repository\DataProviderRepository;
use App\Service\WorkloadReportService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
Expand All @@ -19,7 +18,6 @@
class WorkloadReportController extends AbstractController
{
public function __construct(
private readonly DataProviderRepository $dataProviderRepository,
private readonly WorkloadReportService $workloadReportService,
) {
}
Expand All @@ -43,23 +41,14 @@ public function index(Request $request): Response

$form->handleRequest($request);

$requestData = $request->query->all('workload_report');
if ($form->isSubmitted() && $form->isValid()) {
$viewPeriodType = $form->get('viewPeriodType')->getData() ?? PeriodTypeEnum::WEEK;
$viewMode = $form->get('viewMode')->getData() ?? ViewModeEnum::WORKLOAD;

if (!empty($requestData['dataProvider'])) {
$dataProvider = $this->dataProviderRepository->find($requestData['dataProvider']);

if ($form->isSubmitted() && $form->isValid()) {
$selectedDataProvider = $form->get('dataProvider')->getData() ?? $dataProvider;
$viewPeriodType = $form->get('viewPeriodType')->getData() ?? PeriodTypeEnum::WEEK;
$viewMode = $form->get('viewMode')->getData() ?? ViewModeEnum::WORKLOAD;

if ($selectedDataProvider) {
try {
$reportData = $this->workloadReportService->getWorkloadReport($viewPeriodType, $viewMode);
} catch (\Exception $e) {
$error = $e->getMessage();
}
}
try {
$reportData = $this->workloadReportService->getWorkloadReport($viewPeriodType, $viewMode);
} catch (\Exception $e) {
$error = $e->getMessage();
}
}

Expand Down
68 changes: 68 additions & 0 deletions src/Entity/Epic.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace App\Entity;

use App\Repository\EpicRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: EpicRepository::class)]
class Epic
{
public function __construct()
{
$this->issues = new ArrayCollection();
}
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;

#[ORM\Column(length: 255)]
private ?string $title = null;

#[ORM\ManyToMany(targetEntity: Issue::class, mappedBy: 'epics')]
private Collection $issues;

public function getId(): ?int
{
return $this->id;
}

public function getTitle(): ?string
{
return $this->title;
}

public function setTitle(string $title): static
{
$this->title = $title;

return $this;
}

/**
* @return Collection<int, Epic>
*/
public function getIssues(): Collection
{
return $this->issues;
}

public function addIssue(Issue $issue): self
{
if (!$this->issues->contains($issue)) {
$this->issues->add($issue);
}

return $this;
}

public function removeIssue(Issue $issues): self
{
$this->issues->removeElement($issues);

return $this;
}
}
27 changes: 27 additions & 0 deletions src/Entity/Issue.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class Issue extends AbstractBaseEntity
#[ORM\Column(length: 255, nullable: true)]
private ?string $epicName = null;

#[ORM\ManyToMany(targetEntity: Epic::class, inversedBy: 'issues')]
private Collection $epics;
#[ORM\ManyToMany(targetEntity: Version::class, inversedBy: 'issues')]
private Collection $versions;

Expand Down Expand Up @@ -79,6 +81,7 @@ public function __construct()
$this->versions = new ArrayCollection();
$this->worklogs = new ArrayCollection();
$this->products = new ArrayCollection();
$this->epics = new ArrayCollection();
}

public function getName(): ?string
Expand Down Expand Up @@ -201,6 +204,30 @@ public function removeVersion(Version $version): self
return $this;
}

/**
* @return Collection<int, Epic>
*/
public function getEpics(): Collection
{
return $this->epics;
}

public function addEpic(Epic $epic): self
{
if (!$this->epics->contains($epic)) {
$this->epics->add($epic);
}

return $this;
}

public function removeEpic(Epic $epic): self
{
$this->epics->removeElement($epic);

return $this;
}

/**
* @return Collection<int, Worklog>
*/
Expand Down
17 changes: 17 additions & 0 deletions src/Entity/IssueEpic.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

class IssueEpic extends AbstractBaseEntity
{
#[ORM\Column(length: 255)]
private ?string $name = null;

#[ORM\Column(length: 255)]
private ?string $value = null;

#[ORM\Column(length: 255, nullable: true)]
private ?string $projectTrackerId = null;
}
15 changes: 15 additions & 0 deletions src/Entity/Version.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ class Version extends AbstractBaseEntity
#[ORM\ManyToMany(targetEntity: Issue::class, mappedBy: 'versions')]
private Collection $issues;

#[ORM\Column]
private ?bool $isBillable = true;

public function __construct()
{
$this->issues = new ArrayCollection();
Expand Down Expand Up @@ -101,4 +104,16 @@ public function removeIssue(Issue $issue): self

return $this;
}

public function isBillable(): ?bool
{
return $this->isBillable;
}

public function setIsBillable(bool $isBillable): static
{
$this->isBillable = $isBillable;

return $this;
}
}
24 changes: 24 additions & 0 deletions src/Enum/NonBillableEpicsEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace App\Enum;

/*
* Kind is a term on a worklog in Leantime:
* https://github.com/Leantime/leantime/blob/80c4542e19692e423820bd9030907070d281571e/app/Domain/Timesheets/Services/Timesheets.php#L22
* */
enum NonBillableEpicsEnum: string
{
case UB = 'UB';

/**
* @return array<string,string>
*/
public static function getAsArray(): array
{
return array_reduce(
self::cases(),
static fn (array $choices, NonBillableEpicsEnum $type) => $choices + [$type->name => $type->value],
[],
);
}
}
20 changes: 20 additions & 0 deletions src/Enum/NonBillableVersionsEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace App\Enum;

enum NonBillableVersionsEnum: string
{
case UB = 'UB';

/**
* @return array<string,string>
*/
public static function getAsArray(): array
{
return array_reduce(
self::cases(),
static fn (array $choices, NonBillableVersionsEnum $type) => $choices + [$type->name => $type->value],
[],
);
}
}
23 changes: 0 additions & 23 deletions src/Form/WorkloadReportType.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@

namespace App\Form;

use App\Entity\DataProvider;
use App\Model\Reports\WorkloadReportFormData;
use App\Model\Reports\WorkloadReportPeriodTypeEnum as PeriodTypeEnum;
use App\Model\Reports\WorkloadReportViewModeEnum;
use App\Repository\DataProviderRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EnumType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
Expand All @@ -17,32 +14,12 @@
class WorkloadReportType extends AbstractType
{
public function __construct(
private readonly DataProviderRepository $dataProviderRepository,
private readonly ?string $defaultDataProvider,
) {
}

public function buildForm(FormBuilderInterface $builder, array $options): void
{
$dataProviders = $this->dataProviderRepository->findAll();
$defaultProvider = $this->dataProviderRepository->find($this->defaultDataProvider);

if (null === $defaultProvider && count($dataProviders) > 0) {
$defaultProvider = $dataProviders[0];
}

$builder
->add('dataProvider', EntityType::class, [
'class' => DataProvider::class,
'required' => false,
'label' => 'workload_report.select_data_provider',
'label_attr' => ['class' => 'label'],
'attr' => [
'class' => 'form-element',
],
'data' => $defaultProvider,
'choices' => $dataProviders,
])
->add('viewMode', EnumType::class, [
'required' => false,
'label' => 'workload_report.select_viewmode',
Expand Down
3 changes: 1 addition & 2 deletions src/Interface/DataProviderServiceInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
use App\Model\Invoices\ClientData;
use App\Model\Invoices\PagedResult;
use App\Model\Invoices\ProjectDataCollection;
use App\Model\Invoices\WorklogDataCollection;
use App\Model\Planning\PlanningData;
use App\Model\SprintReport\SprintReportData;
use App\Model\SprintReport\SprintReportProjects;
Expand Down Expand Up @@ -36,5 +35,5 @@ public function getSprintReportVersions(string $projectId): SprintReportVersions

public function getProjectDataCollection(): ProjectDataCollection;

public function getWorklogDataCollection(string $projectId): WorklogDataCollection;
public function getWorklogDataForProjectPaged(string $projectId, int $startAt = 0, $maxResults = 50): PagedResult;
}
Loading
Loading