spread heavy workload days

This commit is contained in:
Yisroel Baum 2026-08-20 08:21:15 +03:00
parent 85e2c33c31
commit 6dc2ccbb55
Signed by: yisroelbaum
GPG key ID: 0FA60884F75520A9
2 changed files with 65 additions and 53 deletions

View file

@ -18,15 +18,7 @@ class EvenDistributionScheduler
$differenceInDays = $startDate->diff($targetDate)->days;
$dayCount = $differenceInDays + 1;
if ($workloadPlacement === WorkloadPlacement::Spread) {
return $this->sparseDates(
assignmentCount: $assignmentCount,
dayCount: $dayCount,
startDate: $startDate,
);
}
return $this->groupedDates(
return $this->distributedDates(
assignmentCount: $assignmentCount,
dayCount: $dayCount,
startDate: $startDate,
@ -37,33 +29,7 @@ class EvenDistributionScheduler
/**
* @return list<DateTimeImmutable>
*/
private function sparseDates(
int $assignmentCount,
int $dayCount,
DateTimeImmutable $startDate,
): array {
if ($assignmentCount === 1) {
return [$startDate];
}
$dates = [];
for ($assignmentIndex = 0;
$assignmentIndex < $assignmentCount;
$assignmentIndex++
) {
$scaledIndex = $assignmentIndex * ($dayCount - 1)
/ ($assignmentCount - 1);
$dayIndex = (int) floor($scaledIndex + 0.5);
$dates[] = $startDate->modify("+{$dayIndex} days");
}
return $dates;
}
/**
* @return list<DateTimeImmutable>
*/
private function groupedDates(
private function distributedDates(
int $assignmentCount,
int $dayCount,
DateTimeImmutable $startDate,
@ -71,19 +37,18 @@ class EvenDistributionScheduler
): array {
$assignmentsPerDay = intdiv($assignmentCount, $dayCount);
$remainderDayCount = $assignmentCount % $dayCount;
$remainderBlockStart = $this->remainderBlockStart(
$remainderDayIndexes = $this->remainderDayIndexes(
assignmentsPerDay: $assignmentsPerDay,
dayCount: $dayCount,
remainderDayCount: $remainderDayCount,
workloadPlacement: $workloadPlacement,
);
$remainderDays = array_fill_keys($remainderDayIndexes, true);
$dates = [];
for ($dayIndex = 0; $dayIndex < $dayCount; $dayIndex++) {
$assignmentCountForDay = $assignmentsPerDay;
if (
$dayIndex >= $remainderBlockStart
&& $dayIndex < $remainderBlockStart + $remainderDayCount
) {
if (isset($remainderDays[$dayIndex])) {
$assignmentCountForDay++;
}
@ -96,6 +61,65 @@ class EvenDistributionScheduler
return $dates;
}
/**
* @return list<int>
*/
private function remainderDayIndexes(
int $assignmentsPerDay,
int $dayCount,
int $remainderDayCount,
WorkloadPlacement $workloadPlacement,
): array {
if ($remainderDayCount === 0) {
return [];
}
if ($workloadPlacement === WorkloadPlacement::Spread) {
return $this->spreadRemainderDayIndexes(
assignmentsPerDay: $assignmentsPerDay,
dayCount: $dayCount,
remainderDayCount: $remainderDayCount,
);
}
$blockStart = $this->remainderBlockStart(
dayCount: $dayCount,
remainderDayCount: $remainderDayCount,
workloadPlacement: $workloadPlacement,
);
return range($blockStart, $blockStart + $remainderDayCount - 1);
}
/**
* @return list<int>
*/
private function spreadRemainderDayIndexes(
int $assignmentsPerDay,
int $dayCount,
int $remainderDayCount,
): array {
if ($remainderDayCount === 1) {
if ($assignmentsPerDay === 0) {
return [0];
}
return [intdiv($dayCount - 1, 2)];
}
$dayIndexes = [];
for ($remainderIndex = 0;
$remainderIndex < $remainderDayCount;
$remainderIndex++
) {
$scaledIndex = $remainderIndex * ($dayCount - 1)
/ ($remainderDayCount - 1);
$dayIndexes[] = (int) floor($scaledIndex + 0.5);
}
return $dayIndexes;
}
private function remainderBlockStart(
int $dayCount,
int $remainderDayCount,

View file

@ -248,9 +248,6 @@ class CreateSchedule
return $workloadPlacement;
}
/**
* @throws BadRequestException
*/
private function resolvedWorkloadPlacement(
?WorkloadPlacement $requestedWorkloadPlacement,
int $assignmentCount,
@ -267,15 +264,6 @@ class CreateSchedule
: WorkloadPlacement::Middle;
}
if (
$requestedWorkloadPlacement === WorkloadPlacement::Spread
&& ! $isSparse
) {
throw new BadRequestException(
'spread placement requires fewer assignments than days',
);
}
return $requestedWorkloadPlacement;
}
}