Skip to content

Commit

Permalink
Allow direct access to course level dashboard from the admin pages
Browse files Browse the repository at this point in the history
  • Loading branch information
marcospri committed May 30, 2024
1 parent 59e9841 commit 80ea7a9
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 3 deletions.
1 change: 1 addition & 0 deletions lms/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ def includeme(config): # noqa: PLR0915

config.add_route("admin.courses", "/admin/courses")
config.add_route("admin.course", "/admin/course/{id_}")
config.add_route("admin.courses.dashboard", "/admin/courses/{id_}/dashboard")

config.add_route("admin.assignment", "/admin/assignment/{id_}")
config.add_route("admin.assignment.dashboard", "/admin/assignment/{id_}/dashboard")
Expand Down
4 changes: 4 additions & 0 deletions lms/templates/admin/course/show.html.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
{% block content %}
<div>
<fieldset>
<div class="block has-text-right">
<a class="button is-primary" target="_blank" href="{{ request.route_url('admin.courses.dashboard', id_=course.id) }}">Open instructor dashboard</a>
</div>

{{ macros.disabled_text_field("ID", course.id) }}
{{ macros.disabled_text_field("Name", course.name) }}
{{ macros.disabled_text_field("H ID", course.authority_provided_id) }}
Expand Down
41 changes: 39 additions & 2 deletions lms/views/admin/course.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from dataclasses import asdict

from marshmallow import validate
from pyramid.httpexceptions import HTTPNotFound
from pyramid.httpexceptions import HTTPFound, HTTPNotFound
from pyramid.view import view_config, view_defaults
from webargs import fields

from lms.models import Course
from lms.events import AuditTrailEvent, ModelChange
from lms.models import Course, EventType
from lms.models.public_id import InvalidPublicId
from lms.security import Permissions
from lms.services import OrganizationService
Expand Down Expand Up @@ -53,6 +56,40 @@ def show(self):

return {"course": course}

@view_config(
route_name="admin.courses.dashboard",
request_method="GET",
permission=Permissions.STAFF,
)
def course_dashboard(self):
course_id = self.request.matchdict["id_"]
course = self._get_course_or_404(course_id)
self.request.registry.notify(
AuditTrailEvent(
request=self.request,
type=EventType.Type.AUDIT_TRAIL,
data=asdict(
ModelChange(
model=Course.__name__,
id=course.id,
action="view_dashboard",
source="admin_pages",
userid=self.request.identity.userid,
changes={},
)
),
)
)

response = HTTPFound(
location=self.request.route_url(
"dashboard.course",
public_id=course.application_instance.organization._public_id, # noqa: SLF001
course_id=course.id,
),
)
return response

@view_config(
route_name="admin.courses",
request_method="POST",
Expand Down
48 changes: 47 additions & 1 deletion tests/unit/lms/views/admin/course_test.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from unittest.mock import sentinel

import pytest
from pyramid.httpexceptions import HTTPNotFound
from h_matchers import Any
from pyramid.httpexceptions import HTTPFound, HTTPNotFound

from lms.models import EventType
from lms.models.public_id import InvalidPublicId
from lms.views.admin.course import AdminCourseViews
from tests import factories


@pytest.mark.usefixtures("course_service", "organization_service")
Expand All @@ -27,6 +30,39 @@ def test_show_not_found(self, pyramid_request, course_service, views):
with pytest.raises(HTTPNotFound):
views.show()

def test_course_dashboard(
self,
pyramid_request,
course_service,
views,
AuditTrailEvent,
organization,
course,
):
pyramid_request.matchdict["id_"] = sentinel.id
course_service.get_by_id.return_value = course

response = views.course_dashboard()

AuditTrailEvent.assert_called_once_with(
request=pyramid_request,
type=EventType.Type.AUDIT_TRAIL,
data={
"action": "view_dashboard",
"id": course.id,
"model": "Course",
"source": "admin_pages",
"userid": "TEST_USER_ID",
"changes": {},
},
)
pyramid_request.registry.notify.has_call_with(AuditTrailEvent.return_value)
assert response == Any.instance_of(HTTPFound).with_attrs(
{
"location": f"http://example.com/dashboard/organizations/{organization._public_id}/courses/{course.id}", # noqa: SLF001
}
)

def test_search(self, pyramid_request, course_service, views):
pyramid_request.params["context_id"] = " PUBLIC_ID "
pyramid_request.params["name"] = " NAME "
Expand Down Expand Up @@ -83,6 +119,16 @@ def test_blank_search(self, views, course_service):
id_="", name="", context_id="DUMMY-CONTEXT-ID", h_id="", organization=None
)

@pytest.fixture
def course(self, application_instance, db_session):
course = factories.Course(application_instance=application_instance)
db_session.flush() # Give the DB objects IDs
return course

@pytest.fixture
def AuditTrailEvent(self, patch):
return patch("lms.views.admin.course.AuditTrailEvent")

@pytest.fixture
def views(self, pyramid_request):
return AdminCourseViews(pyramid_request)

0 comments on commit 80ea7a9

Please sign in to comment.