4
4
import zipfile
5
5
from collections import defaultdict
6
6
from collections .abc import Generator
7
+ from datetime import datetime
7
8
from io import BytesIO
8
9
from pathlib import Path
9
10
from tempfile import TemporaryDirectory
@@ -73,20 +74,40 @@ def sync_canvas_archive(bucket, key: str, overwrite):
73
74
return resource_readable_id , run
74
75
75
76
77
+ def _course_url (course_archive_path ) -> str :
78
+ context_info = parse_context_xml (course_archive_path )
79
+ return f"https://{ context_info .get ('canvas_domain' )} /courses/{ context_info .get ('course_id' )} /"
80
+
81
+
76
82
def run_for_canvas_archive (course_archive_path , course_folder , overwrite ):
77
83
"""
78
84
Generate and return a LearningResourceRun for a Canvas course
79
85
"""
80
86
checksum = calc_checksum (course_archive_path )
81
87
course_info = parse_canvas_settings (course_archive_path )
82
88
course_title = course_info .get ("title" )
89
+ url = _course_url (course_archive_path )
90
+ start_at = course_info .get ("start_at" )
91
+ end_at = course_info .get ("conclude_at" )
92
+ if start_at :
93
+ try :
94
+ start_at = datetime .fromisoformat (start_at )
95
+ except (ValueError , TypeError ):
96
+ log .warning ("Invalid start_at date format: %s" , start_at )
97
+ if end_at :
98
+ try :
99
+ end_at = datetime .fromisoformat (end_at )
100
+ except (ValueError , TypeError ):
101
+ log .warning ("Invalid start_at date format: %s" , end_at )
102
+
83
103
readable_id = f"{ course_folder } -{ course_info .get ('course_code' )} "
84
104
# create placeholder learning resource
85
105
resource , _ = LearningResource .objects .update_or_create (
86
106
readable_id = readable_id ,
87
107
defaults = {
88
108
"title" : course_title ,
89
109
"published" : False ,
110
+ "url" : url ,
90
111
"test_mode" : True ,
91
112
"etl_source" : ETLSource .canvas .name ,
92
113
"platform" : LearningResourcePlatform .objects .get (
@@ -100,6 +121,8 @@ def run_for_canvas_archive(course_archive_path, course_folder, overwrite):
100
121
run_id = f"{ readable_id } +canvas" ,
101
122
learning_resource = resource ,
102
123
published = True ,
124
+ start_date = start_at ,
125
+ end_date = end_at ,
103
126
)
104
127
run = resource .runs .first ()
105
128
resource_readable_id = run .learning_resource .readable_id
@@ -206,6 +229,21 @@ def transform_canvas_problem_files(
206
229
yield problem_file_data
207
230
208
231
232
+ def parse_context_xml (course_archive_path : str ) -> dict :
233
+ with zipfile .ZipFile (course_archive_path , "r" ) as course_archive :
234
+ context = course_archive .read ("course_settings/context.xml" )
235
+ root = ElementTree .fromstring (context )
236
+ namespaces = {"ns" : "http://canvas.instructure.com/xsd/cccv1p0" }
237
+ context_info = {}
238
+ item_keys = ["course_id" , "root_account_id" , "canvas_domain" , "root_account_name" ]
239
+ for key in item_keys :
240
+ element = root .find (f"ns:{ key } " , namespaces )
241
+ if element is not None :
242
+ context_info [key ] = element .text
243
+
244
+ return context_info
245
+
246
+
209
247
def parse_module_meta (course_archive_path : str ) -> dict :
210
248
"""
211
249
Parse module_meta.xml and return publish/active status of resources.
0 commit comments