Skip to content

Commit 43a8e04

Browse files
ISSUE #1669: Gear maintenance - every x days used
1 parent 9385fa0 commit 43a8e04

File tree

4 files changed

+170
-0
lines changed

4 files changed

+170
-0
lines changed

docs/configuration/gear-maintenance.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ components:
8989
# - km (every x km used),
9090
# - mi (every x miles used),
9191
# - hours (every x hours used),
92+
# - daysUsed (every x days used),
9293
# - days (every x days),
9394
unit: km
9495
# If you don't want to reference images, set gears to an empty array: `gears: []`

src/Domain/Gear/Maintenance/Task/IntervalUnit.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ enum IntervalUnit: string
1010
case EVERY_X_MILES_USED = 'mi';
1111
case EVERY_X_HOURS_USED = 'hours';
1212
case EVERY_X_DAYS = 'days';
13+
case EVERY_X_DAYS_USED = 'daysUsed';
1314
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Domain\Gear\Maintenance\Task\Progress;
6+
7+
use App\Domain\Gear\Maintenance\GearMaintenanceConfig;
8+
use App\Domain\Gear\Maintenance\GearMaintenanceCountersResetMode;
9+
use App\Domain\Gear\Maintenance\Task\IntervalUnit;
10+
use Doctrine\DBAL\ArrayParameterType;
11+
use Doctrine\DBAL\Connection;
12+
use Symfony\Contracts\Translation\TranslatorInterface;
13+
14+
final readonly class EveryXDaysUsedProgressCalculation implements MaintenanceTaskProgressCalculation
15+
{
16+
public function __construct(
17+
private Connection $connection,
18+
private GearMaintenanceConfig $config,
19+
private TranslatorInterface $translator,
20+
) {
21+
}
22+
23+
public function supports(IntervalUnit $intervalUnit): bool
24+
{
25+
return IntervalUnit::EVERY_X_DAYS_USED === $intervalUnit;
26+
}
27+
28+
public function calculate(ProgressCalculationContext $context): MaintenanceTaskProgress
29+
{
30+
$operator = GearMaintenanceCountersResetMode::NEXT_ACTIVITY_ONWARDS === $this->config->getResetMode() ? '>' : '>=';
31+
$query = '
32+
SELECT strftime("%Y-%m-%d", startDateTime) AS day
33+
FROM Activity
34+
WHERE gearId IN(:gearIds)
35+
AND startDateTime '.$operator.' (
36+
SELECT startDateTime
37+
FROM Activity
38+
WHERE activityId = :activityId
39+
)
40+
GROUP BY day';
41+
42+
$daysUsedSinceLastTagged = count($this->connection->fetchFirstColumn($query, [
43+
'gearIds' => $context->getGearIds()->toArray(),
44+
'activityId' => $context->getLastTaggedOnActivityId(),
45+
], [
46+
'gearIds' => ArrayParameterType::STRING,
47+
]));
48+
49+
return MaintenanceTaskProgress::from(
50+
percentage: min((int) round(($daysUsedSinceLastTagged / $context->getIntervalValue()) * 100), 100),
51+
description: $this->translator->trans('{daysSinceLastTagged} days', [
52+
'{daysSinceLastTagged}' => $daysUsedSinceLastTagged,
53+
]),
54+
);
55+
}
56+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<?php
2+
3+
namespace App\Tests\Domain\Gear\Maintenance\Task\Progress;
4+
5+
use App\Domain\Activity\ActivityId;
6+
use App\Domain\Activity\ActivityWithRawData;
7+
use App\Domain\Activity\ActivityWithRawDataRepository;
8+
use App\Domain\Gear\GearId;
9+
use App\Domain\Gear\GearIds;
10+
use App\Domain\Gear\Maintenance\GearMaintenanceConfig;
11+
use App\Domain\Gear\Maintenance\Task\IntervalUnit;
12+
use App\Domain\Gear\Maintenance\Task\Progress\EveryXDaysUsedProgressCalculation;
13+
use App\Domain\Gear\Maintenance\Task\Progress\MaintenanceTaskProgress;
14+
use App\Domain\Gear\Maintenance\Task\Progress\ProgressCalculationContext;
15+
use App\Infrastructure\ValueObject\Time\SerializableDateTime;
16+
use App\Tests\ContainerTestCase;
17+
use App\Tests\Domain\Activity\ActivityBuilder;
18+
use Doctrine\DBAL\Connection;
19+
use Symfony\Contracts\Translation\TranslatorInterface;
20+
21+
class EveryXDaysUsedProgressCalculationTest extends ContainerTestCase
22+
{
23+
private EveryXDaysUsedProgressCalculation $calculation;
24+
25+
public function testCalculate(): void
26+
{
27+
$this->getContainer()->get(ActivityWithRawDataRepository::class)->add(ActivityWithRawData::fromState(
28+
ActivityBuilder::fromDefaults()
29+
->withActivityId(ActivityId::fromUnprefixed('last-tagged'))
30+
->withStartDateTime(SerializableDateTime::fromString('2025-01-01 00:00:00'))
31+
->build(),
32+
[]
33+
));
34+
35+
$this->getContainer()->get(ActivityWithRawDataRepository::class)->add(ActivityWithRawData::fromState(
36+
ActivityBuilder::fromDefaults()
37+
->withActivityId(ActivityId::fromUnprefixed('include'))
38+
->withGearId(GearId::fromUnprefixed('test'))
39+
->withStartDateTime(SerializableDateTime::fromString('2025-01-01 01:00:00'))
40+
->withMovingTimeInSeconds(3600)
41+
->build(),
42+
[]
43+
));
44+
45+
$this->getContainer()->get(ActivityWithRawDataRepository::class)->add(ActivityWithRawData::fromState(
46+
ActivityBuilder::fromDefaults()
47+
->withActivityId(ActivityId::fromUnprefixed('include-2'))
48+
->withGearId(GearId::fromUnprefixed('test'))
49+
->withStartDateTime(SerializableDateTime::fromString('2025-01-01 02:00:00'))
50+
->withMovingTimeInSeconds(3600)
51+
->build(),
52+
[]
53+
));
54+
55+
$this->getContainer()->get(ActivityWithRawDataRepository::class)->add(ActivityWithRawData::fromState(
56+
ActivityBuilder::fromDefaults()
57+
->withActivityId(ActivityId::fromUnprefixed('include-3'))
58+
->withGearId(GearId::fromUnprefixed('test'))
59+
->withStartDateTime(SerializableDateTime::fromString('2025-01-02 02:00:00'))
60+
->withMovingTimeInSeconds(3600)
61+
->build(),
62+
[]
63+
));
64+
65+
$this->getContainer()->get(ActivityWithRawDataRepository::class)->add(ActivityWithRawData::fromState(
66+
ActivityBuilder::fromDefaults()
67+
->withActivityId(ActivityId::fromUnprefixed('1'))
68+
->withGearId(GearId::random())
69+
->withMovingTimeInSeconds(3600)
70+
->build(),
71+
[]
72+
));
73+
74+
$this->getContainer()->get(ActivityWithRawDataRepository::class)->add(ActivityWithRawData::fromState(
75+
ActivityBuilder::fromDefaults()
76+
->withActivityId(ActivityId::fromUnprefixed('2'))
77+
->withGearId(GearId::fromUnprefixed('test'))
78+
->withStartDateTime(SerializableDateTime::fromString('2024-01-01 00:00:00'))
79+
->withMovingTimeInSeconds(3600)
80+
->build(),
81+
[]
82+
));
83+
84+
$this->assertEquals(
85+
MaintenanceTaskProgress::from(
86+
20,
87+
'2 days',
88+
),
89+
$this->calculation->calculate(
90+
ProgressCalculationContext::from(
91+
gearIds: GearIds::fromArray([GearId::fromUnprefixed('test')]),
92+
lastTaggedOnActivityId: ActivityId::fromUnprefixed('last-tagged'),
93+
lastTaggedOn: SerializableDateTime::fromString('01-01-2025'),
94+
intervalUnit: IntervalUnit::EVERY_X_HOURS_USED,
95+
intervalValue: 10,
96+
)
97+
)
98+
);
99+
}
100+
101+
#[\Override]
102+
protected function setUp(): void
103+
{
104+
parent::setUp();
105+
106+
$this->calculation = new EveryXDaysUsedProgressCalculation(
107+
$this->getContainer()->get(Connection::class),
108+
$this->getContainer()->get(GearMaintenanceConfig::class),
109+
$this->getContainer()->get(TranslatorInterface::class),
110+
);
111+
}
112+
}

0 commit comments

Comments
 (0)