Skip to content

feat(timetable): answer a timetable query by course, and carry a lesson's course and online link - #865

Merged
rubenvdlinde merged 5 commits into
developmentfrom
feat/timetable-course-query
Oct 3, 2026
Merged

rubenvdlinde merged 5 commits into
developmentfrom
feat/timetable-course-query

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Timetable contract v2: answer a query by course, and carry a lesson's course and online link

Decision: Ruben, DECISIONS row 53 (learniq live pass D8): "build the planninq path: planninq answers the course query and carries a lesson link; learniq reads both." The contract both apps build against is ~/memcap-work/build-all/for-ruben/planninq-timetable-course-query-and-lesson-link.md (v2). learniq's half is on ConductionNL/learniq fix/planninq-course-sessions-and-link, written and tested against exactly this contract.

What changes

  • TimetableSessionsQueryEvent::CONTRACT_VERSION is 2. learniq sends a course query only from 2; nothing else in the event class changes.
  • timetableSession (schema 0.4.0, register 0.37.0) gains courseId (string, uuid, nullable) and onlineMeetingUrl (string, uri, nullable, max 2048). Both are writable through the existing upsert (event and admin endpoint). A course id that is not a uuid is rejected with invalid-course-id, a link that is not a full address with invalid-link, before anything is written (so OpenRegister never refuses a save silently); an empty value clears the field to null.
  • TimetableSessionQuery::IDENTITY_KEYS gains courseId: a query naming only a course (plus from, to, limit) answers that course's lessons, with the same window, cancelled and draft rules. The refusal message names courseId too.
  • Every lesson a query answers carries courseId and onlineMeetingUrl, null when unset, on cohort and teacher queries as well.
  • 4 schema strings in 36 locales. OpenSpec change timetable-course-query (stays open: the live check with learniq, task 2.1).

Tests

  • Red first: tests/unit/Listener/TimetableCourseQueryTest.php runs the real event, listener, service, rows and query over the in-memory OpenRegister and validates every stored lesson against the real timetableSession fragment with Opis. On development 5 of 6 fail (lane file lanefix/red-tt.log); green after with the timetable listener and service suites (184 tests, lanefix/green-tt.log).

Live check (after learniq's branch lands too)

Set courseId on two overlapping planninq lessons for two elective courses; GET /apps/learniq/api/timetable/course-slots?courseIds=<A>,<B>&withCore=1 as a learner answers both slots and the picker warns. Set an https onlineMeetingUrl on a lesson: My timetable shows Join and it opens in a new tab. learniq then refreshes its stub tests/Stubs/Planninq/Event/TimetableSessionsQueryEvent.php.

Inherited on CI, not touched here: PHPUnit (PHP 8.3, NC stable34, pgsql), Newman, Quality Report.

@SPEC openspec/changes/timetable-course-query/specs/school-timetable/spec.md#requirement-another-app-reads-a-courses-lessons-and-their-online-link-req-007

…ry lesson carries its course and link (contract v2, red)
…on's course and online link (contract v2)

DECISIONS row 53 (learniq live pass D8). TimetableSessionsQueryEvent::CONTRACT_VERSION 2; courseId is an identity key; timetableSession gains courseId (uuid) and onlineMeetingUrl (uri), both nullable, writable through the upsert, refused when malformed (invalid-course-id, invalid-link), cleared by an empty value; every lesson in the read shape carries both. Schema 0.4.0, register 0.37.0, 4 strings x 36 locales.
…ape, so the session service stays under phpmd's complexity limit

Also wraps the optional field list for phpcs, and the draft-status test checks the timetableSession version is at least 0.3.0 instead of pinning it.
…-course-query

# Conflicts:
#	l10n/be.js
#	l10n/be.json
#	l10n/bg.js
#	l10n/bg.json
#	l10n/bs.js
#	l10n/bs.json
#	l10n/ca.js
#	l10n/ca.json
#	l10n/cs.js
#	l10n/cs.json
#	l10n/da.js
#	l10n/da.json
#	l10n/de.js
#	l10n/de.json
#	l10n/el.js
#	l10n/el.json
#	l10n/en.js
#	l10n/en.json
#	l10n/es.js
#	l10n/es.json
#	l10n/et.js
#	l10n/et.json
#	l10n/fi.js
#	l10n/fi.json
#	l10n/fr.js
#	l10n/fr.json
#	l10n/ga.js
#	l10n/ga.json
#	l10n/hr.js
#	l10n/hr.json
#	l10n/hu.js
#	l10n/hu.json
#	l10n/is.js
#	l10n/is.json
#	l10n/it.js
#	l10n/it.json
#	l10n/lb.js
#	l10n/lb.json
#	l10n/lt.js
#	l10n/lt.json
#	l10n/lv.js
#	l10n/lv.json
#	l10n/mk.js
#	l10n/mk.json
#	l10n/mt.js
#	l10n/mt.json
#	l10n/nb.js
#	l10n/nb.json
#	l10n/nl.js
#	l10n/nl.json
#	l10n/pl.js
#	l10n/pl.json
#	l10n/pt.js
#	l10n/pt.json
#	l10n/rm.js
#	l10n/rm.json
#	l10n/ro.js
#	l10n/ro.json
#	l10n/ru.js
#	l10n/ru.json
#	l10n/sk.js
#	l10n/sk.json
#	l10n/sl.js
#	l10n/sl.json
#	l10n/sq.js
#	l10n/sq.json
#	l10n/sr.js
#	l10n/sr.json
#	l10n/sv.js
#	l10n/sv.json
#	l10n/tr.js
#	l10n/tr.json
#	l10n/uk.js
#	l10n/uk.json
@rubenvdlinde
rubenvdlinde merged commit e20f551 into development Oct 3, 2026
38 of 40 checks passed
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/planninq @ 0eecb41

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 locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development.
Hydra gates ✅

Quality workflow — 2026-10-03 06:53 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