cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
lchallen
Community Contributor

Use the Content Migration API to copy courses but without their calendar events (or remove them after)

Jump to solution

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)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
Labels (1)
1 Solution

Accepted Solutions
lchallen
Community Contributor

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

View solution in original post

16 Replies
James
Community Champion

 @lchallen ,

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.

268614_2018-02-19_22-35-22.png

My Syllabus page has undated items at the bottom, but they are all for assignments, not non-assignment events.

268615_pastedImage_3.png

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.

lchallen
Community Contributor

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:

268728_pastedImage_1.png

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.

James
Community Champion

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?

James
Community Champion

 @lchallen , 

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.

lchallen
Community Contributor

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

lchallen
Community Contributor

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

View solution in original post

James
Community Champion

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.

jared_flaherty
Community Contributor

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.