Skip to content

Commit 5cdd913

Browse files
refactor: move more functionality to Canvas plugin (#387)
* refactor: move more functionality to Canvas plugin
1 parent f666e70 commit 5cdd913

File tree

8 files changed

+122
-24
lines changed

8 files changed

+122
-24
lines changed

src/ol_openedx_canvas_integration/BUILD

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ python_distribution(
1212
],
1313
provides=setup_py(
1414
name="ol-openedx-canvas-integration",
15-
version="0.3.0",
15+
version="0.4.0",
1616
description="An Open edX plugin to add canvas integration support",
1717
license="BSD-3-Clause",
1818
entry_points={

src/ol_openedx_canvas_integration/README.rst

+23-6
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,15 @@ We had to make some changes to edx-platform itself in order to add the "Canvas"
99

1010
The ``edx-platform`` branch/tag you're using must include one of the below commit for ``ol-openedx-canvas-integration`` plugin to work properly:
1111

12-
**For "Nutmeg" or more recent release of edX platform, you should cherry-pick below commit:**
12+
**For "Sumac" or more recent release of edX platform, you should cherry-pick below commit:**
13+
14+
TBA - Will be added when we merge the PR in edx-platform
15+
16+
**For "Quince" to "Redwood" release of edX platform, you should cherry-pick below commit:**
17+
18+
https://github.com/mitodl/edx-platform/commit/7a2edd5d29ead6845cb33d2001746207cf696383
19+
20+
**For "Nutmeg" to "Palm" release of edX platform, you should cherry-pick below commit:**
1321

1422
- https://github.com/mitodl/edx-platform/pull/297/commits/c354a99bd14393b89a780692d07b6e70b586d172
1523

@@ -20,10 +28,18 @@ The ``edx-platform`` branch/tag you're using must include one of the below commi
2028

2129
Version Compatibility
2230
---------------------
23-
**For "Nutmeg" or more recent release of edX platform**
2431

25-
Use ``0.2.4`` or a above version of this plugin
32+
**For "Sumac" or more recent release of edX platform**
2633

34+
Use ``0.4.0`` or a above version of this plugin
35+
36+
**For "Quince" to "Redwood" release of edX platform**
37+
38+
Use ``0.3.0`` or a above version of this plugin
39+
40+
**For "Nutmeg" to "Palm" release of edX platform**
41+
42+
Use ``0.2.4`` or a above version of this plugin
2743

2844
**For releases prior to "Nutmeg"**
2945

@@ -49,8 +65,8 @@ You can install this plugin into any Open edX instance by using any of the follo
4965
Follow these steps in a terminal on your machine:
5066

5167
1. Navigate to ``open-edx-plugins`` directory
52-
2. If you haven't done so already, run ``./pants build``
53-
3. Run ``./pants package ::``. This will create a "dist" directory inside "open-edx-plugins" directory with ".whl" & ".tar.gz" format packages for all the "ol_openedx_*" plugins in "open-edx-plugins/src")
68+
2. If you haven't done so already, run ``pants build``
69+
3. Run ``pants package ::``. This will create a "dist" directory inside "open-edx-plugins" directory with ".whl" & ".tar.gz" format packages for all the "ol_openedx_*" plugins in "open-edx-plugins/src")
5470
4. Move/copy any of the ".whl" or ".tar.gz" files for this plugin that were generated in the above step to the machine/container running Open edX (NOTE: If running devstack via Docker, you can use ``docker cp`` to copy these files into your LMS container)
5571
5. Run a shell in the machine/container running Open edX, and install this plugin using pip
5672

@@ -71,7 +87,8 @@ Add the following configuration values to the config file in Open edX. For any r
7187

7288
1) Open your course in Studio.
7389
2) Navigate to "Advanced Settings".
74-
3) Add a ``canvas_course_id`` value. This should be the id of a course that exists on Canvas. (NOTE: Canvas tab would only be visible if this value is set)
90+
3) Enable other course settings by enabling ``ENABLE_OTHER_COURSE_SETTINGS`` feature flag in CMS
91+
4) Open course advanced settings in Open edX CMS, Add a dictionary in ``{"canvas_id": <canvas_course_id>}``. The ``canvas_course_id`` should be the id of a course that exists on Canvas. (NOTE: Canvas tab would only be visible if this value is set)
7592

7693

7794
How To Use

src/ol_openedx_canvas_integration/api.py

+20-4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
create_assignment_payload,
1515
update_grade_payload_kv,
1616
)
17+
from ol_openedx_canvas_integration.constants import COURSE_KEY_ID_EMPTY
18+
from ol_openedx_canvas_integration.utils import get_canvas_course_id
1719
from opaque_keys.edx.locator import CourseLocator
1820

1921
log = logging.getLogger(__name__)
@@ -156,13 +158,20 @@ def sync_canvas_enrollments(course_key, canvas_course_id, unenroll_current):
156158
canvas_course_id (int): The canvas course id
157159
unenroll_current (bool): If true, unenroll existing students if not staff
158160
"""
159-
client = CanvasClient(canvas_course_id)
160-
emails_to_enroll = client.list_canvas_enrollments()
161-
users_to_unenroll = []
161+
if not course_key:
162+
raise Exception(COURSE_KEY_ID_EMPTY) # noqa: TRY002
163+
164+
if not canvas_course_id:
165+
msg = f"No canvas_course_id set for course: {course_key}"
166+
raise Exception(msg) # noqa: TRY002
162167

163168
course_key = CourseLocator.from_string(course_key)
164169
course = get_course_by_id(course_key)
165170

171+
client = CanvasClient(canvas_course_id)
172+
emails_to_enroll = client.list_canvas_enrollments()
173+
users_to_unenroll = []
174+
166175
if unenroll_current:
167176
enrolled_user_dict = {
168177
user.email: user for user in get_enrolled_non_staff_users(course)
@@ -197,7 +206,14 @@ def push_edx_grades_to_canvas(course):
197206
Returns:
198207
dict: A dictionary with some information about the success/failure of the updates
199208
""" # noqa: E501
200-
canvas_course_id = course.canvas_course_id
209+
if not course:
210+
raise Exception(COURSE_KEY_ID_EMPTY) # noqa: TRY002
211+
212+
canvas_course_id = get_canvas_course_id(course)
213+
if not canvas_course_id:
214+
msg = f"No canvas_course_id set for course: {course.id}"
215+
raise Exception(msg) # noqa: TRY002
216+
201217
client = CanvasClient(canvas_course_id=canvas_course_id)
202218
existing_assignment_dict = client.get_assignments_by_int_id()
203219
subsection_block_user_grades = get_subsection_block_user_grades(course)

src/ol_openedx_canvas_integration/constants.py

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
TASK_TYPE_SYNC_CANVAS_ENROLLMENTS,
66
TASK_TYPE_PUSH_EDX_GRADES_TO_CANVAS,
77
]
8+
COURSE_KEY_ID_EMPTY = "Course Key/ID is empty."

src/ol_openedx_canvas_integration/context_api.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import pkg_resources
66
from django.urls import reverse
77
from django.utils.translation import gettext as _
8+
from ol_openedx_canvas_integration.utils import get_canvas_course_id
89
from web_fragments.fragment import Fragment
910

1011

@@ -29,7 +30,7 @@ def plugin_context(context):
2930

3031
# Don't add Canvas tab is the Instructor Dashboard if it doesn't have any associated
3132
# canvas_course_id set from Canvas Service
32-
if not course.canvas_course_id:
33+
if not get_canvas_course_id(course):
3334
return None
3435

3536
fragment = Fragment()

src/ol_openedx_canvas_integration/task_helpers.py

+23
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@
22
Helper functions for canvas integration tasks
33
"""
44

5+
import datetime
56
from time import time
67

8+
import pytz
79
from lms.djangoapps.courseware.courses import get_course_by_id
10+
from lms.djangoapps.instructor_task.api import get_running_instructor_tasks
11+
from lms.djangoapps.instructor_task.models import InstructorTask
812
from lms.djangoapps.instructor_task.tasks_helper.runner import TaskProgress
913
from ol_openedx_canvas_integration import api
14+
from ol_openedx_canvas_integration.constants import CANVAS_TASK_TYPES
1015

1116

1217
def sync_canvas_enrollments(
@@ -46,3 +51,21 @@ def push_edx_grades_to_canvas(
4651
return task_progress.update_task_state(
4752
extra_meta={"step": "Done", "results": results}
4853
)
54+
55+
56+
def get_filtered_instructor_tasks(course_id, user):
57+
"""
58+
Return a filtered query of InstructorTasks based on the course, user, and desired
59+
task types
60+
"""
61+
instructor_tasks = get_running_instructor_tasks(course_id)
62+
now = datetime.datetime.now(pytz.utc)
63+
filtered_tasks = InstructorTask.objects.filter(
64+
course_id=course_id,
65+
task_type__in=CANVAS_TASK_TYPES,
66+
updated__lte=now,
67+
updated__gte=now - datetime.timedelta(days=2),
68+
requester=user,
69+
).order_by("-updated")
70+
71+
return (instructor_tasks | filtered_tasks).distinct()[0:3]
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"""Utilities for Canvas plugin"""
2+
3+
4+
def get_canvas_course_id(course=None):
5+
"""Get the course Id from the course settings"""
6+
return course.other_course_settings.get("canvas_id") if course else None
7+
8+
9+
def get_task_output_formatted_message(task_output):
10+
"""Take the edX task output and format a message for table display on task result"""
11+
# this reports on actions for a course as a whole
12+
results = task_output.get("results", {})
13+
assignments_count = results.get("assignments", 0)
14+
grades_count = results.get("grades", 0)
15+
16+
return (
17+
f"{grades_count} grades and {assignments_count} assignments updated or created"
18+
)

src/ol_openedx_canvas_integration/views.py

+34-12
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
from lms.djangoapps.instructor_task.api_helper import AlreadyRunningError
1717
from ol_openedx_canvas_integration import tasks
1818
from ol_openedx_canvas_integration.client import CanvasClient
19+
from ol_openedx_canvas_integration.constants import COURSE_KEY_ID_EMPTY
20+
from ol_openedx_canvas_integration.utils import get_canvas_course_id
1921
from opaque_keys.edx.locator import CourseLocator
2022

2123
log = logging.getLogger(__name__)
@@ -44,13 +46,18 @@ def list_canvas_enrollments(request, course_id): # noqa: ARG001
4446
"""
4547
Fetch enrollees for a course in canvas and list them
4648
"""
49+
if not course_id:
50+
raise Exception(COURSE_KEY_ID_EMPTY) # noqa: TRY002
51+
4752
course_key = CourseLocator.from_string(course_id)
4853
course = get_course_by_id(course_key)
49-
if not course.canvas_course_id:
50-
msg = f"No canvas_course_id set for course {course_id}"
54+
canvas_course_id = get_canvas_course_id(course)
55+
56+
if not canvas_course_id:
57+
msg = f"No canvas_course_id set for course: {course_id}"
5158
raise Exception(msg) # noqa: TRY002
5259

53-
client = CanvasClient(canvas_course_id=course.canvas_course_id)
60+
client = CanvasClient(canvas_course_id=canvas_course_id)
5461
# WARNING: this will block the web thread
5562
enrollment_dict = client.list_canvas_enrollments()
5663

@@ -73,15 +80,16 @@ def add_canvas_enrollments(request, course_id):
7380
unenroll_current = request.POST.get("unenroll_current", "").lower() == "true"
7481
course_key = CourseLocator.from_string(course_id)
7582
course = get_course_by_id(course_key)
76-
if not course.canvas_course_id:
83+
canvas_course_id = get_canvas_course_id(course)
84+
if not canvas_course_id:
7785
msg = f"No canvas_course_id set for course {course_id}"
7886
raise Exception(msg) # noqa: TRY002
7987

8088
try:
8189
tasks.run_sync_canvas_enrollments(
8290
request=request,
8391
course_key=course_id,
84-
canvas_course_id=course.canvas_course_id,
92+
canvas_course_id=canvas_course_id,
8593
unenroll_current=unenroll_current,
8694
)
8795
log.info("Syncing canvas enrollments for course %s", course_id)
@@ -99,12 +107,17 @@ def add_canvas_enrollments(request, course_id):
99107
@require_course_permission(permissions.OVERRIDE_GRADES)
100108
def list_canvas_assignments(request, course_id): # noqa: ARG001
101109
"""List Canvas assignments"""
110+
if not course_id:
111+
raise Exception(COURSE_KEY_ID_EMPTY) # noqa: TRY002
112+
102113
course_key = CourseLocator.from_string(course_id)
103114
course = get_course_by_id(course_key)
104-
client = CanvasClient(canvas_course_id=course.canvas_course_id)
105-
if not course.canvas_course_id:
106-
msg = f"No canvas_course_id set for course {course_id}"
115+
canvas_course_id = get_canvas_course_id(course)
116+
if not canvas_course_id:
117+
msg = f"No canvas_course_id set for course: {course_id}"
107118
raise Exception(msg) # noqa: TRY002
119+
120+
client = CanvasClient(canvas_course_id=canvas_course_id)
108121
return JsonResponse(client.list_canvas_assignments())
109122

110123

@@ -113,13 +126,18 @@ def list_canvas_assignments(request, course_id): # noqa: ARG001
113126
@require_course_permission(permissions.OVERRIDE_GRADES)
114127
def list_canvas_grades(request, course_id):
115128
"""List grades"""
129+
if not course_id:
130+
raise Exception(COURSE_KEY_ID_EMPTY) # noqa: TRY002
131+
116132
assignment_id = int(request.GET.get("assignment_id"))
117133
course_key = CourseLocator.from_string(course_id)
118134
course = get_course_by_id(course_key)
119-
client = CanvasClient(canvas_course_id=course.canvas_course_id)
120-
if not course.canvas_course_id:
135+
canvas_course_id = get_canvas_course_id(course)
136+
if not canvas_course_id:
121137
msg = f"No canvas_course_id set for course {course_id}"
122138
raise Exception(msg) # noqa: TRY002
139+
140+
client = CanvasClient(canvas_course_id=canvas_course_id)
123141
return JsonResponse(client.list_canvas_grades(assignment_id=assignment_id))
124142

125143

@@ -130,10 +148,14 @@ def list_canvas_grades(request, course_id):
130148
@require_course_permission(permissions.OVERRIDE_GRADES)
131149
def push_edx_grades(request, course_id):
132150
"""Push user grades for all graded items in edX to Canvas"""
151+
if not course_id:
152+
raise Exception(COURSE_KEY_ID_EMPTY) # noqa: TRY002
153+
133154
course_key = CourseLocator.from_string(course_id)
134155
course = get_course_by_id(course_key)
135-
if not course.canvas_course_id:
136-
msg = f"No canvas_course_id set for course {course_id}"
156+
canvas_course_id = get_canvas_course_id(course)
157+
if not canvas_course_id:
158+
msg = f"No canvas_course_id set for course: {course_id}"
137159
raise Exception(msg) # noqa: TRY002
138160
try:
139161
tasks.run_push_edx_grades_to_canvas(

0 commit comments

Comments
 (0)