-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New dahboard endpoint to sync grades of a given async
- Loading branch information
Showing
6 changed files
with
236 additions
and
10 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import logging | ||
from datetime import datetime | ||
|
||
from marshmallow import Schema, fields | ||
from pyramid.view import view_config | ||
from sqlalchemy import select | ||
|
||
from lms.models import ( | ||
ApplicationInstance, | ||
Grouping, | ||
LMSUser, | ||
LTIRegistration, | ||
) | ||
from lms.security import Permissions | ||
from lms.services import LTIAHTTPService | ||
from lms.services.dashboard import DashboardService | ||
from lms.services.lti_grading.factory import LTI13GradingService | ||
from lms.validation._base import JSONPyramidRequestSchema | ||
|
||
LOG = logging.getLogger(__name__) | ||
|
||
|
||
class _GradeSchema(Schema): | ||
h_userid = fields.Str(required=True) | ||
grade = fields.Float(required=True) | ||
|
||
|
||
class AutoGradeSyncSchema(JSONPyramidRequestSchema): | ||
grades = fields.List(fields.Nested(_GradeSchema), required=True) | ||
|
||
|
||
class DashboardGradingViews: | ||
def __init__(self, request) -> None: | ||
self.request = request | ||
self.db = request.db | ||
self.dashboard_service: DashboardService = request.find_service( | ||
name="dashboard" | ||
) | ||
|
||
@view_config( | ||
route_name="api.dashboard.assignments.grading.sync", | ||
request_method="POST", | ||
renderer="json", | ||
permission=Permissions.GRADE_ASSIGNMENT, | ||
schema=AutoGradeSyncSchema, | ||
) | ||
def auto_grading_sync(self): | ||
assignment = self.dashboard_service.get_request_assignment(self.request) | ||
assert assignment.lis_outcome_service_url, "Assignment without grading URL" | ||
lti_registration = self.db.scalars( | ||
select(LTIRegistration) | ||
.join(ApplicationInstance) | ||
.join(Grouping) | ||
.where(Grouping.id == assignment.course_id) | ||
.order_by(LTIRegistration.updated.desc()) | ||
).first() | ||
assert lti_registration, "No LTI registraion for LTI1.3 assignment" | ||
|
||
sync_h_user_ids = [g["h_userid"] for g in self.request.parsed_params["grades"]] | ||
|
||
sync_lms_users = self.db.execute( | ||
select(LMSUser.h_userid, LMSUser.lti_v13_user_id).where( | ||
LMSUser.h_userid.in_(sync_h_user_ids) | ||
) | ||
).all() | ||
# Organize the data in a dict h_userid -> lti_user_id for easier access | ||
sync_lms_users_by_h_userid = {r[0]: r[1] for r in sync_lms_users} | ||
|
||
grading_service = LTI13GradingService( | ||
ltia_service=self.request.find_service(LTIAHTTPService), | ||
line_item_url=None, | ||
line_item_container_url=None, | ||
product_family=None, # type: ignore | ||
misc_plugin=None, # type: ignore | ||
lti_registration=None, # type: ignore | ||
) | ||
# Use the same timestamp for all grades of the same sync | ||
grade_sync_time_stamp = datetime.now() | ||
for grade in self.request.parsed_params["grades"]: | ||
grading_service.sync_grade( | ||
lti_registration, | ||
assignment.lis_outcome_service_url, | ||
grade_sync_time_stamp, | ||
sync_lms_users_by_h_userid[grade["h_userid"]], | ||
grade["grade"], | ||
) |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
from datetime import datetime | ||
|
||
import pytest | ||
from freezegun import freeze_time | ||
|
||
from lms.views.dashboard.api.grading import DashboardGradingViews | ||
from tests import factories | ||
|
||
|
||
@pytest.mark.usefixtures("dashboard_service", "ltia_http_service") | ||
class TestDashboardGradingViews: | ||
@freeze_time("2022-06-21 12:00:00") | ||
def test_auto_grading_sync( | ||
self, | ||
lti_v13_application_instance, | ||
pyramid_request, | ||
views, | ||
DashboardGradingView, | ||
ltia_http_service, | ||
dashboard_service, | ||
assignment, | ||
): | ||
dashboard_service.get_request_assignment.return_value = assignment | ||
|
||
views.auto_grading_sync() | ||
|
||
dashboard_service.get_request_assignment.assert_called_once_with( | ||
pyramid_request | ||
) | ||
DashboardGradingView.assert_called_once_with( | ||
ltia_service=ltia_http_service, | ||
line_item_url=None, | ||
line_item_container_url=None, | ||
product_family=None, | ||
misc_plugin=None, | ||
lti_registration=None, | ||
) | ||
DashboardGradingView.return_value.sync_grade.assert_called_once_with( | ||
lti_v13_application_instance.lti_registration, | ||
"LIS_OUTCOME_SERVICE_URL", | ||
datetime(2022, 6, 21, 12, 0, 0), | ||
"LTI_V13_USER_ID", | ||
1, | ||
) | ||
|
||
@pytest.fixture | ||
def assignment(self, lti_v13_application_instance, db_session): | ||
course = factories.Course(application_instance=lti_v13_application_instance) | ||
assignment = factories.Assignment( | ||
lis_outcome_service_url="LIS_OUTCOME_SERVICE_URL", course=course | ||
) | ||
db_session.flush() | ||
return assignment | ||
|
||
@pytest.fixture | ||
def lms_user(self): | ||
return factories.LMSUser(lti_v13_user_id="LTI_V13_USER_ID") | ||
|
||
@pytest.fixture | ||
def pyramid_request(self, pyramid_request, lms_user): | ||
pyramid_request.parsed_params = { | ||
"grades": [{"h_userid": lms_user.h_userid, "grade": 1}] | ||
} | ||
return pyramid_request | ||
|
||
@pytest.fixture | ||
def views(self, pyramid_request): | ||
return DashboardGradingViews(pyramid_request) | ||
|
||
@pytest.fixture | ||
def DashboardGradingView(self, patch): | ||
return patch("lms.views.dashboard.api.grading.LTI13GradingService") |