cp -r ../../../Canvas/Canvas\ API/R .

Get course, assignment, submission and user information

# Course
DSB100       <- course_id_of("DSB100_23se2")

# Assessment
Assessment_1 <-  
  List_assignments(DSB100) |> 
  filter(str_detect(name, "Assessment 1")) |> pull(assignment_id)

# Submissions
Submissions  <- 
  List_assignment_submissions(DSB100, Assessment_1) |>
  filter(!is.na(attempt))

# Users (students only)
Users <-
  List_users_in_course(
    DSB100,                        # The course of interest
    `enrollment_type[]`="student", # This excludes users who are not students
    progress=FALSE                 # Suppress progress bar
  ) |>
  extract_first_name(short_name)   # Add first_name the the result

Join user and submission information

User.Submissions <- 
  left_join(Submissions, Users, by="user_id")

Read in scores and feedback

Scores <- readRDS("canvas/score.feedback.rds")

Check that Scores and Submissions match

length(User.Submissions$login_id)
[1] 111
length(Scores$SIS_Login_ID)
[1] 112

Yikes!

base::setdiff(
  Scores$SIS_Login_ID,
  User.Submissions$login_id
)
[1] "n11290803"

This student is listed as having WITHDRAWN on QUT Virtual. So, it’s OK to do the following join

User.Submissions.Scores <-
  left_join(User.Submissions, Scores, join_by(login_id==SIS_Login_ID))

Add on another file to upload

Render <-
  tibble(
    filepath_render=list.files(path="./render.html", pattern="n*.html", full.names = TRUE)
  ) |>
  mutate(login_id=str_extract(filepath_render, "n[0-9]{7,8}"))
User.Submissions.Scores.Upload <-
  User.Submissions |>
  left_join(Scores, join_by(login_id==SIS_Login_ID)) |>
  left_join(Render, by="login_id") |>
  mutate(
    course_id=DSB100,
    comment="",
    do_upload=FALSE,
    file_id_feedback=0L,
    file_id_render=0L,
    comment_submission_id=0L,
    comment_submitted_at=as_datetime(0)
  ) |>
    select(
    course_id, assignment_id, submission_id, attempt,
    user_id, login_id, first_name, sortable_name, 
    score, comment, 
    filepath_feedback, file_id_feedback, 
    filepath_render, file_id_render,
    do_upload, 
    comment_submission_id, 
    comment_submitted_at
  ) 
make_comment <- function(first_name, login_id){
  paste0(
    "Dear ", first_name, " (", login_id, "),\n",
    "We have attached two files to this comment:\n",
    "1. detailed feedback on your submission\n",
    "2. the html that we were able to render.\n\n",
    "With kind regards,\n",
    "The DSB100 Teaching Team\n\n",
    "Comment uploaded at: ", now() |> strftime("%Y-%m-%d %H:%M:%S\n")
  )
}

Upload to Canvas

I’m going to use a for loop of 1 to try things out

User.Submissions.Scores.Upload$do_upload[]    <- FALSE
User.Submissions.Scores.Upload$do_upload[1:3] <- TRUE
User.Submissions.Scores.Upload$do_upload[] <- TRUE
N  <- 3 * sum(User.Submissions.Scores.Upload$do_upload)
pb <- progress_bar$new(total = N)

for(i in 1:nrow(User.Submissions.Scores.Upload)){
  student <- User.Submissions.Scores.Upload[i,]
  
  # Skip students who we don't want to do an upload for
  if(student$do_upload){
    # Prepare the comment
    newcomment <- with(student, make_comment(first_name, login_id))

    pb$tick()
    # Upload the feedback file and get its Canvas file_id
    response <-
      with(
        student,
        Upload_submission_comments_file(
          course_id       = course_id, 
          assignment_id   = assignment_id, 
          user_id         = user_id, 
          canvas_filename = paste0(login_id,".feedback.html"),
          local_filepath  = filepath_feedback
        )
      )
    file_id1 <- response$id
    
    pb$tick()
    # Upload the rendered html file and get its Canvas file_id
    response <-
      with(
        student,
        Upload_submission_comments_file(
          course_id       = course_id, 
          assignment_id   = assignment_id, 
          user_id         = user_id, 
          canvas_filename = paste0(login_id,".render.html"),
          local_filepath  = filepath_render
        )
      )
    file_id2 <- response$id

    pb$tick()
    result <- 
      with(
        student,
        Grade_or_comment_on_a_submission(
          course_id                  = course_id, 
          assignment_id              = assignment_id, 
          user_id                    = user_id, 
          attempt                    = attempt, 
          `submission[posted_grade]` = score,
          `comment[text_comment]`    = newcomment,
          `comment[file_ids][]`      = file_id1,
          `comment[file_ids][]`      = file_id2
        )
      )
    
    # Keep a record
    User.Submissions.Scores.Upload[i,"comment"                 ] <- newcomment
    User.Submissions.Scores.Upload[i,"file_id_feedback"        ] <- file_id1
    User.Submissions.Scores.Upload[i,"file_id_render"          ] <- file_id2
    User.Submissions.Scores.Upload[i,"comment_submission_id"   ] <- result$submission_id
    User.Submissions.Scores.Upload[i,"comment_submitted_at"    ] <- result$submitted_at
  }
}

# Write the record to file
saveRDS(
  User.Submissions.Scores.Upload, 
  paste0(
    "./rds/User.Submissions.Scores.Upload.",
    now() |> strftime("%Y%m%d%H%M%S.rds")
  )
)