add tests batch 20

This commit is contained in:
root
2026-06-09 01:03:53 -04:00
parent 95efb9652e
commit 6be4875c5e
1502 changed files with 13797 additions and 11313 deletions
@@ -45,7 +45,6 @@ class BalancedSectionPlacementService
return $scoreB <=> $scoreA;
}
$name = strcmp((string) ($a['student_name'] ?? ''), (string) ($b['student_name'] ?? ''));
return $name !== 0 ? $name : ((int) $a['student_id'] <=> (int) $b['student_id']);
});
}
@@ -80,7 +79,6 @@ class BalancedSectionPlacementService
for ($i = 1; $i <= $sectionCount; $i++) {
$sizes[$i] = $base + ($i <= $remainder ? 1 : 0);
}
return $sizes;
}
@@ -100,7 +98,6 @@ class BalancedSectionPlacementService
if ($cmp !== 0) {
return $cmp;
}
return $a['section_index'] <=> $b['section_index'];
});
@@ -7,7 +7,9 @@ use Illuminate\Support\Facades\DB;
class PlacementPoolBuilder
{
public function __construct(private ScoreBandClassifier $bands) {}
public function __construct(private ScoreBandClassifier $bands)
{
}
public function build(string $fromSchoolYear, string $toSchoolYear, ?int $toGradeLevelId = null): array
{
@@ -36,39 +38,34 @@ class PlacementPoolBuilder
$studentId = (int) $row->student_id;
if ((int) ($row->is_active ?? 1) === 0) {
$exceptions['withdrawn_or_inactive_student'][] = $studentId;
continue;
}
if ($this->alreadyPlaced($studentId, $toSchoolYear)) {
$exceptions['duplicate_target_year_placement'][] = $studentId;
continue;
}
if (! $this->isPromotedDecision((string) ($row->decision ?? ''))) {
if (!$this->isPromotedDecision((string) ($row->decision ?? ''))) {
$bucket = $row->decision === null ? 'missing_student_decision' : 'decision_conflicts_with_enrollment_request';
$exceptions[$bucket][] = $studentId;
continue;
}
$score = $row->year_score !== null ? (float) $row->year_score : ($row->final_average !== null ? (float) $row->final_average : null);
if ($score === null) {
$exceptions['missing_student_decision'][] = $studentId;
continue;
}
try {
$band = $this->bands->fromScore($score, true);
} catch (\RuntimeException) {
$exceptions['failed_retained'][] = $studentId;
continue;
}
$pool[] = [
'student_id' => $studentId,
'student_type' => 'returning',
'student_name' => trim((string) $row->lastname.', '.(string) $row->firstname),
'student_name' => trim((string) $row->lastname . ', ' . (string) $row->firstname),
'source_decision_id' => $row->decision_id ? (int) $row->decision_id : null,
'source_enrollment_id' => $row->enrollment_id ? (int) $row->enrollment_id : null,
'final_score' => $score,
@@ -101,19 +98,17 @@ class PlacementPoolBuilder
$studentId = (int) $row->student_id;
if ((int) ($row->is_active ?? 1) === 0) {
$exceptions['withdrawn_or_inactive_student'][] = $studentId;
continue;
}
if ($this->alreadyPlaced($studentId, $toSchoolYear)) {
$exceptions['duplicate_target_year_placement'][] = $studentId;
continue;
}
$pool[] = [
'student_id' => $studentId,
'student_type' => 'new',
'student_name' => trim((string) $row->lastname.', '.(string) $row->firstname),
'student_name' => trim((string) $row->lastname . ', ' . (string) $row->firstname),
'source_decision_id' => null,
'source_enrollment_id' => (int) $row->enrollment_id,
'final_score' => null,
@@ -182,7 +177,6 @@ class PlacementPoolBuilder
private function isPromotedDecision(string $decision): bool
{
$normalized = strtolower(trim($decision));
return in_array($normalized, ['passed', 'pass', 'promoted', 'promote', 'eligible_to_continue'], true);
}
}
@@ -9,7 +9,6 @@ use RuntimeException;
class PromotionSectionCapacityService
{
public const CONFIG_KEY = 'promotion.section_capacity';
public const DEFAULT_CAPACITY = 20;
public function capacity(): array
@@ -24,7 +23,7 @@ class PromotionSectionCapacityService
'fallback' => self::DEFAULT_CAPACITY,
]);
$value = self::DEFAULT_CAPACITY;
} elseif (! ctype_digit((string) $raw)) {
} elseif (!ctype_digit((string) $raw)) {
throw new RuntimeException('promotion.section_capacity must be a positive integer.');
} else {
$value = (int) $raw;
@@ -14,7 +14,8 @@ class SectionPlacementPreviewService
private PromotionSectionCapacityService $capacityService,
private PlacementPoolBuilder $poolBuilder,
private BalancedSectionPlacementService $placementService
) {}
) {
}
public function createDraft(
string $fromSchoolYear,
@@ -118,12 +119,12 @@ class SectionPlacementPreviewService
->where('is_event_only', 0)
->exists();
if ($duplicate) {
throw new RuntimeException('Student '.$studentId.' already has a target-year class placement.');
throw new RuntimeException('Student ' . $studentId . ' already has a target-year class placement.');
}
$sectionId = $sectionMap[(int) $student->planned_section_index] ?? null;
if ($sectionId === null) {
throw new RuntimeException('Missing target section for planned section '.$student->planned_section_index.'.');
throw new RuntimeException('Missing target section for planned section ' . $student->planned_section_index . '.');
}
DB::table('student_class')->insert([
@@ -132,7 +133,7 @@ class SectionPlacementPreviewService
'is_event_only' => 0,
'semester' => $semester,
'school_year' => $batch->to_school_year,
'description' => 'Finalized from section placement batch #'.$batch->getKey(),
'description' => 'Finalized from section placement batch #' . $batch->getKey(),
'created_at' => now(),
'updated_at' => now(),
'updated_by' => $userId,
@@ -153,7 +154,7 @@ class SectionPlacementPreviewService
private function ensureSections(SectionPlacementBatch $batch, string $semester): array
{
if (! $batch->to_grade_level_id) {
if (!$batch->to_grade_level_id) {
throw new RuntimeException('to_grade_level_id is required to finalize placement sections.');
}
@@ -175,7 +176,6 @@ class SectionPlacementPreviewService
if ($existing) {
$map[$i] = (int) $existing;
continue;
}
@@ -198,8 +198,7 @@ class SectionPlacementPreviewService
private function sectionName(int $classId, string $suffix, string $schoolYear): string
{
$className = DB::table('classes')->where('id', $classId)->value('class_name');
$base = $className ? (string) $className : ('Class '.$classId);
return $base.'-'.$suffix.' '.$schoolYear;
$base = $className ? (string) $className : ('Class ' . $classId);
return $base . '-' . $suffix . ' ' . $schoolYear;
}
}