add sparse workload placement
This commit is contained in:
parent
ff8efd7f4a
commit
90dcc8665a
3 changed files with 55 additions and 16 deletions
|
|
@ -18,7 +18,7 @@ class EvenDistributionScheduler
|
||||||
$differenceInDays = $startDate->diff($targetDate)->days;
|
$differenceInDays = $startDate->diff($targetDate)->days;
|
||||||
$dayCount = $differenceInDays + 1;
|
$dayCount = $differenceInDays + 1;
|
||||||
|
|
||||||
if ($assignmentCount < $dayCount) {
|
if ($workloadPlacement === WorkloadPlacement::Spread) {
|
||||||
return $this->sparseDates(
|
return $this->sparseDates(
|
||||||
assignmentCount: $assignmentCount,
|
assignmentCount: $assignmentCount,
|
||||||
dayCount: $dayCount,
|
dayCount: $dayCount,
|
||||||
|
|
@ -26,7 +26,7 @@ class EvenDistributionScheduler
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->dailyDates(
|
return $this->groupedDates(
|
||||||
assignmentCount: $assignmentCount,
|
assignmentCount: $assignmentCount,
|
||||||
dayCount: $dayCount,
|
dayCount: $dayCount,
|
||||||
startDate: $startDate,
|
startDate: $startDate,
|
||||||
|
|
@ -63,17 +63,17 @@ class EvenDistributionScheduler
|
||||||
/**
|
/**
|
||||||
* @return list<DateTimeImmutable>
|
* @return list<DateTimeImmutable>
|
||||||
*/
|
*/
|
||||||
private function dailyDates(
|
private function groupedDates(
|
||||||
int $assignmentCount,
|
int $assignmentCount,
|
||||||
int $dayCount,
|
int $dayCount,
|
||||||
DateTimeImmutable $startDate,
|
DateTimeImmutable $startDate,
|
||||||
WorkloadPlacement $workloadPlacement,
|
WorkloadPlacement $workloadPlacement,
|
||||||
): array {
|
): array {
|
||||||
$assignmentsPerDay = intdiv($assignmentCount, $dayCount);
|
$assignmentsPerDay = intdiv($assignmentCount, $dayCount);
|
||||||
$heavierDayCount = $assignmentCount % $dayCount;
|
$remainderDayCount = $assignmentCount % $dayCount;
|
||||||
$heavierBlockStart = $this->heavierBlockStart(
|
$remainderBlockStart = $this->remainderBlockStart(
|
||||||
dayCount: $dayCount,
|
dayCount: $dayCount,
|
||||||
heavierDayCount: $heavierDayCount,
|
remainderDayCount: $remainderDayCount,
|
||||||
workloadPlacement: $workloadPlacement,
|
workloadPlacement: $workloadPlacement,
|
||||||
);
|
);
|
||||||
$dates = [];
|
$dates = [];
|
||||||
|
|
@ -81,8 +81,8 @@ class EvenDistributionScheduler
|
||||||
for ($dayIndex = 0; $dayIndex < $dayCount; $dayIndex++) {
|
for ($dayIndex = 0; $dayIndex < $dayCount; $dayIndex++) {
|
||||||
$assignmentCountForDay = $assignmentsPerDay;
|
$assignmentCountForDay = $assignmentsPerDay;
|
||||||
if (
|
if (
|
||||||
$dayIndex >= $heavierBlockStart
|
$dayIndex >= $remainderBlockStart
|
||||||
&& $dayIndex < $heavierBlockStart + $heavierDayCount
|
&& $dayIndex < $remainderBlockStart + $remainderDayCount
|
||||||
) {
|
) {
|
||||||
$assignmentCountForDay++;
|
$assignmentCountForDay++;
|
||||||
}
|
}
|
||||||
|
|
@ -96,9 +96,9 @@ class EvenDistributionScheduler
|
||||||
return $dates;
|
return $dates;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function heavierBlockStart(
|
private function remainderBlockStart(
|
||||||
int $dayCount,
|
int $dayCount,
|
||||||
int $heavierDayCount,
|
int $remainderDayCount,
|
||||||
WorkloadPlacement $workloadPlacement,
|
WorkloadPlacement $workloadPlacement,
|
||||||
): int {
|
): int {
|
||||||
if ($workloadPlacement === WorkloadPlacement::Start) {
|
if ($workloadPlacement === WorkloadPlacement::Start) {
|
||||||
|
|
@ -106,9 +106,9 @@ class EvenDistributionScheduler
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($workloadPlacement === WorkloadPlacement::End) {
|
if ($workloadPlacement === WorkloadPlacement::End) {
|
||||||
return $dayCount - $heavierDayCount;
|
return $dayCount - $remainderDayCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
return intdiv($dayCount - $heavierDayCount, 2);
|
return intdiv($dayCount - $remainderDayCount, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ class CreateSchedule
|
||||||
'targetDate must not be before startDate',
|
'targetDate must not be before startDate',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$workloadPlacement = $this->workloadPlacement(
|
$requestedWorkloadPlacement = $this->workloadPlacement(
|
||||||
$request->workloadPlacement,
|
$request->workloadPlacement,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -78,6 +78,13 @@ class CreateSchedule
|
||||||
throw new BadRequestException('level has no elements');
|
throw new BadRequestException('level has no elements');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$workloadPlacement = $this->resolvedWorkloadPlacement(
|
||||||
|
requestedWorkloadPlacement: $requestedWorkloadPlacement,
|
||||||
|
assignmentCount: count($elements),
|
||||||
|
startDate: $startDate,
|
||||||
|
targetDate: $targetDate,
|
||||||
|
);
|
||||||
|
|
||||||
$scheduledDates = $this->evenDistributionScheduler->scheduledDates(
|
$scheduledDates = $this->evenDistributionScheduler->scheduledDates(
|
||||||
assignmentCount: count($elements),
|
assignmentCount: count($elements),
|
||||||
startDate: $startDate,
|
startDate: $startDate,
|
||||||
|
|
@ -225,19 +232,50 @@ class CreateSchedule
|
||||||
/**
|
/**
|
||||||
* @throws BadRequestException
|
* @throws BadRequestException
|
||||||
*/
|
*/
|
||||||
private function workloadPlacement(?string $value): WorkloadPlacement
|
private function workloadPlacement(?string $value): ?WorkloadPlacement
|
||||||
{
|
{
|
||||||
if ($value === null) {
|
if ($value === null) {
|
||||||
return WorkloadPlacement::Middle;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$workloadPlacement = WorkloadPlacement::tryFrom($value);
|
$workloadPlacement = WorkloadPlacement::tryFrom($value);
|
||||||
if ($workloadPlacement === null) {
|
if ($workloadPlacement === null) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
'workloadPlacement must be start, middle, or end',
|
'workloadPlacement must be spread, start, middle, or end',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $workloadPlacement;
|
return $workloadPlacement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws BadRequestException
|
||||||
|
*/
|
||||||
|
private function resolvedWorkloadPlacement(
|
||||||
|
?WorkloadPlacement $requestedWorkloadPlacement,
|
||||||
|
int $assignmentCount,
|
||||||
|
DateTimeImmutable $startDate,
|
||||||
|
DateTimeImmutable $targetDate,
|
||||||
|
): WorkloadPlacement {
|
||||||
|
$differenceInDays = $startDate->diff($targetDate)->days;
|
||||||
|
$dayCount = $differenceInDays + 1;
|
||||||
|
$isSparse = $assignmentCount < $dayCount;
|
||||||
|
|
||||||
|
if ($requestedWorkloadPlacement === null) {
|
||||||
|
return $isSparse
|
||||||
|
? WorkloadPlacement::Spread
|
||||||
|
: WorkloadPlacement::Middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
$requestedWorkloadPlacement === WorkloadPlacement::Spread
|
||||||
|
&& ! $isSparse
|
||||||
|
) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
'spread placement requires fewer assignments than days',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $requestedWorkloadPlacement;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ namespace App\Schedule;
|
||||||
|
|
||||||
enum WorkloadPlacement: string
|
enum WorkloadPlacement: string
|
||||||
{
|
{
|
||||||
|
case Spread = 'spread';
|
||||||
case Start = 'start';
|
case Start = 'start';
|
||||||
case Middle = 'middle';
|
case Middle = 'middle';
|
||||||
case End = 'end';
|
case End = 'end';
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue