#######################################################
# published_courses.rb - a Ruby script for retrieving #
# all the ids for all courses #
# in the published state for a given sub-account on #
# Instructure Canvas. #
# Dependencies: - Ruby V. 1.9 or greater #
# RUBY GEMS: #
# - rest-client : Ruby REST library #
# - yaml : For reading YAML config file #
# - cgi : For retrieving URL GET vars #
# - uri : For parsing URL GET vars #
# - json : For parsing JSON objects #
# returned from API calls #
# #
# Jeffrey Anderson, M.Ed #
# Multimedia Programmer Analyst #
# Center for Teaching and Learning #
# Mesa Community College #
# jeffrey.anderson@mesacc.edu #
# Maricopa County Community College District #
# http://ctl.mesacc.edu #
# http://www.mesacc.edu #
# http://www.maricopa.edu #
# #
#######################################################
#importing required ruby gem libraries
require 'rest_client'
require 'json'
require 'yaml'
require 'cgi'
require 'uri'
#config file containing all necessary configuration options
config = nil
#the array of JSON objects returned from the Courses API calls
$courses_json_arr = []
#the array of course ids that will be used to retrieve the teacher enrollment data
$course_ids = []
#the array of enrollment API call urls used to get the teacher enrollment data
#$enrollments_calls = []
#Instructor emails array
#$instructor_emails = []
#Instructor names array
#$instructor_names = []
#sub-account's name
#$subaccount_name
#Load and parse the configuration YAML file. It must be called "config.yaml"
parsed = begin
config = YAML.load(File.open("courses_config.yaml"))
rescue Exception => e
puts "Could not parse config YAML: #{e.message}"
end
auth_token = "Bearer " + config["api_key"]
subaccount_name_req = RestClient.get config["base_url"] + "/api/v1/accounts/" + config["subaccount_id"], :Authorization => auth_token
subaccount_json = JSON.parse subaccount_name_req.to_s
$subaccount_name = subaccount_json["name"]
#$subaccount_name = subaccount_json["name"]
#puts $subaccount_name
per_page = "1"
enrollment_term_id = config["enrollment_term_id"]
subaccount_url = config["base_url"] + "/api/v1/accounts/" + config["subaccount_id"] + "/courses/?page=1&per_page=" + per_page + "&enrollment_term_id=" + enrollment_term_id + "&published=false"
#puts subaccount_url
#puts auth_token
first_res = RestClient.get subaccount_url, :Authorization => auth_token
links = first_res.headers[:link]
#puts links
all_links = links.split(",")
#the last link is the 3rd in the Links header, and the actual URL is inside the first in the chunk that's returned. It looks like this:
#<https://<canvas-instance>/api/v1/accounts/:id/courses?page=<total_number_of_courses>&per_page=1>; rel="last"
#So the page parameter on the query string is the total number of courses if request that you're getting one course at a time. <total_number_of_courses>
#will contain an arbitray number depending on how many total courses there are in the sub-account. This number is important because it's how many subsequent
#times to continue to run the loop to grab the courses in the least amount of API server requests necessary
next_link = all_links[0].split(";")[0].gsub(/\<|\>/, "")
last = all_links[2].split(";")[0].gsub(/\<|\>/, "")
last_uri = URI.parse(last)
uri_parms = CGI.parse(last_uri.query)
#num_courses is currently a String variable at this point
$num_courses = uri_parms["page"][0]
$num_courses = $num_courses.to_i
puts "Number of courses found in sub-account: " + $num_courses.to_s
puts "Getting all Course Info in chunks of " + config["per_page"]
per_page = "50"
$course_collected = 0
$courses_json_arr = []
subaccount_url = config["base_url"] + "/api/v1/accounts/" + config["subaccount_id"] + "/courses/?page=1&per_page=" + per_page + "&enrollment_term_id=" + enrollment_term_id + "&published=false"
#puts subaccount_url
while $course_collected < $num_courses
response = RestClient.get subaccount_url, :Authorization => auth_token
links = response.headers[:link]
all_links = links.split(",")
#the next link is the 1st in the Links header if there are more pages in the pagination structure, and the actual URL is inside the
#first in the chunk that's returned. It should look like this:
#Link: <https://<canvas-instance>/api/v1/accounts/:id/courses?page=2&per_page=50>; rel="next"
next_link = all_links[0].split(';')[0].gsub(/\<|\>/, "")
#puts next_link
subaccount_url = next_link
temp_arr = JSON.parse response.to_s
$courses_json_arr.concat temp_arr
#puts $courses_json_arr[0]["id"]
#puts $courses_json_arr.length
#puts $courses_json_arr[$courses_json_arr.length]
$course_collected += temp_arr.length
#puts "Next link: " + next_link
#num_courses += per_page.to_i
puts "Total Number of Course IDs Collected: " + $course_collected.to_s
end
$output_file = File.new("course_ids.txt", 'w')
$courses_json_arr.each { |x|
#$course_ids.push x["id"]
$output_file.puts x["id"]
}
puts "# of published course ids collected: #{$course_collected}"
puts "Course IDs File Saved to Disk"
$output_file.close