Repository navigation
feat: link timetable subjects to their Campus Virtual courses (#45) - #48
Merged
Merged
Conversation
Each subject can now carry its Campus Virtual (cv.upt.ro) course id, and every timetable response that has a subject gives a ready campusVirtualUrl (null when unknown), so web and mobile link each class without building URLs. - scripts/campus-virtual-courses.py (stdlib): searches the catalog with the read-only core_course_search_courses, matches the subjects per programme-year shortname code, and rewrites the overrides' campusVirtual section; ambiguous and unmatched subjects are listed and left out, hand: true entries survive a re-run. The token is read from the environment or an env file, sent only in an https POST body, never printed or written. - import-timetable --subjects-json FILE exports the subjects read, for the script. - The overrides validate the campusVirtual section (programmes, years, ids, duplicates, unknown keys); the import stores the id (subjects.campus_virtual_course_id, nullable), prints which subjects have none, adds the count to the run summary and warns about entries naming no subject. - Tests: synthetic catalogs for the script (run in CI), and parser, import and endpoint tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- W1: the export carries the overrides' SHA-256 and the script refuses a file that changed since, so a stale export can't undo hand edits; the summary lists the entries removed and changed. - W2: names with different numbers (1/2, I/II) never match; spelling closeness counts only when every word of the subject's is in the course's; initials alone no longer match; an abbreviation scores 0.95 (3+ letters for the sheet's), and against a course that fits by name it is ambiguous; the cross-programme fallback keeps to the same level and year. Every match under 1.0 is listed with its score. - W3: the token goes only to https://cv.upt.ro. - The file is replaced atomically with a .bak; stale markers anywhere are dropped and comments inside the section kept; subjects in two workbooks give one entry; --subjects-json isn't written when a sheet has errors; the import validates campusVirtual.search and codes. - Tests: the token stays out of output when urlopen fails or returns non-JSON; a fixture the script writes is parsed by ImportOverrides; markers, end of file and duplicates; a removed entry or a hand entry without an id clears the course; refusals assert FormatException and its message; the W2 cases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #45
What
Each timetable subject can carry its Campus Virtual (cv.upt.ro) course, and every class links to it.
scripts/campus-virtual-courses.py(Python, standard library only) builds the mapping once per semester:core_course_search_courses, for the shortname prefixes in the overrides'campusVirtual.search;<CODE><YEAR>part, withcampusVirtual.codesfor programmes whose courses sit under more than one code), by name, by the sheet's abbreviation or by initials. A subject it can't place there gets another programme's course only under exactly its name (a shared course);campusVirtualsection of the private overrides file and keeps every other line and comment;hand: trueis kept on the next run, to pick a course or say "none".MOODLE_TOKENin the environment or an--env-file. It goes only into the body of an https POST and is never printed or written; a test checks both.import-timetable --subjects-json FILEwrites the subjects the import read (plus the section's input) for the script. No database is needed: the script can't parse YAML or run the parser itself.campusVirtual: known programmes, years 1-6, ids above 0, no name listed twice, and unknown keys refused as everywhere.Subject.CampusVirtualCourseId(new nullable columnsubjects.campus_virtual_course_id; migrationAddSubjectCampusVirtualCourse, made on ThorSPB).-- Campus Virtual: N of M subjects have a coursewith the ones that have none, and adds the count to the run's summary.cv:<id>, so--dry-runshows a changed link.docs/timetable-import.md("Campus Virtual courses", the overrides example) and the README's timetable section.Why
#45: a student goes from a class in the timetable to its Moodle course in one tap. The mapping is made with a token where one is available. The import itself can't reach cv.upt.ro, because the Pi's egress guard allows only sign-in and push.
How I tested it
--subjects-jsonoutput and its usage error;campusVirtualUrlon/me/timetable, choices,/me/subjects(GET and PUT) and the room timetable.-warnaserror,dotnet format --verify-no-changes, the non-database tests, and the script tests.API contract
A new nullable string field,
campusVirtualUrl(https://cv.upt.ro/course/view.php?id=<id>, or null), on:OccurrenceResponse(/me/timetable,/timetable/groups/{id});ChoiceSubjectResponse(/timetable/groups/{id}/choices);StudentSubjectResponse(/me/subjects,PUT /me/subjects/{id});RoomOccurrenceResponse(/rooms/{code}/timetable).Nothing is removed. The id itself isn't exposed, so clients never build the URL.
Checklist
🤖 Generated with Claude Code