21c9322127
API CI/CD / Validate (composer + pint) (push) Successful in 3m9s
API CI/CD / Test (PHPUnit) (push) Failing after 5m6s
API CI/CD / Build frontend assets (push) Successful in 1m2s
API CI/CD / Security audit (push) Failing after 49s
API CI/CD / Deploy to shared hosting (PHP) (push) Has been skipped
202 lines
5.3 KiB
PHP
202 lines
5.3 KiB
PHP
<?php
|
|
|
|
namespace App\Services\ClassProgress;
|
|
|
|
use App\Models\Configuration;
|
|
use App\Models\SubjectCurriculum;
|
|
use App\Services\SemesterRangeService;
|
|
|
|
class ClassProgressMetaService
|
|
{
|
|
public function subjectSections(): array
|
|
{
|
|
return (array) config('progress.subject_sections', []);
|
|
}
|
|
|
|
public function statusOptions(): array
|
|
{
|
|
return (array) config('progress.status_options', []);
|
|
}
|
|
|
|
/**
|
|
* Fallback: next N Sundays from today (legacy `buildUpcomingSundayOptions`).
|
|
*
|
|
* @return list<string>
|
|
*/
|
|
public function sundayOptions(int $count = 12): array
|
|
{
|
|
return $this->buildUpcomingSundayOptions($count);
|
|
}
|
|
|
|
/**
|
|
* legacy-aligned Sundays within semester/school-year range when configuration allows.
|
|
*
|
|
* @return list<string>
|
|
*/
|
|
public function sundayOptionsAlignedWithCi(int $count = 12): array
|
|
{
|
|
$range = $this->resolveProgressDateRange();
|
|
if ($range === null) {
|
|
return $this->buildUpcomingSundayOptions($count);
|
|
}
|
|
|
|
[$rangeStart, $rangeEnd] = $range;
|
|
|
|
try {
|
|
$start = new \DateTime($rangeStart);
|
|
$end = new \DateTime($rangeEnd);
|
|
} catch (\Exception $e) {
|
|
return $this->buildUpcomingSundayOptions($count);
|
|
}
|
|
|
|
if ($end < $start) {
|
|
[$start, $end] = [$end, $start];
|
|
}
|
|
|
|
$weekday = (int) $start->format('w');
|
|
if ($weekday !== 0) {
|
|
$start->modify('next sunday');
|
|
}
|
|
|
|
$options = [];
|
|
while ($start <= $end) {
|
|
$options[] = $start->format('Y-m-d');
|
|
$start->modify('+7 days');
|
|
}
|
|
|
|
return $options !== [] ? $options : $this->buildUpcomingSundayOptions($count);
|
|
}
|
|
|
|
/** legacy `pickDefaultWeekStart`. */
|
|
public function pickDefaultWeekStart(array $options): string
|
|
{
|
|
if ($options === []) {
|
|
return '';
|
|
}
|
|
|
|
$today = date('Y-m-d');
|
|
$default = '';
|
|
foreach ($options as $option) {
|
|
if ($option <= $today) {
|
|
$default = $option;
|
|
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return $default !== '' ? $default : $options[0];
|
|
}
|
|
|
|
/**
|
|
* @return list<string>
|
|
*/
|
|
private function buildUpcomingSundayOptions(int $count): array
|
|
{
|
|
$start = new \DateTime('today');
|
|
$weekday = (int) $start->format('w');
|
|
if ($weekday !== 0) {
|
|
$start->modify('next sunday');
|
|
}
|
|
|
|
$options = [];
|
|
for ($i = 0; $i < $count; $i++) {
|
|
$options[] = $start->format('Y-m-d');
|
|
$start->modify('+7 days');
|
|
}
|
|
|
|
return $options;
|
|
}
|
|
|
|
/**
|
|
* legacy `resolveProgressDateRange`.
|
|
*
|
|
* @return array{0:string,1:string}|null
|
|
*/
|
|
private function resolveProgressDateRange(): ?array
|
|
{
|
|
$schoolYear = (string) (Configuration::getConfig('school_year') ?? '');
|
|
if ($schoolYear === '') {
|
|
return null;
|
|
}
|
|
|
|
/** @var SemesterRangeService $svc */
|
|
$svc = app(SemesterRangeService::class);
|
|
|
|
$semester = $svc->normalizeSemester((string) (Configuration::getConfig('semester') ?? ''));
|
|
if ($semester === '') {
|
|
$semester = $svc->getSemesterForDate();
|
|
}
|
|
|
|
if ($semester !== '') {
|
|
$range = $svc->getSemesterRange($schoolYear, $semester);
|
|
if ($range !== null) {
|
|
return $range;
|
|
}
|
|
}
|
|
|
|
return $svc->getSchoolYearRange($schoolYear);
|
|
}
|
|
|
|
public function curriculumOptions(?int $classId, ?string $schoolYear = null): array
|
|
{
|
|
if (! $classId) {
|
|
return [];
|
|
}
|
|
|
|
$options = [];
|
|
foreach ($this->subjectSections() as $slug => $section) {
|
|
$options[$slug] = SubjectCurriculum::getOptionsForClass((int) $classId, (string) $slug, $schoolYear);
|
|
}
|
|
|
|
return $options;
|
|
}
|
|
|
|
public function unitOptions(?int $classId, ?string $schoolYear = null): array
|
|
{
|
|
$curriculum = $this->curriculumOptions($classId, $schoolYear);
|
|
$units = [];
|
|
|
|
foreach ($curriculum as $slug => $rows) {
|
|
$seen = [];
|
|
$units[$slug] = [];
|
|
|
|
foreach ($rows as $row) {
|
|
$unitNumber = $row->unit_number ?? null;
|
|
$unitTitle = trim((string) ($row->unit_title ?? ''));
|
|
|
|
if ($unitNumber === null && $unitTitle === '') {
|
|
continue;
|
|
}
|
|
|
|
$key = ((string) $unitNumber).'|'.$unitTitle;
|
|
if (isset($seen[$key])) {
|
|
continue;
|
|
}
|
|
|
|
$seen[$key] = true;
|
|
$units[$slug][] = [
|
|
'unit_number' => $unitNumber !== null ? (int) $unitNumber : null,
|
|
'unit_title' => $unitTitle,
|
|
'label' => $this->formatUnitLabel($unitNumber !== null ? (int) $unitNumber : null, $unitTitle),
|
|
];
|
|
}
|
|
}
|
|
|
|
return $units;
|
|
}
|
|
|
|
private function formatUnitLabel(?int $unitNumber, string $unitTitle): string
|
|
{
|
|
if ($unitNumber !== null && $unitTitle !== '') {
|
|
return 'Unit '.$unitNumber.' - '.$unitTitle;
|
|
}
|
|
|
|
if ($unitNumber !== null) {
|
|
return 'Unit '.$unitNumber;
|
|
}
|
|
|
|
return $unitTitle;
|
|
}
|
|
}
|