Skip to content

Commit 049c698

Browse files
committed
feat: add support for optional Canvas due date syncing
Implement logic to optionally sync assignment due dates from Open edX to Canvas. Key changes include: - Added `get_use_canvas_due_dates` utility to check course settings. - Updated `create_assignment_payload` to conditionally include due dates based on the new setting. - Modified `diff_assignments` and sync workflows to utilize the `use_canvas_due_dates` flag. - Refactored assignment sync logic to support toggling due date synchronization.
1 parent 05d9042 commit 049c698

5 files changed

Lines changed: 31 additions & 18 deletions

File tree

src/edx_sysadmin/tests/test_tasks.py

Whitespace-only changes.

src/ol_openedx_canvas_integration/ol_openedx_canvas_integration/api.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
update_grade_payload_kv,
1818
)
1919
from ol_openedx_canvas_integration.constants import COURSE_KEY_ID_EMPTY
20-
from ol_openedx_canvas_integration.utils import get_canvas_course_id
20+
from ol_openedx_canvas_integration.utils import get_canvas_course_id, get_use_canvas_due_dates
2121

2222
log = logging.getLogger(__name__)
2323

@@ -227,6 +227,7 @@ def push_edx_grades_to_canvas(course):
227227
if not canvas_course_id:
228228
msg = f"No canvas_course_id set for course: {course.id}"
229229
raise Exception(msg) # noqa: TRY002
230+
use_canvas_due_dates = get_use_canvas_due_dates(course)
230231

231232
client = CanvasClient(canvas_course_id=canvas_course_id)
232233
existing_assignment_dict = client.get_canvas_assignments()
@@ -240,7 +241,7 @@ def push_edx_grades_to_canvas(course):
240241
)
241242
created_assignments = {
242243
subsection_block: client.create_canvas_assignment(
243-
create_assignment_payload(subsection_block)
244+
create_assignment_payload(subsection_block, use_canvas_due_dates)
244245
)
245246
for subsection_block in new_assignment_blocks
246247
}

src/ol_openedx_canvas_integration/ol_openedx_canvas_integration/client.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -247,32 +247,39 @@ def update_assignment_grades(self, canvas_assignment_id, payload):
247247
)
248248

249249

250-
def create_assignment_payload(subsection_block):
250+
def create_assignment_payload(subsection_block, use_canvas_dates=False):
251251
"""
252252
Create a Canvas assignment dict matching a subsection block on edX
253253
254254
Args:
255255
subsection_block (openedx.core.djangoapps.content.block_structure.block_structure.BlockData):
256256
The block data for the graded assignment/exam (in the structure of a course, this unit is a subsection)
257+
use_canvas_dates (bool): Whether to use the due dates from canvas instead of Open edX
257258
258259
Returns:
259260
dict:
260261
Assignment payload to be sent to Canvas to create or update the assignment
261262
""" # noqa: E501
263+
due_date_dict = {
264+
"due_at": (
265+
None
266+
if not subsection_block.fields.get("due")
267+
# The internal API gives us a TZ-naive datetime for the due date, but Studio indicates that # noqa: E501
268+
# the user should enter a UTC datetime for the due date. Coerce this to UTC before creating the # noqa: E501
269+
# string representation.
270+
else subsection_block.fields["due"].astimezone(pytz.UTC).isoformat()
271+
),
272+
}
273+
# If we're using canvas dates, don't set the due dats from Open edX
274+
if use_canvas_dates:
275+
due_date_dict = {}
262276
return {
263277
"assignment": {
264278
"name": subsection_block.display_name,
265279
"integration_id": str(subsection_block.location),
266280
"grading_type": "percent",
267281
"points_possible": DEFAULT_ASSIGNMENT_POINTS,
268-
"due_at": (
269-
None
270-
if not subsection_block.fields.get("due")
271-
# The internal API gives us a TZ-naive datetime for the due date, but Studio indicates that # noqa: E501
272-
# the user should enter a UTC datetime for the due date. Coerce this to UTC before creating the # noqa: E501
273-
# string representation.
274-
else subsection_block.fields["due"].astimezone(pytz.UTC).isoformat()
275-
),
282+
**due_date_dict,
276283
"submission_types": ["none"],
277284
"published": False,
278285
}

src/ol_openedx_canvas_integration/ol_openedx_canvas_integration/cms_tasks.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,20 @@
2424

2525
from ol_openedx_canvas_integration.api import course_graded_items
2626
from ol_openedx_canvas_integration.client import CanvasClient, create_assignment_payload
27-
from ol_openedx_canvas_integration.utils import get_canvas_course_id
27+
from ol_openedx_canvas_integration.utils import get_use_canvas_due_dates, get_canvas_course_id
2828

2929
logger = logging.getLogger(__name__)
3030
TASK_LOG = logging.getLogger("edx.celery.task")
3131

3232

33-
def diff_assignments(openedx_assignments, canvas_assignments_map):
33+
def diff_assignments(openedx_assignments, canvas_assignments_map, use_canvas_dates=False):
3434
"""Perform a diff between the assignments in Canvas and Open edX.
3535
3636
Args:
3737
openedx_assignments (list): List of Open edX subsection objects
3838
canvas_assignments_map (dict): Map of assignment integration IDs to Canvas
3939
assignment IDs
40+
use_canvas_dates (bool): Whether to sync the due dates of the assignments with the due dates of the subsections
4041
4142
Returns:
4243
dict: The diff between the assignments with the following structure:
@@ -49,7 +50,7 @@ def diff_assignments(openedx_assignments, canvas_assignments_map):
4950
assignment_diff = {"add": [], "update": {}, "delete": []}
5051
for subsection in openedx_assignments:
5152
integration_id = str(subsection.location)
52-
payload = create_assignment_payload(subsection)
53+
payload = create_assignment_payload(subsection, use_canvas_dates=use_canvas_dates)
5354
canvas_assignment = canvas_assignments_map.pop(integration_id, None)
5455
if canvas_assignment:
5556
# if the assignment exists in Canvas, remove from the map to indicate
@@ -156,6 +157,7 @@ def sync_course_assignments_with_canvas(course_id):
156157
course_key = CourseLocator.from_string(course_id)
157158
course = get_course_by_id(course_key)
158159
canvas_course_id = get_canvas_course_id(course)
160+
use_canvas_due_dates = get_use_canvas_due_dates(course)
159161

160162
if not canvas_course_id:
161163
logger.info(
@@ -170,7 +172,7 @@ def sync_course_assignments_with_canvas(course_id):
170172
canvas = CanvasClient(canvas_course_id=canvas_course_id)
171173
canvas_assignments = canvas.get_canvas_assignments()
172174

173-
operations_map = diff_assignments(openedx_assignments, canvas_assignments)
175+
operations_map = diff_assignments(openedx_assignments, canvas_assignments, use_canvas_due_dates)
174176
logger.info(
175177
"Syncing assignments with Canvas. Adding: %d, Updating: %d, Deleting: %d",
176178
len(operations_map["add"]),
@@ -198,9 +200,7 @@ def _sync_canvas_due_dates(course_id: str):
198200
course_id,
199201
)
200202
return
201-
use_canvas_due_dates = course.other_course_settings.get(
202-
"use_canvas_due_dates", False
203-
)
203+
use_canvas_due_dates = get_use_canvas_due_dates(course)
204204
if not use_canvas_due_dates:
205205
TASK_LOG.info(
206206
"Due Date Sync: Disabled. Skipped for course %s",

src/ol_openedx_canvas_integration/ol_openedx_canvas_integration/utils.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ def get_canvas_course_id(course=None):
66
return course.other_course_settings.get("canvas_id") if course else None
77

88

9+
def get_use_canvas_due_dates(course=None):
10+
"""Get the canvas due dates setting from the course settings"""
11+
return course.other_course_settings.get("use_canvas_due_dates", False) if course else None
12+
13+
914
def get_task_output_formatted_message(task_output):
1015
"""Take the edX task output and format a message for table display on task result"""
1116
# this reports on actions for a course as a whole

0 commit comments

Comments
 (0)