Ruby script for API: how many (and which) submissions have comments from +1 teacher?

a_craik
Community Contributor
2
1304

EDIT -  a bit of retrospectively added blurb about this script:

If you didn't know, Canvas APIs are fantastic. To really leverage the API, you need to learn at least a little bit of coding so you can run a script that will ultimately help you to achieve something that would otherwise have taken an awful lot of arduous clicking around in Canvas.

So this is my first script. When it runs it makes three different GET calls to retrieve data about a particular assignment (you define which assignment - look at the code comments in green). The 3 calls are to (1) retrieve the comments left for a submission, (2) get a list of the teachers on the course, and (3) get a list of the students on the course. The script then uses the response data from these calls to work out which students received comments on their submission from more than one teacher. Our assessment regulations require 10% or more of the submissions to be treated this way, so the script ultimately outputs (1) how many students submitted, (2) how many received a comment from more than one teacher, (3) whether the 10% rule was met or not, and (4) the list of student SIS Ids who did receive this.

As you read through the code you'll spot other things going on to do with how the response data gets manipulated to achieve the end result. If you look at this and think 'this is terrifying nonsense and sod it I'm no coder so never mind, APIs aren't for me' - STOP. Don't worry - I went from that starting point to this script in about 3 weeks (and that was playing around outside of work time!). I can attribute my success here to this self-paced course built specifically for learning ruby (a coding language) with the Canvas APIs:

Getting Started with Ruby & the Canvas API . Go join it. Make awesome things. Just remember to give kudos to Jeremy for building that course for us all!

(P.S my original comment is still below the code)

require 'typhoeus'
require 'link_header'
require 'json'

@canvas_url = 'https://YOURCANVAS.test.instructure.com' #you will need to put your own Canvas url here (recommend you use test environment!)
@canvas_token = 'YOURTOKEN' #put your own token here!
@api_endpoint = '/api/v1/courses/[COURSE NO]' #update with the course number (placeholder is "[COURSE NO]" - square brackets not required)
@api_endpoint1 = '/assignments/[ASSIGNMENT NO]/submissions?include[]=submission_comments' #update with the assignment number (placeholder is "[ASSIGNMENT NO]" - square brackets not required)

#You should not change any of the code below, unless you know what you're doing (or you're learning by tinkering but go careful!)

@api_endpoint2 = '/enrollments?type[]=TeacherEnrollment'
@api_endpoint3 = '/enrollments?type[]=StudentEnrollment'

@commentarray = [] #creates empty global array for full data of comments call
@commenthash = Hash.new{|hsh,key| hsh[key] = [] } #creates empty hash with values as arrays for specific data from commentarray to go into
@teacherarray = [] #creates empty global array for teacher enrollments call
@studenthash = {} #creates empty hash for student enrollments call

#the following code gets data relating to student submissions of the desired course assignment:

request_url = "#{@canvas_url}#{@api_endpoint}#{@api_endpoint1}"
more_data = true
while more_data # while more_data is true keep looping through the data
# puts request_url
get_comments = Typhoeus::Request.new(
request_url, #we need a variable here because we need the api url to change
method: :get,
headers: { authorization: "Bearer #{@canvas_token}" }
)

get_comments.on_complete do |response|
#get next link
links = LinkHeader.parse(response.headers['link']).links
next_link = links.find { |link| link['rel'] == 'next' }
request_url = next_link.href if next_link
if next_link && "#{response.body}" != "[]"
more_data = true
else
more_data = false
end
#ends next link code

if response.code == 200
data = JSON.parse(response.body)
data.collect do |comments|
@commentarray.push(comments) #adds all data from enrollments JSON responses into the @commentarray
end
else
puts "Something went wrong! Response code was #{response.code}"
end
end

get_comments.run
end

studentarraycounter = 0
commentcounter = 0
more_comments = true
while more_comments
if !@commentarray[studentarraycounter].nil? && !@commentarray[studentarraycounter]["submission_comments"][commentcounter].nil?
@commenthash[@commentarray[studentarraycounter]["user_id"]].push @commentarray[studentarraycounter]["submission_comments"][commentcounter]["author_id"]
commentcounter +=1
more_comments = true

else
studentarraycounter +=1
commentcounter = 0
if !@commentarray[studentarraycounter].nil?
more_comments = true
else
more_comments = false
end
end
end

#the following code relates to teacher enrollment on the desired course:

request_url = "#{@canvas_url}#{@api_endpoint}#{@api_endpoint2}"
more_data = true
while more_data # while more_data is true keep looping through the data
# puts request_url
get_teachers = Typhoeus::Request.new(
request_url, #we need a variable here because we need the api url to change
method: :get,
headers: { authorization: "Bearer #{@canvas_token}" }
)

get_teachers.on_complete do |response|
#get next link
links = LinkHeader.parse(response.headers['link']).links
next_link = links.find { |link| link['rel'] == 'next' }
request_url = next_link.href if next_link
if next_link && "#{response.body}" != "[]"
more_data = true
else
more_data = false
end
#ends next link code

if response.code == 200
data = JSON.parse(response.body)
data.each do |teachers|
@teacherarray << teachers["user"]["id"] #adds just the teacher IDs into the @teacherarray
end
else
puts "Something went wrong! Response code was #{response.code}"
end
end

get_teachers.run
end

#the following code relates to student enrollment on the desired course:

request_url = "#{@canvas_url}#{@api_endpoint}#{@api_endpoint3}"
more_data = true
while more_data # while more_data is true keep looping through the data
# puts request_url
get_students = Typhoeus::Request.new(
request_url, #we need a variable here because we need the api url to change
method: :get,
headers: { authorization: "Bearer #{@canvas_token}" }
)

get_students.on_complete do |response|
#get next link
links = LinkHeader.parse(response.headers['link']).links
next_link = links.find { |link| link['rel'] == 'next' }
request_url = next_link.href if next_link
if next_link && "#{response.body}" != "[]"
more_data = true
else
more_data = false
end
#ends next link code

if response.code == 200
data = JSON.parse(response.body)
data.each do |scrapestudents|
@studenthash[scrapestudents["sis_user_id"]] = [scrapestudents["user_id"]]

end
else
puts "Something went wrong! Response code was #{response.code}"
end
end

get_students.run
end

#the remaining code now interrogates the manipulated data from the calls above and presents the findings (did 10% of submissions have comments from more than one teacher? If so, which students were these? )

totalsubs = @commenthash.length

@second_markedarray = @commenthash.select{|_, v| (v & @teacherarray).length > 1}.keys
marked = @commenthash.select{|_, v| (v & @teacherarray).length > 1}.keys.count
puts "#{totalsubs} students have submitted"
puts "#{marked} submissions were second marked"
done = (marked.to_f / totalsubs) * 100
if done >= 10
puts "MARKING COMPLETED: #{done.round(1)}% of submissions have been second marked."
else
puts "Only #{done.round(1)}% of submissions have been second marked!"
end

fin = @studenthash.select{|_, v| (v & @second_markedarray).length == 1}.keys
puts "This is the list of student no's (SIS ID's) that have been 2nd marked:"
puts fin
2 Comments
stuart_ryan
Community Novice

Hi  @a_craik ,

That is a neat little script! (love it!)

While Ruby is still new to me I can figure out most of it, I was thinking it might be really handy to have a little blurb at the top of your post before your script to help guide people. Your code comments are exceptional *high fives*, for someone that is new a little blurb at the start might give them that little bit extra they need.

Love your work!

Stuart

a_craik
Community Contributor

Hey Stuart

Thanks for the nudge and sorry it has taken me a while to get round to this - done now! Hope the revised blurb proves useful to those that find themselves landing here