Skip to content

Commit

Permalink
Move and rename assignment to course_assignments_metrics
Browse files Browse the repository at this point in the history
This better reflects what the endpoint returns.
  • Loading branch information
marcospri committed Jul 4, 2024
1 parent c8b6c68 commit 0e907c9
Show file tree
Hide file tree
Showing 11 changed files with 145 additions and 147 deletions.
2 changes: 1 addition & 1 deletion lms/js_config_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class DashboardRoutes(TypedDict):
course: str
"""Fetch a single course by ID"""

course_assignment_stats: str
course_assignments_metrics: str

organization_courses: str

Expand Down
4 changes: 2 additions & 2 deletions lms/resources/_js_config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,8 @@ def enable_dashboard_mode(self) -> None:
"api.dashboard.students.metrics"
),
course=self._to_frontend_template("api.dashboard.course"),
course_assignment_stats=self._to_frontend_template(
"api.dashboard.course.assignments.stats"
course_assignments_metrics=self._to_frontend_template(
"api.dashboard.course.assignments.metrics"
),
organization_courses=self._to_frontend_template(
"api.dashboard.organizations.courses"
Expand Down
7 changes: 4 additions & 3 deletions lms/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,11 +269,12 @@ def includeme(config): # noqa: PLR0915
"api.dashboard.assignment", "/api/dashboard/assignments/{assignment_id}"
)
config.add_route("api.dashboard.course", "/api/dashboard/courses/{course_id}")

config.add_route("api.dashboard.assignments", "/api/dashboard/assignments")
config.add_route(
"api.dashboard.course.assignments.stats",
"/api/dashboard/courses/{course_id}/assignments/stats",
"api.dashboard.course.assignments.metrics",
"/api/dashboard/courses/{course_id}/assignments/metrics",
)
config.add_route("api.dashboard.assignments", "/api/dashboard/assignments")

config.add_route("api.dashboard.students", "/api/dashboard/students")
config.add_route(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default function CourseActivity() {
replaceURLParams(routes.course, { course_id: courseId }),
);
const assignments = useAPIFetch<AssignmentsResponse>(
replaceURLParams(routes.course_assignment_stats, {
replaceURLParams(routes.course_assignments_metrics, {
course_id: courseId,
}),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe('CourseActivity', () => {
dashboard: {
routes: {
course: '/api/dashboard/course/:course_id',
course_assignment_stats: '/api/dashboard/course/:course_id/stats',
course_assignments_metrics: '/api/dashboard/course/:course_id/stats',
},
},
};
Expand Down
2 changes: 1 addition & 1 deletion lms/static/scripts/frontend_apps/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ export type DashboardRoutes = {
/** Fetch a single course by ID */
course: string;
/** Fetch assignments stats for a single course by ID */
course_assignment_stats: string;
course_assignments_metrics: string;

/** Fetch courses stats */
organization_courses: string;
Expand Down
55 changes: 54 additions & 1 deletion lms/views/dashboard/api/assignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
from pyramid.view import view_config

from lms.js_config_types import (
AnnotationMetrics,
APIAssignment,
APIAssignments,
APICourse,
)
from lms.models import Assignment
from lms.models import Assignment, RoleScope, RoleType
from lms.security import Permissions
from lms.services.h_api import HAPI
from lms.views.dashboard.pagination import PaginationParametersMixin, get_page
Expand All @@ -25,6 +26,7 @@ def __init__(self, request) -> None:
self.h_api = request.find_service(HAPI)
self.assignment_service = request.find_service(name="assignment")
self.dashboard_service = request.find_service(name="dashboard")
self.course_service = request.find_service(name="course")

@view_config(
route_name="api.dashboard.assignments",
Expand Down Expand Up @@ -62,3 +64,54 @@ def assignment(self) -> APIAssignment:
title=assignment.title,
course=APICourse(id=assignment.course.id, title=assignment.course.lms_name),
)

@view_config(
route_name="api.dashboard.course.assignments.metrics",
request_method="GET",
renderer="json",
permission=Permissions.DASHBOARD_VIEW,
)
def course_assignments_metrics(self) -> APIAssignments:
course = self.dashboard_service.get_request_course(self.request)
course_students = self.course_service.get_members(
course, role_scope=RoleScope.COURSE, role_type=RoleType.LEARNER
)

stats = self.h_api.get_annotation_counts(
# Annotations in the course group and any children
[course.authority_provided_id]
+ [child.authority_provided_id for child in course.children],
group_by="assignment",
h_userids=[s.h_userid for s in course_students],
)
# Organize the H stats by assignment ID for quick access
stats_by_assignment = {s["assignment_id"]: s for s in stats}
assignments: list[APIAssignment] = []

# Same course for all these assignments
api_course = APICourse(id=course.id, title=course.lms_name)
for assignment in self.course_service.get_assignments(
course, h_userid=self.request.user.h_userid if self.request.user else None
):
if h_stats := stats_by_assignment.get(assignment.resource_link_id):
metrics = AnnotationMetrics(
annotations=h_stats["annotations"],
replies=h_stats["replies"],
last_activity=h_stats["last_activity"],
)
else:
# Assignment with no annos, zeroing the stats
metrics = AnnotationMetrics(
annotations=0, replies=0, last_activity=None
)

assignments.append(
APIAssignment(
id=assignment.id,
title=assignment.title,
course=api_course,
annotation_metrics=metrics,
)
)

return {"assignments": assignments}
62 changes: 2 additions & 60 deletions lms/views/dashboard/api/course.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
from pyramid.view import view_config

from lms.js_config_types import (
AnnotationMetrics,
APIAssignment,
APIAssignments,
APICourse,
APICourses,
CourseMetrics,
)
from lms.models import Course, RoleScope, RoleType
from lms.js_config_types import APICourse, APICourses, CourseMetrics
from lms.models import Course
from lms.security import Permissions
from lms.services.h_api import HAPI
from lms.services.organization import OrganizationService
Expand Down Expand Up @@ -98,54 +91,3 @@ def course(self) -> APICourse:
"id": course.id,
"title": course.lms_name,
}

@view_config(
route_name="api.dashboard.course.assignments.stats",
request_method="GET",
renderer="json",
permission=Permissions.DASHBOARD_VIEW,
)
def course_assignments(self) -> APIAssignments:
course = self.dashboard_service.get_request_course(self.request)
course_students = self.course_service.get_members(
course, role_scope=RoleScope.COURSE, role_type=RoleType.LEARNER
)

stats = self.h_api.get_annotation_counts(
# Annotations in the course group and any children
[course.authority_provided_id]
+ [child.authority_provided_id for child in course.children],
group_by="assignment",
h_userids=[s.h_userid for s in course_students],
)
# Organize the H stats by assignment ID for quick access
stats_by_assignment = {s["assignment_id"]: s for s in stats}
assignments: list[APIAssignment] = []

# Same course for all these assignments
api_course = APICourse(id=course.id, title=course.lms_name)
for assignment in self.course_service.get_assignments(
course, h_userid=self.request.user.h_userid if self.request.user else None
):
if h_stats := stats_by_assignment.get(assignment.resource_link_id):
metrics = AnnotationMetrics(
annotations=h_stats["annotations"],
replies=h_stats["replies"],
last_activity=h_stats["last_activity"],
)
else:
# Assignment with no annos, zeroing the stats
metrics = AnnotationMetrics(
annotations=0, replies=0, last_activity=None
)

assignments.append(
APIAssignment(
id=assignment.id,
title=assignment.title,
course=api_course,
annotation_metrics=metrics,
)
)

return {"assignments": assignments}
2 changes: 1 addition & 1 deletion tests/unit/lms/resources/_js_config/__init___test.py
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,7 @@ def test_it(self, js_config, lti_user):
"assignment": "/api/dashboard/assignments/:assignment_id",
"students_metrics": "/api/dashboard/students/:assignment_id/metrics",
"course": "/api/dashboard/courses/:course_id",
"course_assignment_stats": "/api/dashboard/courses/:course_id/assignments/stats",
"course_assignments_metrics": "/api/dashboard/courses/:course_id/assignments/metrics",
"organization_courses": "/api/dashboard/organizations/:organization_public_id/courses",
"courses": "/api/dashboard/courses",
"assignments": "/api/dashboard/assignments",
Expand Down
79 changes: 78 additions & 1 deletion tests/unit/lms/views/dashboard/api/assignment_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
from lms.views.dashboard.api.assignment import AssignmentViews
from tests import factories

pytestmark = pytest.mark.usefixtures("h_api", "assignment_service", "dashboard_service")
pytestmark = pytest.mark.usefixtures(
"h_api", "assignment_service", "dashboard_service", "course_service"
)


class TestAssignmentViews:
Expand Down Expand Up @@ -51,6 +53,81 @@ def test_assignment(
"course": {"id": course.id, "title": course.lms_name},
}

def test_course_assignments(
self,
views,
pyramid_request,
course_service,
h_api,
db_session,
dashboard_service,
):
pyramid_request.matchdict["course_id"] = sentinel.id
course = factories.Course()
section = factories.CanvasSection(parent=course)
dashboard_service.get_request_course.return_value = course

assignment = factories.Assignment()
assignment_with_no_annos = factories.Assignment()

course_service.get_assignments.return_value = [
assignment,
assignment_with_no_annos,
]
course_service.get_members.return_value = factories.User.create_batch(5)

db_session.flush()

stats = [
{
"assignment_id": assignment.resource_link_id,
"annotations": sentinel.annotations,
"replies": sentinel.replies,
"userid": "TEACHER",
"last_activity": sentinel.last_activity,
},
]

h_api.get_annotation_counts.return_value = stats

response = views.course_assignments_metrics()

h_api.get_annotation_counts.assert_called_once_with(
[course.authority_provided_id, section.authority_provided_id],
group_by="assignment",
h_userids=[u.h_userid for u in course_service.get_members.return_value],
)
assert response == {
"assignments": [
{
"id": assignment.id,
"title": assignment.title,
"course": {
"id": course.id,
"title": course.lms_name,
},
"annotation_metrics": {
"annotations": sentinel.annotations,
"replies": sentinel.replies,
"last_activity": sentinel.last_activity,
},
},
{
"id": assignment_with_no_annos.id,
"title": assignment_with_no_annos.title,
"course": {
"id": course.id,
"title": course.lms_name,
},
"annotation_metrics": {
"annotations": 0,
"replies": 0,
"last_activity": None,
},
},
]
}

@pytest.fixture
def views(self, pyramid_request):
return AssignmentViews(pyramid_request)
Expand Down
75 changes: 0 additions & 75 deletions tests/unit/lms/views/dashboard/api/course_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,81 +81,6 @@ def test_course(self, views, pyramid_request, dashboard_service):
"title": course.lms_name,
}

def test_course_assignments(
self,
views,
pyramid_request,
course_service,
h_api,
db_session,
dashboard_service,
):
pyramid_request.matchdict["course_id"] = sentinel.id
course = factories.Course()
section = factories.CanvasSection(parent=course)
dashboard_service.get_request_course.return_value = course

assignment = factories.Assignment()
assignment_with_no_annos = factories.Assignment()

course_service.get_assignments.return_value = [
assignment,
assignment_with_no_annos,
]
course_service.get_members.return_value = factories.User.create_batch(5)

db_session.flush()

stats = [
{
"assignment_id": assignment.resource_link_id,
"annotations": sentinel.annotations,
"replies": sentinel.replies,
"userid": "TEACHER",
"last_activity": sentinel.last_activity,
},
]

h_api.get_annotation_counts.return_value = stats

response = views.course_assignments()

h_api.get_annotation_counts.assert_called_once_with(
[course.authority_provided_id, section.authority_provided_id],
group_by="assignment",
h_userids=[u.h_userid for u in course_service.get_members.return_value],
)
assert response == {
"assignments": [
{
"id": assignment.id,
"title": assignment.title,
"course": {
"id": course.id,
"title": course.lms_name,
},
"annotation_metrics": {
"annotations": sentinel.annotations,
"replies": sentinel.replies,
"last_activity": sentinel.last_activity,
},
},
{
"id": assignment_with_no_annos.id,
"title": assignment_with_no_annos.title,
"course": {
"id": course.id,
"title": course.lms_name,
},
"annotation_metrics": {
"annotations": 0,
"replies": 0,
"last_activity": None,
},
},
]
}

@pytest.fixture
def views(self, pyramid_request):
return CourseViews(pyramid_request)
Expand Down

0 comments on commit 0e907c9

Please sign in to comment.