Register for InstructureCon25 • Passes include access to all sessions, the expo hall, entertainment and networking events, meals, and extraterrestrial encounters.
Found this content helpful? Log in or sign up to leave a like!
Hi,
My end goal is to make a script that copies over courses without calendar events.
I have edited a script I found to use the content_migration api call to copy a canvas course from an old one to a new one. I have also explored using the course_copy api call to copy the course without calendar events but I can see it's been retired for a reason - other settings are not coming through and you are left with the default module view.
My long winded solution is to somehow use the content_migration and then check the course for calendar events without dates and remove them... Seems like a really long workaround and, as a total novice, I'm not even sure how to get a list of calendar events by course.
I saw an old feature request asking for a solution here but nothing else helpful.
Does anybody else have any ideas or solutions?
Also - thanks to whoever originally made the code that I've based this all on.
import requests
import time
token='token goes here'
def migrateContents(old_course, new_course):
payload={'migration_type':'course_copy_importer','settings[source_course_id]': str(old_course), 'date_shift_options[remove_dates]': 'True'}
r = requests.post('https://your institution.instructure.com/api/v1/courses/'+ str(new_course)+ '/content_migrations/', params=payload, headers = {'Authorization': 'Bearer ' + token})
data = r.json()
print data
progress_url = data[u'progress_url']
print "Migration URL is: " + str(progress_url)
progress = 0
while progress != 100:
progress_check = requests.get(progress_url, headers = {'Authorization': 'Bearer ' + token})
progress_result = progress_check.json()
print "Migration Status is: " + str(data[u'workflow_state']) + ", | progress: " + str(progress_result[u'completion']) + "%"
#print progress_result
progress = progress_result[u'completion']
time.sleep(2)
if progress_result[u'completion'] == 100:
print "------------------------------"
print "Migration completed."
break
def copyContents(old_course, new_course):
payload={'source_course':str(old_course),'except[]': 'calendar_events'}
r = requests.post('https://your institution.instructure.com/api/v1/courses/'+ str(new_course)+ '/course_copy/', params=payload, headers = {'Authorization': 'Bearer ' + token})
data = r.json()
print data
#comment out the def you don't want to call before running!
copyContents(1840,3592)
migrateContents(1840,3591)
Solved! Go to Solution.
Hi @James ,
Very helpful, I updated the payload and added in those copy parameters and tested it - all seems to work fine and no calendar events so question answered. I have included the updated code below. My next step is to read in a CSV file of old and new courses to automate our course rollover process. If anybody is interested in the complete end code let me know and I'll keep you updated. In the meantime here is what I have right now (you can see the additional parameters in line 7):
import requests
import time
token='token here'
def migrateContents(old_course, new_course):
payload={'migration_type':'course_copy_importer','settings[source_course_id]': str(old_course), 'date_shift_options[remove_dates]':'True','copy[all_course_settings]':'1','copy[all_syllabus_body]':'1','copy[all_context_modules]':'1','copy[all_assignments]':'1','copy[all_quizzes]':'1','copy[all_assessment_question_banks]':'1','copy[all_discussion_topics]':'1','copy[all_wiki_pages]':'1','copy[all_context_external_tools]':'1','copy[all_rubrics]':'1','copy[all_attachments]':'1'}
r = requests.post('https://yourinstitution.instructure.com/api/v1/courses/'+ str(new_course)+ '/content_migrations/', params=payload, headers = {'Authorization': 'Bearer ' + token})
data = r.json()
print data
progress_url = data[u'progress_url']
print "Migration URL is: " + str(progress_url)
progress = 0
while progress != 100:
progress_check = requests.get(progress_url, headers = {'Authorization': 'Bearer ' + token})
progress_result = progress_check.json()
print "Migration Status is: " + str(data[u'workflow_state']) + ", | progress: " + str(progress_result[u'completion']) + "%"
#print progress_result
progress = progress_result[u'completion']
time.sleep(2)
if progress_result[u'completion'] == 100:
print "------------------------------"
print "Migration completed."
break
migrateContents(1840,3595)
#first number is source course and second is destination
Calendar events can be a little confusing to understand. There are two types of things under the umbrella of "calendar events": events and assignments. Here the word event means a non-assignment event.
Assignments may not have a due date and be considered undated. I have not seen an undated "event". That doesn't mean that they don't exist, just that it would be hard to imagine an event without a date, so I want to make sure we're talking about the same thing.
When you look at the place that makes you think there are undated calendar events, are they really for events not tied to an assignment or are they for assignments? For example, my Calendar has an Undated section at the bottom, but everything there is for an assignment.
My Syllabus page has undated items at the bottom, but they are all for assignments, not non-assignment events.
If what you're looking at are truly events that are not tied to assignments, please share an example (and how it was created) so that I can understand. If this really is the case, then you would need to get the list of calendar events after the course migration is done and then selectively remove the ones that have no dates. You would do this through the Calendar Events API.
If they are for assignments and you're trying to clean up a course by removing assignments that were part of the course but never assigned and given a due date, then you'll need to pull up a list of assignments through the API, look for ones that are not published and/or have no due date, and remove them. You do not remove assignment events from the calendar, you remove the assignment and it automatically disappears from the calendar.
Hi James,
Thanks for the reply.
These are definitely calendar events that are undated. I am happy, and in fact, want the assignments in there and undated.
When I copy over a course, using either "copy a canvas course" in import course content or using the API if you remove dates the calendar events still come through. See screenshot below. Red box is for stuff I do not want and green is for the assignments:
To recreate the screenshot above please do a course migration, either with the API or import course content and then select all content and then remove dates. Copy a course that has dated calendar events and you will end up with imported undated calendar events once done.
I have had a look at the calendar events API and I can only seem to find a way to list calendar events by user and not by course or by section. So have I missed something simple and is there a fairly straight forward way to get a list of calendar events by course using the API?
Hope all of this makes sense.
This makes more sense -- I think. You're saying that they were dated in the original course, but during the copy you removed the dates and now they're considered undated. Is that correct?
It sounds like you want to remove all calendar events not tied to an assignment. If you strip the dates, then none of them would be dated, and all should be removed. Is that correct?
I copied a course using the web UI and then looked at the call that was made by Canvas. It was an API call, which is great! The call was a PUT to the Update a content migration endpoint and not an internal call.
Inside of the form data that was sent was a copy property that contained another object. This is what was part of the copy object when I checked everything but the calendar events. Rather than using copy[all_calender_events]="0", it left it out completely. Here is the payload that was sent.
{
"id":"1200036",
"workflow_state":"waiting_for_select",
"user_id":"2175488",
"copy":{
"all_course_settings":"1",
"all_syllabus_body":"1",
"all_context_modules":"1",
"all_assignments":"1",
"all_quizzes":"1",
"all_assessment_question_banks":"1",
"all_discussion_topics":"1",
"all_wiki_pages":"1",
"all_context_external_tools":"1",
"all_rubrics":"1",
"all_attachments":"1"
}
}
I have not looked into the "except" thing. It also sounds like through the API and not the web, you would send that as a POST to the Create a content migration endpoint. There may be other parameters required.
I've not messed much with content copy through the API, so hopefully this is enough to get you going in the right direction or someone else who has done it can step in with more wisdom.
Hey James,
That is amazing, I am going to see what I can do with this and will share my results in case it's of use to anybody else.
Thanks,
Lawrence
Hi @James ,
Very helpful, I updated the payload and added in those copy parameters and tested it - all seems to work fine and no calendar events so question answered. I have included the updated code below. My next step is to read in a CSV file of old and new courses to automate our course rollover process. If anybody is interested in the complete end code let me know and I'll keep you updated. In the meantime here is what I have right now (you can see the additional parameters in line 7):
import requests
import time
token='token here'
def migrateContents(old_course, new_course):
payload={'migration_type':'course_copy_importer','settings[source_course_id]': str(old_course), 'date_shift_options[remove_dates]':'True','copy[all_course_settings]':'1','copy[all_syllabus_body]':'1','copy[all_context_modules]':'1','copy[all_assignments]':'1','copy[all_quizzes]':'1','copy[all_assessment_question_banks]':'1','copy[all_discussion_topics]':'1','copy[all_wiki_pages]':'1','copy[all_context_external_tools]':'1','copy[all_rubrics]':'1','copy[all_attachments]':'1'}
r = requests.post('https://yourinstitution.instructure.com/api/v1/courses/'+ str(new_course)+ '/content_migrations/', params=payload, headers = {'Authorization': 'Bearer ' + token})
data = r.json()
print data
progress_url = data[u'progress_url']
print "Migration URL is: " + str(progress_url)
progress = 0
while progress != 100:
progress_check = requests.get(progress_url, headers = {'Authorization': 'Bearer ' + token})
progress_result = progress_check.json()
print "Migration Status is: " + str(data[u'workflow_state']) + ", | progress: " + str(progress_result[u'completion']) + "%"
#print progress_result
progress = progress_result[u'completion']
time.sleep(2)
if progress_result[u'completion'] == 100:
print "------------------------------"
print "Migration completed."
break
migrateContents(1840,3595)
#first number is source course and second is destination
I'm glad you were able to figure it out.
I hope someone else can provide an easier way -- if Canvas comes along and adds another content type, then you'll need to add it, while an exclude type argument would still work.
Hi @lchallen I would be interested in seeing the process now that you have finished it, inclusive of the CSV.
i'm probably missing something here, but in the content migration api removing dates can be done by using the
date_shift_options[remove_dates]
we use the api to create courses and then use the api to migrate content from course masters. we use the shift dates option
date_shift_options[shift_dates]
we did use the remove dates part for awhile and it stripped off any course content due dates, availability dates, etc.
I believe the desire was to copy a course without copying the calendar events. In the response that Lawrence left and marked as correct, he has included the option to remove dates on line 7.
Yes! I would love a copy of the code when you are done. jrboek@ou.edu. We have been just thinking about how to do this kind of process..
Hi John,
This is is now on low priority so nothing planned until sometime in the future!
I got as far as writing up a impel list of requirements that I would be happy to share but no more code for now.
I will be changing roles soon so I am going to tag my colleague Dan in for the future!
We also ended up supporting rollover and excluding calendar events for all our users. We did this in the same manner of creating a content migration and excluding calendar events (we also ended up excluding announcements as well).
Hi Matthew would you be able to share this process with me?
@OliverKharas Sure, but I'm not sure how helpful what we've done is. We have an application integrated into Canvas (through LTI/APIs) that presents a UI similar to the courses view in an account and allows users who don't have permission to create courses to rollover courses. It's a large chunk of Java code with a React frontend, but the bit that handles the copying of course content is attached. It uses the K-State Canvas Java API (https://github.com/kstateome/canvas-api) to handle all the calls to Canvas.
We always do a selective migration (as we never want to copy everything). To do a selective copy we first create a content migration (https://canvas.instructure.com/doc/api/content_migrations.html#method.content_migrations.create) and because we asked for a selective migration at that point the copy is waiting for us to say what to copy.
Then you can list the available options (https://canvas.instructure.com/doc/api/content_migrations.html#method.content_migrations.content_lis...) and then update the migration with the items you want to copy.
When rolling over a course we let the user choose if they want to copy the content.
Attached is the code we use to do the rollover of one of the Canvas courses to give you an idea of how this works.
Thanks @matthew_buckett appreciate your response although I am still unsure about where to begin. I can do simple API calls for example listing students in a course using Postman.
The situation I'm in is I have 300 courses a semester that need to be rolled over each time, trying to figure out if its just an uploaded CSV file with say COPY FROM & COPY TO. or need to use the content migration API? had no luck in finding a video on something similar either.
To interact with Panda Bot in the Instructure Community, you need to sign up or log in:
Sign In