The following blocks of code demonstrate one method for extracting grade books from multiple classes and binding them into a single data frame. The rcanvas package (Ranzolin, Hua, and Hathaway 2021) is used to connect to Canvas and retrieve the grade books from multiple comparable classes. Since the process of downloading multiple data frames from Canvas can take a while, more than 15 minutes with eight classes, this retrieval portion of the process is separated from the exploratory portion of the process which is detailed in a separate file.

1 Load Libraries

# Safely store credentials
library(keyring)

# Read canvas course information
library(rcanvas)

# Data wrangling and formatting
library(tidyverse)
library(kableExtra)
library(knitr)
opts_chunk$set(comment=NA, prompt=FALSE, cache=FALSE, echo=TRUE, results='asis')

# Plotting
library(ggplot2)
library(ggstatsplot)

2 Establish Connection to Canvas

To connect to Canvas, one must establish a Canvas token. Instructions for doing this can be found at the rcanvas package repository.

To avoid writing credentials into the script, the keyring (Csárdi 2021) library is used. The credential entry for Canvas was created in a separate step. Instructions for making keyring entries can be found at the R-bloggers website..

# Enter token, set domain, and get class list
canvas_token <- key_get("canvas-token", "nmc")
set_canvas_token(token = canvas_token)

#Set domain
set_canvas_domain("https://nmsu.instructure.com")

# Get course list and eliminate unnecessary columns
course_list <- get_course_list() %>% 
  select(id, name)
course_list

2.1 Get all of one type of class

Use a grepl statement to filter the course list down to a subset of comparable classes.

course_list <- course_list %>% 
  filter(.,grepl("ANTH-125G|HON-235G|ANTH-1140G|HNRS-2161G", name))
course_list

With the subset list of classes, use the rcanvas function get_course_gradebook() to retrieve the grade book for each relevant class. In the example below, this is hard coded. It is not the optimal work flow. I am sure there is a programmatic way to do this (by means of a for loop, lapply, or map function in purrr). However, I fiddled around and couldn’t figure it out. If someone knows a better method, please open an issue in the repository and I will incorporate any working suggestions. Lacking the knowledge of how to do this programmatically, in this case the workaround solution is hard coding. Note the following step takes more than 15 minutes.

# Loading the grade book is pretty slow.
# Assign grade book to a variable.
gb_all_1244932 <- get_course_gradebook(1244932)
gb_all_1246789 <- get_course_gradebook(1246789)
gb_all_1207030 <- get_course_gradebook(1207030)
gb_all_1314867 <- get_course_gradebook(1314867)
gb_all_1312607 <- get_course_gradebook(1312607)
gb_all_1273540 <- get_course_gradebook(1273540)
gb_all_1346300 <- get_course_gradebook(1346300)
gb_all_1345426 <- get_course_gradebook(1345426)

Once each of the grade books is retrieved and assigned to a variable, use the dplyr function to add a new column with the appropriate course name. Again, it is no doubt possible to perform this task with a function. If anyone has an idea about how to do this better please open an issue in the repository with a suggestion, I will hapily incorporate it, and provide attribution.

# Add a course name column to each data frame
gb_all_1244932 <- gb_all_1244932 %>% 
  mutate(course_name = "ANTH-125G F19")

gb_all_1246789 <- gb_all_1246789 %>% 
  mutate(course_name = "HON-235G F19")

gb_all_1207030 <- gb_all_1207030 %>% 
  mutate(course_name = "ANTH-125G S19")

gb_all_1314867 <- gb_all_1314867 %>% 
  mutate(course_name = "ANTH-125G F20")

gb_all_1312607 <- gb_all_1312607 %>% 
  mutate(course_name = "HON-235G F20")

gb_all_1273540 <- gb_all_1273540 %>% 
  mutate(course_name = "ANTH-125G S20")

gb_all_1346300 <- gb_all_1346300 %>% 
  mutate(course_name = "ANTH-1140G S21")

gb_all_1345426 <- gb_all_1345426 %>% 
  mutate(course_name = "HNRS-2161G S21")

With all of the grade books downloaded, the course_name column added, now it is converted into a list.

# Bind the data frames into a list.
gb_list <- list(gb_all_1244932,
                gb_all_1246789,
                gb_all_1207030,
                gb_all_1314867,
                gb_all_1312607,
                gb_all_1273540,
                gb_all_1346300,
                gb_all_1345426)

gb_list

A function is created to do several data wrangling tasks including: - create an anonymous student ID (anon_id) based on two existing columns - subsetting the columns - removing the “test student” which has no anon_id - converting assignment_name, course_id, and course_name to factors

# Apply a function that cleans the raw data.
# Given that the data are written to cvs, the as_factor functions may not be necessary.
prepR <- function(x){
  x$assignment_name <- gsub('\\s+', '', x$assignment_name)
  
  x %>% 
  mutate(anon_id = str_c(
    str_sub(user.name, -3), str_sub(user_id, -3))
    ) %>% 
    select(anon_id,
           assignment_name, 
           score, 
           course_id,
           course_name) %>% 
  filter(anon_id != 0) %>% # Remove Test Student
  mutate_all(~replace(., is.na(.), 0)) %>% # Replace NA with 0
  mutate(assignment_name = as_factor(assignment_name)) %>% 
  mutate(course_id = as_factor(course_id)) %>% 
  mutate(course_name = as_factor(course_name))
}

Use the purrr package that is part of the tidyverse to map() the previously defined function to the list of dataframes.

# Apply the function to the list of grade books.
gb_list <- map(gb_list, ~prepR(.))
gb_list

Use the bind_rows() function to convert the list of dataframes into one single long form dataframe.

# Bind each of the grade books in the list into a single dataframe
gb_list <- gb_list %>% 
  bind_rows()

Review the dataframe to make sure everything is ok.

# Spot check the data frame
gb_list

Given that the data retrieval process takes a long time, save the resulting wrangled dataframe to a csv file.

# Save the dataframe to a csv file.
write.csv(gb_list, "../data/gb_list.csv")

From here with the file saved, one can move on to exploration and analysis. This is performed in another notebook.

References

Csárdi, Gábor. 2021. Keyring: Access the System Credential Store from r. https://CRAN.R-project.org/package=keyring.
Ranzolin, David, Chris Hua, and J. Hathaway. 2021. Rcanvas: R Client for Canvas API. https://github.com/daranzolin/rcanvas.