Celebrate Excellence in Education: Nominate Outstanding Educators by April 15!
Found this content helpful? Log in or sign up to leave a like!
I wrote a Python script to publish a large group of courses at one time using the API for Update courses (https://canvas.instructure.com/doc/api/courses.html#method.courses.batch_update).
What I originally wrote was very similar to Mike Nahmias’ post (https://community.canvaslms.com/message/109721-python-script-to-publish-courses-in-an-account).
However, it only works for about 350 courses while the limit is listed as 500. I get a 414 error back instead of 200, because the URL is too long. Here’s the full error:
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>414 Request-URI Too Long</title>
</head><body>
<h1>Request-URI Too Long</h1>
<p>The requested URL's length exceeds the capacity
limit for this server.<br />
</p>
<hr>
<address>Apache Server at canvas.instructure.com Port 80</address>
</body></html>
Like Mike, I was using the “requests.put”:
requests.put(url, headers=headers, params=payload)
So, that explains the error as everything is being put onto the URL and it’s just too long.
However, when I switch it to use post, it no longer works. Instead it creates a new course, as do all of these:
requests.post(url, headers=headers, params=payload)
requests.post(url, headers=headers, json=payload)
requests.post(url, headers=headers, json=json.dumps(payload))
(Thanks to Brian Bennett’s reply and Jason Kocur’s thread for the json ideas, but it doesn’t work for this API.
https://community.canvaslms.com/thread/40398-canvas-apipython-help-post-requests)
Has anybody gotten this to work for > 350 courses? And what’s the secret?
I’d be happy to try UCF Open’s canvasapi, but I can’t find this batch update for courses.
Thanks!
Ember
Don't try to send 500. 500 is an internal maximum supported by Canvas for that call, but it's still subject to other restrictions like the length of an URL.
It goes into a background process and you get a progress object returned, so it doesn't matter whether you send 500 at once or 250 twice.
I capped mine at 200 for this call (method 3 under) How to list teachers who have published/unpublished courses. I'm building the list as I iterate through my courses and just call the API every 200 entries. However, if I had an array already formed, I take an array slice and call it multiple times.
Hi James,
Thanks for your help. That's good to know.
All best,
Ember
Ember -
Here is a small snippet of some Python code of how I delete courses in 500 course batches. You can do this, but there is a specific way you need to make the API call:
# there is a course id limit for the API call, so make sure we don't go over that
# need to ensure we call this at least once
while len(course_id_list) > 0 or len(course_id_list) > self.course_limit:
# take the first elements up to the limit
temp_list = course_id_list[0:self.course_limit]
# trim the original list for the next cycle
course_id_list = course_id_list[self.course_limit:]
# now we delete the list of courses
# response is a progress object that we will query the status before progressing
# to send the list of course ids in a batch like this, we need to send as a list of tuples to the request object
# a dictionary won't work because the keys are the same and will just get overridden with the last value
data = [("event", "delete")]
for _ in temp_list: data.append(("course_ids[]", _))
# send the request
resp = s.put(f'{url}{course_endpoint.format(account_id)}', data=data, headers=self.header)
# we should get a progress object back
# we write out the progress to file
if resp.status_code == 200:
resp_dict = loads(resp.text)
if 'url' not in resp_dict: raise Exception(f'{self.__class__.__name__}.set_courses. No URL returned in the progress object.')
self.get_progress(resp_dict)
else: raise Exception(f'{self.__class__.__name__}.set_courses. Code: {resp.status_code}, Message: {resp.text}.')
To explain:
The self.course_limit is set to 500 from the script config file. The course_id_list is a list of course ids (previously compiled earlier in the script) that need to be deleted. I create a temp list that holds the list of courses to update (up to the 500 limit) and attached that tuple object to the data payload. I make the call and it returns a progress id that I trap for later.
This code is part of a larger course archiving process that we run at the end of the school year. Hope this helps. Reach out if you need more clarification.
To participate in the Instructure Community, you need to sign up or log in:
Sign In