Skip to content

feat(timetable): store timetable wishes and scenarios, and set the school week grid - #790

Merged
rubenvdlinde merged 3 commits into
developmentfrom
feat/timetable-generator-schemas
Sep 29, 2026
Merged

rubenvdlinde merged 3 commits into
developmentfrom
feat/timetable-generator-schemas

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Timetable generator, section 1: wishes and scenarios can be stored, and admins set the school week

Base: development (7a588fc).

Change: timetabling-generator (DECISIONS row 17), section 1 of 9 (tasks 1.1 and 1.2). The change stays open; no row moves yet. tt-hard-soft-wishes is set built by section 5, tt-scenario-compare by section 7. Sections 1 to 4 do not depend on the solver choice that is still open for Ruben.

What changes

  • Two new schemas in the planninq register (0.26.0), both admin-write and read by planninq-timetable and admins, like timetableSession:
    • timetableWish: appliesTo (teacher, group, room, activity), reference, kind (unavailable, avoid, maxPerDay, noGaps, sameRoom), periods (period keys such as wed-5, checked by pattern), limit, strength (hard, soft), weight (1 to 3), note.
    • timetableScenario: title, source (generated, imported), weekOf, windowFrom, windowTo, status (queued, running, done, failed, published), seed, input (the SolverInput snapshot), placements, unplaced, brokenWishes, metrics, reason, publishedAt.
  • Three demo rows of each in planninq_mock_register.json.
  • Admin settings timetable_period_grid (default Monday to Friday, eight periods of 50 minutes from 08:30) and timetable_generator_budget_minutes (default 10, 1 to 120). lib/Service/TimetableGridService.php validates, reads and stores both; SettingsController merges them into GET /api/settings and saves them on POST /api/settings. A refused value is not stored, which is how every other admin setting behaves. They live beside SettingsService rather than in it: one more dependency there crossed phpmd's coupling limit (14), the same way the risk scale is handled.
  • 57 schema strings in all 36 locales (check:schema-l10n stays at its baseline of 124).
  • Design amended in this PR: the scenario window is two date fields, windowFrom and windowTo, not a nested object. A nested object needs its own translated titles (gate-51). A reason field holds why a run failed, which task 5.2 needs.
  • App version 0.2.23-unstable.20260930110000 so the register imports.

No screen yet: the wish editor is section 3 and the grid has no admin form in this PR (it is read by the input builder in section 2).

Tests

  • PlanninqRegisterSchemaTest::testTimetableWishSchemaAndItsRules: fields, enums and rules; the spec's hard wish (klaas, Wednesday periods 5 to 8) and a soft wish with weight 2 pass OpenRegister's validator (Opis); strength firm, weight 4 and period wednesday-5 do not.
  • PlanninqRegisterSchemaTest::testTimetableScenarioKeepsItsInputAndResult: an example SolverInput in a queued scenario and a finished scenario pass the validator; status finished, source manual and a placement without a room do not.
  • PlanninqRegisterSchemaTest::testMockRegisterCarriesTimetableGeneratorDemoRows and testRegisterDeclaresExactlySeventeenSchemas.
  • TimetableGridServiceTest (3): the default grid and budget and its 40 period keys; an empty day list, an unknown day, a day twice, overlapping periods, a period ending before it starts, no periods, a bad time, a period that is not an object and non-JSON are refused; budgets 0, 121, ten and 2.5 are refused; a good grid is stored in week order and its period keys follow it.
  • SettingsControllerTest::testTheSettingsCarryTheTimetableGrid: GET /api/settings carries both, an admin POST stores a good grid and not a refused one.

Red first, against development 7a588fc: the four register tests and the first grid test failed (four failures, one TypeError: no grid setting). The grid tests were rewritten when the settings moved out of SettingsService and failed again against the first commit (no settings()). Log in the lane files.

Verification

At head 4eea55e (development 7a588fc): composer check:strict 0 (PHPUnit 489, 7 skipped; psalm InvalidOperand in periodKeys() fixed in the last commit), npm run lint, check:l10n, check:l10n-js, check:schema-l10n (124, baseline), check:manifest, vitest (397), openspec validate --specs (39/39) and parity_verify --strict 0 (run on c66cc2b; the later commits touch PHP only). CI-shaped Hydra gates (hydra-gates@main, full coverage): ALL 83 applicable gates passed.

Live check

  1. Upgrade the app. GET /apps/openregister/api/objects/planninq/timetableWish as an admin returns an empty list, not an unknown-schema error.
  2. As an admin, POST a wish {"appliesTo":"teacher","reference":"klaas","kind":"unavailable","periods":["wed-5","wed-6"],"strength":"hard"}: created. The same with "strength":"firm": refused by validation.
  3. As a member of planninq-timetable, read the wish: returned. Try to POST one: refused.
  4. POST /apps/planninq/api/settings with timetable_period_grid {"days":["mon"],"periods":[{"start":"09:00","end":"08:00"}]}, then GET /apps/planninq/api/settings: the grid is still the default.

Inherited: the non-required Newman, PHPUnit (pgsql) and Quality Report jobs have been red on every PR since #758.

🤖 Generated with Claude Code

…for the timetable generator

timetableWish and timetableScenario (admin write, planninq-timetable read),
three demo rows each, register 0.26.0; admin settings timetable_period_grid
and timetable_generator_budget_minutes validated by TimetableGridService.
Schema strings in 36 locales. Design: window is windowFrom/windowTo.
…nd reach the settings page through SettingsController (phpmd coupling on SettingsService)
@rubenvdlinde
rubenvdlinde merged commit 19711e0 into development Sep 29, 2026
30 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/planninq @ 94436a1

Check PHP Vue Security License Tests
lint ✅
phpcs ✅
phpmd ✅
psalm ✅
phpstan ✅
phpmetrics ✅
eslint ✅
stylelint ✅
build ✅
check-manifest ✅
check-l10n-js ✅
check-schema-l10n ✅
composer ✅ ✅ 107/107
npm ✅ ✅ 646/646
app:check-code ⏭️
info.xml ✅
REUSE ❌
lockfile sync ✅
PHPUnit ❌
Newman ❌
Playwright ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it on any branch from the Actions tab, or locally with npx playwright test.
Hydra gates ✅

Quality workflow — 2026-09-29 22:17 UTC

Download the full PDF report from the workflow artifacts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant