add workload placement

This commit is contained in:
Yisroel Baum 2026-08-19 09:06:56 +03:00
parent 465db4a5b8
commit 46ba265f38
Signed by: yisroelbaum
GPG key ID: 0FA60884F75520A9
8 changed files with 291 additions and 34 deletions

View file

@ -8,8 +8,10 @@ use App\Exceptions\BadRequestException;
use App\Exceptions\NotFoundException;
use App\Schedule\CreateScheduleAssignmentDto;
use App\Schedule\CreateScheduleDto;
use App\Schedule\EvenDistributionScheduler;
use App\Schedule\Schedule;
use App\Schedule\ScheduleRepository;
use App\Schedule\WorkloadPlacement;
use App\Set\Set;
use App\Set\SetLevelRepository;
use App\Set\SetRepository;
@ -23,6 +25,7 @@ class CreateSchedule
private SetLevelRepository $setLevelRepository,
private ElementRepository $elementRepository,
private ScheduleRepository $scheduleRepository,
private EvenDistributionScheduler $evenDistributionScheduler,
) {}
/**
@ -61,6 +64,9 @@ class CreateSchedule
'targetDate must not be before startDate',
);
}
$workloadPlacement = $this->workloadPlacement(
$request->workloadPlacement,
);
$elements = array_values(array_filter(
$this->orderedElements($set),
@ -72,10 +78,15 @@ class CreateSchedule
throw new BadRequestException('level has no elements');
}
$assignments = $this->assignments(
elements: $elements,
$scheduledDates = $this->evenDistributionScheduler->scheduledDates(
assignmentCount: count($elements),
startDate: $startDate,
targetDate: $targetDate,
workloadPlacement: $workloadPlacement,
);
$assignments = $this->assignments(
elements: $elements,
scheduledDates: $scheduledDates,
);
return $this->scheduleRepository->create(new CreateScheduleDto(
@ -144,29 +155,21 @@ class CreateSchedule
/**
* @param list<Element> $elements
* @param list<DateTimeImmutable> $scheduledDates
* @return list<CreateScheduleAssignmentDto>
*/
private function assignments(
array $elements,
DateTimeImmutable $startDate,
DateTimeImmutable $targetDate,
array $scheduledDates,
): array {
$differenceInDays = $startDate->diff($targetDate)->days;
$dayCount = $differenceInDays + 1;
$elementCount = count($elements);
$assignments = [];
foreach ($elements as $index => $element) {
$dayIndex = $this->dayIndex(
elementIndex: $index,
elementCount: $elementCount,
dayCount: $dayCount,
);
$assignments[] = new CreateScheduleAssignmentDto(
name: $element->getName(),
kind: $element->getKind(),
path: $this->elementPath($element),
scheduledDate: $startDate->modify("+{$dayIndex} days"),
scheduledDate: $scheduledDates[$index],
position: $index + 1,
);
}
@ -190,25 +193,6 @@ class CreateSchedule
return $path;
}
private function dayIndex(
int $elementIndex,
int $elementCount,
int $dayCount,
): int {
if ($elementCount === 1 || $dayCount === 1) {
return 0;
}
if ($elementCount < $dayCount) {
$scaledIndex = $elementIndex * ($dayCount - 1)
/ ($elementCount - 1);
return (int) floor($scaledIndex + 0.5);
}
return intdiv($elementIndex * $dayCount, $elementCount);
}
/**
* @throws BadRequestException
*/
@ -237,4 +221,23 @@ class CreateSchedule
return $date;
}
/**
* @throws BadRequestException
*/
private function workloadPlacement(?string $value): WorkloadPlacement
{
if ($value === null) {
return WorkloadPlacement::Middle;
}
$workloadPlacement = WorkloadPlacement::tryFrom($value);
if ($workloadPlacement === null) {
throw new BadRequestException(
'workloadPlacement must be start, middle, or end',
);
}
return $workloadPlacement;
}
}