diff --git a/lms/services/roster.py b/lms/services/roster.py index 8194822ec6..71164cb6e2 100644 --- a/lms/services/roster.py +++ b/lms/services/roster.py @@ -1,6 +1,6 @@ from logging import getLogger -from sqlalchemy import Select, select, update +from sqlalchemy import Select, func, select, text, update from lms.models import ( ApplicationInstance, @@ -251,6 +251,11 @@ def _get_roster_users(self, roster, tool_consumer_instance_guid): for member in roster: lti_user_id = member.get("lti11_legacy_user_id") or member["user_id"] lti_v13_user_id = member["user_id"] + lms_api_user_id = ( + member.get("message", [{}])[0] + .get("https://purl.imsglobal.org/spec/lti/claim/custom", {}) + .get("canvas_user_id") + ) name = display_name( given_name=member.get("name", ""), family_name=member.get("family_name", ""), @@ -270,6 +275,7 @@ def _get_roster_users(self, roster, tool_consumer_instance_guid): "lti_v13_user_id": lti_v13_user_id, "h_userid": h_userid, "display_name": name, + "lms_api_user_id": lms_api_user_id, } ) @@ -282,6 +288,14 @@ def _get_roster_users(self, roster, tool_consumer_instance_guid): "updated", # lti_v13_user_id is not going to change but we want to backfill it for existing users. "lti_v13_user_id", + # Same for lms_api_user_id, not going to change for existing user but we are backfilling for now + ( + "lms_api_user_id", + func.coalesce( + text('"excluded"."lms_api_user_id"'), + text('"lms_user"."lms_api_user_id"'), + ), + ), ], ) diff --git a/tests/unit/lms/services/roster_test.py b/tests/unit/lms/services/roster_test.py index f53ffd5c65..ee54a6d0e2 100644 --- a/tests/unit/lms/services/roster_test.py +++ b/tests/unit/lms/services/roster_test.py @@ -218,6 +218,39 @@ def test_fetch_assignment_roster( assert roster[3].lms_user.lti_user_id == "USER_ID_INACTIVE" assert not roster[3].active + def test_fetch_assignment_roster_with_canvas_user_id( + self, svc, lti_names_roles_service, db_session, lti_role_service, assignment + ): + lti_names_roles_service.get_context_memberships.return_value = [ + { + "user_id": "USER_ID", + "roles": ["ROLE1"], + "status": "Active", + "message": [ + { + "https://purl.imsglobal.org/spec/lti/claim/custom": { + "canvas_user_id": "API_ID" + } + } + ], + }, + ] + lti_role_service.get_roles.return_value = [ + factories.LTIRole(value="ROLE1"), + ] + + svc.fetch_assignment_roster(assignment) + + roster = db_session.scalars( + select(AssignmentRoster) + .order_by(AssignmentRoster.lms_user_id) + .where(AssignmentRoster.assignment_id == assignment.id) + ).all() + + assert roster[0].assignment_id == assignment.id + assert roster[0].lms_user.lti_user_id == "USER_ID" + assert roster[0].lms_user.lms_api_user_id == "API_ID" + @pytest.mark.parametrize( "known_error", [