Skip navigation
All Places > Canvas Developers > Blog > 2017 > February

We’ve been moving to a “standards-based mindset” (props to Schimmer for a great book!) in our assessment and grading over the past couple of years and have begun collecting student performance data using a variety of methods. We’ve tried Active Grade (now a part of Haiku) and JumpRope, which are very full featured standards-based gradebooks, but without a full fledged institutional commitment - i.e., everyone grading fully on standards - it’s difficult to justify the cost of additional software, especially when we are already using Instructure Canvas as our LMS. The goal is to provide feedback using standards, but not transition to a standards-based report card.


Canvas provides a standards-based solution called the Learning Mastery Gradebook, which pulls data from outcomes aligned to assignment rubrics. These outcomes can be organized into outcome groups within a course, then reported to students through the normal “Grades” interface. Outcome scores can be calculated using a variety of methods; the one we prefer is the simplified “decaying average” which favors the most recent assessment point by whatever percent you specify, with the remainder of the score being made up of the average of the rest of the previous assessment points.


Our desired approach involves unpacking curricular standards, or power standards, into specific “learning targets”. In Canvas, power standards correspond to outcome groups, while learning targets correspond to individual outcomes. Ultimately, we want to provide students with scores on both learning targets and power standards - something Canvas currently doesn’t do.


I’ve created a little app using Python that leverages the Canvas API to pull data from a user’s course and the Learning Mastery Gradebook, restructure it, calculate a mean outcome group score, then send students a templated message in their Canvas inbox with their results. I think this will be a functional bridge between what Canvas currently offers and products like JumpRope, which are marketed as standards-based gradebooks and not as an LMS.


Take a look at the code in the github repository here:


I’m very interested in collaborating with others who want to hack the LMG like this - particularly someone who has some experience creating GUIs using Tkinter in Python. The app is currently very ugly; I didn’t pay much attention to how buttons were laid out. Just figuring out how Canvas structured the response data was challenging enough!


Send me an email or tweet me @CITBrian if you’re interested in improving and extending the functionality of the app!


Edit: Here are a few screen shots:




Original post: rcanvas + the tidyverse 

rcanvas continues to grow. Thanks to the recent contributions from Chris Hua, getting user groups, announcements, and discussions from your institution’s Canvas instance has never been easier. More collaborators are welcome!

By my lights, R package development should be attuned to the tidyverse. Piping output into a sequence of clear, logical functions not only makes for clean, readable code, but is an undeniably damned good time.

The remainder of this post will showcase the interplay between the tidyverse and various rcanvas functions.

Getting Students, Teachers, or Observers

students <- get_course_list(include = c("term", "total_students")) %>% 
  filter( == "2017 Spring",
    total_students > 0) %>%
  .$id %>%
  map_df(get_course_items, "enrollments") %>%
  filter(type == "StudentEnrollment", #'TeacherEnrollment' for teachers, 'ObserverEnrollment' for observers
         enrollment_state == "active")


With the data in hand, you can filter by any relevant criteria. Say, for example, you want to know which students have been inactive for more than a week:


students %>%
  mutate(inactive_days = as.numeric(round(difftime(ymd(Sys.Date()), ymd_hms(last_activity_at), units = c("days"))))) %>%
  filter(inactive_days >= 7)


Getting Missing Assignments

Here's how to get all missing assignments for a course, multiple courses, students, or multiple students. Again, the code flow is intuitive. First, get the courses. Second, filter the course(s) you want. Third, pipe the course ids into `purrr::map_df`, and fourth, extend the pipe with user ids into `get_user_items`.


biology_missing_assignments <- get_course_list(include = c("term", "total_students")) %>% 
  filter( == "2017 Spring",
    total_students > 0,
    grepl("Biology", name)) %>%
  .$id %>%
  map_df(get_course_items, "enrollments) %>%
  filter(type == "StudentEnrollment",
         enrollment_state == "active) %>%
  .$user_id %>%
  map_df(get_user_items, "missing_assignments")


In sum, getting lots of data very quickly is just a matter of piping the course and user ids into various functions and letting the tidyverse do its thing.

What else can I get?

Lots! User page views, profiles, avatars, and observees, or course discussion topics, assignments, files, modules, pages, quizzes, folders, todos, settings, and more. If it’s in the API docs, we’ve tried to include it.