Programme-scoped academic roles, and replacements derived from the course being cleared - #1957
Merged
vikrantwiz02 merged 1 commit intoAug 18, 2026
Conversation
… course being cleared Acad UG, Acad PG and Acad Ph.D. are academic administrators for one programme level. A new applications/globals/programme_scope.py resolves the scope from the role the user is acting as, and every academic view now filters through it: reads are limited to that level's students, batches, programmes and grade rows, writes reject a target outside it, and an announcement can only be aimed at a programme the sender administers. acadadmin and Dean Academic stay unscoped. Disciplines, Courses, Course Instructors, Theses and the Academic Calendar are deliberately shared, since they are not per-programme data. Approving a BL request read the grade and the prior registration of the course being *registered* rather than the one being cleared, so a stand-in for an open elective was filed as Regular with no replacement link -- which also meant the old low grade kept counting in CPI, since superseded_course_codes has nothing to follow. Approval now works off the source course, and a BL slot accepts a retake of that source, not only one of the slot's stand-ins. The same missing-source pattern is fixed everywhere else it appears: - a course whose latest attempt cleared it is no longer offered as a BL source (the low-grade filter ran before "latest", so it returned the latest *low* grade); the source list and the slot-eligibility check now agree - one backlog can no longer be cleared through two slots at once, which would have earned its credits twice; a source already in flight is left out of the dropdown and refused on submit - the transcript marks a substitute S and a repeat R by following the replacement link, instead of only recognising SW-prefixed courses - the bulk replacement upload sets the new registration's type from the replaced course's grade instead of leaving it Regular - pre-registration derives backlog vs improvement from the repeated course instead of hardcoding Backlog, and refuses a prev_registration_id that belongs to another student - the Swayam approval reads the source's *latest* grade (an unordered .first() picked a different grade for 1131 student-course pairs in the current data) - the manual add-course paths only link a replaced registration that belongs to the student being edited - Grade Validation labels a substituted course Substitute rather than Regular
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.
Backend half of the Acad UG / Acad PG / Acad Ph.D. roles, plus a family of replacement bugs found while testing them. Frontend counterpart: FusionIIIT/Fusion-client#293.
Programme-scoped roles
Three new designations administer one programme level each.
applications/globals/programme_scope.pyresolves the scope from the role the user is acting as (last_selected_role, guarded so it can never widen access beyond the designations actually held) and every academic view filters through it:acadadminand Dean Academic stay unscopedVerified against a copy of production data: batches 43 = 30 UG + 13 PG; programmes 5/13/8 filtered to 5/0/0, 0/13/0, 0/0/8; grade summary 211 → 202/21/0; an Acad UG attempt to announce to PG returns 403; cross-programme mutations are refused.
Migrations create the designations, add the announcement target, and clone
acadadmin'sModuleAccessrow to the three roles.The replacement bug this started from
Approving a BL request derived the registration type and the replaced registration from the course being registered instead of the one being cleared. A retake happened to work (same course); a stand-in for an open elective did not: no prior grade for
OE3C42meantRegular, and no prior registration of it meant nocourse_replacementrow at all — so the cleared course's low grade also kept counting in CPI, sincesuperseded_course_codeshad no link to follow.Approval now works off
source_course, and a BL slot accepts a retake of that source rather than only the slot's stand-ins (the source is not in any BL slot's course list, so this was a 404 before).Re-approving the affected request through the endpoint:
OE3C42went fromRegular/no link toImprovementreplacingCS8007, and semester 1'sCS8007row now reports "Replaced By OE3C42".The same pattern everywhere else it appears
claimed_bySonly forSW-prefixed courses; a stand-in got no marker at allSsubstitute,RrepeatS(IM)/S(BL)Sfor a substitute; repeats keepR(BL)/R(IM)Regularregistration_type='Backlog'hardcoded, andprev_registration_idtrusted from the request body.first()on the source's grades — a different grade for 1131 student-course pairs in current data (e.g.EC203b:F→ Backlog vs latestD→ Improvement)RegularSubstituteOne shared
latest_grade()/registration_type_for_grade()pair replaces the four copies of the grade lists that had drifted apart.Checks
Every changed path exercised end to end against a copy of production data, with all probe rows and tokens removed afterwards;
manage.py checkclean and no missing migrations. The repo's two backend test modules can't run here — the test database is missing tables, which predates this branch.Deploy
migrate, thennormalize_student_categoriesandsync_student_accounts(in that order) if not already run from #1956.