Course seat management#187
Conversation
pgetta
left a comment
There was a problem hiding this comment.
Great work Gil!
I've added some comments and questions.
pgetta
left a comment
There was a problem hiding this comment.
Thank you Gil.
I'm good with the change, but I'll let you finish the discussion with @bilalesi.
As for the institutions - we can revise that later, either future use cases will confirm they are needed or we just refactor and delete them.
bilalesi
left a comment
There was a problem hiding this comment.
Great work @g-bar,
about your question about the ledger:
think of it like an "undo notebook." when the use case is doing a bunch of steps, creating a Keycloak group, setting up accounting, create seat, ..., each step pushes an undo action onto the ledger. If something blows up halfway, the ledger runs all those undos in reverse (last one first), so you don't end up with half-created stuff floating around
the pattern can be applied to any use cases, you just need to figure out how you order, function implementation, and either throw directly/postpone, this make the mental model and steps of creation very clear both for us and the machine, the other nice thing about the ledger is to have domain errors with the translator which can be very helpful in debugging
still have few comments/questions but it can be in another improvement PR/chat
| async def assign_seats( | ||
| db: AsyncSession, | ||
| *, |
There was a problem hiding this comment.
i would also suggest to see the ledger and compensation tools,
the tool is allow you to create actions and reverse actions, all managed by the ledger (virtual lab creation)
here the catch path only soft-deletes the project and does not reverse/deplete the granted credits
There was a problem hiding this comment.
I refactored this to use the ledger now I create the project, init accounting, fund project, create an enrolment and assign a seat using the ledger to roll back failed ops, all while not committing anything to the db until after all iterations are done to avoid releasing the seats prematurely.
| async def create_course( | ||
| db: AsyncSession, | ||
| payload: CourseCreateBody, |
There was a problem hiding this comment.
this is also best use case for the ledger pattern
There was a problem hiding this comment.
this one not quite, the create_course doesnt create a virtual lab, it merely points to an existing virtual lab created previously.
there is nothing to clean up in case of failure, merely the course row doesnt'get created the virtual lab and project remain valid.
| # Post-commit: refresh vlab (now has course relationship) and fund template project | ||
| await db.refresh(vlab) | ||
| await accounting_cases.fund_project( | ||
| virtual_lab_id=vlab.id, | ||
| project_id=payload.template_project_id, | ||
| amount=settings.CREDITS_PER_SEAT, | ||
| ) |
There was a problem hiding this comment.
the endpoint can create a project even the funding was not successful, is that okey ?
There was a problem hiding this comment.
same as above, the project doesn't get created here.
|
/opt/hostedtoolcache/Python/3.12.13/x64/lib/python3.12/site-packages/safety/auth/main.py:6: AuthlibDeprecationWarning: authlib.jose module is deprecated, please use joserfc instead. poetry audit reportLoading... • starlette installed 0.52.1 affected <1.0.1 CVE CVE-2026-48710 1 vulnerabilities found in 1 packages |
This PR implements most of the functionality required for the education plan: https://github.com/openbraininstitute/PMO/issues/96
Courses Provisioning
Relationships
Course Lifecycle
Provisioning
obi-virtual-lab.POST /virtual-labsPOST /virtual-labs/{vlab_id}/projectsPOST /coursesstart_date,last_drop_date, andend_date.PATCH /courses/{course_id}POST /courses/{course_id}/activatePOST /virtual-labs/{vlab_id}/invitesPOST /seats/provisionEnrolment
Faculty assigns available seats to students (identified by student id + email).
POST /seats/courses/{course_id}/assignThis creates an enrolment and a project per student and sends an invite email.
Student claims the enrolment, storing their
user_id.POST /courses/{course_id}/claimOn login (after
start_date, beforeend_date), the student's enrolment is activated (added to KC groups).POST /courses/activate-enrolmentsDropping
Faculty can drop a student and recover the seat if all conditions are met:
seat.previously_dropped == Falsenow < course.last_drop_datePOST /seats/courses/{course_id}/dropWhen dropped, the student's project budget is depleted and the student is removed from KC groups (vlab and project).
Course Expiry
A daily cronjob checks courses whose
end_datehas passed, drops every remaining student, and depletes all credits from all projects and the virtual lab.