diff --git a/NAMESPACE b/NAMESPACE index ed91356..963582f 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -22,6 +22,10 @@ export(get_assignment_data) export(get_assignment_details) export(get_assignment_groups) export(get_assignment_submissions) +export(get_assignment_submissions_new) +export(get_course_gradebook) +export(get_quiz_submissions) +export(get_submissions) export(get_assignments) export(get_calendar_events) export(get_course_announcements) diff --git a/R/get_assignment_submissions.R b/R/get_assignment_submissions.R index 9b511c3..7677d2b 100644 --- a/R/get_assignment_submissions.R +++ b/R/get_assignment_submissions.R @@ -1,37 +1,41 @@ #' Lists assignment submissions for a course. #' #' This function retrieves a list of assignment submissions for a specific course in the Canvas LMS API. +#' If an assignment_id is provided, it will use the specific assignment endpoint. Otherwise, it will +#' attempt to get all submissions using the students/submissions endpoint, which may not be available +#' in all Canvas systems. #' #' @param canvas An object containing the Canvas API key and base URL, obtained through the `canvas_authenticate` function. #' @param course_id The ID of the course for which to list assignment submissions. -#' @param assignment_id (Optional) The ID of a specific assignment for which to list submissions. +#' @param assignment_id (Optional) The ID of a specific assignment for which to list submissions. If provided, uses the more reliable assignment-specific endpoint. #' @param per_page (Optional) The number of submissions to retrieve per page of results (default is 100). #' #' @return A data frame of assignment submissions for the specified course and assignment. #' @export get_assignment_submissions <- function(canvas, course_id, assignment_id = NULL, per_page = 100) { - # Construct the API endpoint URL - url <- paste0(canvas$base_url, "/api/v1/courses/", course_id, "/students/submissions?per_page=", per_page) - - # Append the assignment_id if provided + # If assignment_id is provided, use the specific assignment endpoint if (!is.null(assignment_id)) { - url <- paste0(url, "&assignment_id=", assignment_id) - } - - # Make the API request - response <- httr::GET(url, httr::add_headers(Authorization = paste("Bearer", canvas$api_key))) + # Use the new get_submissions function for specific assignment + return(get_submissions(canvas, course_id, "assignments", assignment_id, per_page)) + } else { + # For getting all submissions, try the students/submissions endpoint first + # and fall back to iterating through assignments if it fails + url <- paste0(canvas$base_url, "/api/v1/courses/", course_id, "/students/submissions?per_page=", per_page) + + # Make the API request + response <- httr::GET(url, httr::add_headers(Authorization = paste("Bearer", canvas$api_key))) - # Use pagination helper to get all pages - responses <- paginate(response, canvas$api_key) + # Use pagination helper to get all pages + responses <- paginate(response, canvas$api_key) - # Parse and combine all results - submissions_list <- lapply(responses, function(resp) { - httr::content(resp, "text", encoding = "UTF-8") %>% - jsonlite::fromJSON(flatten = TRUE) %>% - as.data.frame() - }) - submissions <- dplyr::bind_rows(submissions_list) + # Parse and combine all results + submissions_list <- lapply(responses, function(resp) { + httr::content(resp, "text", encoding = "UTF-8") %>% + jsonlite::fromJSON(flatten = TRUE) %>% + as.data.frame() + }) + submissions <- dplyr::bind_rows(submissions_list) - # Return the data frame of submissions - return(submissions) + # Return the data frame of submissions + return(submissions) } diff --git a/R/get_course_gradebook.R b/R/get_course_gradebook.R new file mode 100644 index 0000000..784487f --- /dev/null +++ b/R/get_course_gradebook.R @@ -0,0 +1,98 @@ +#' Get course gradebook with all assignment and quiz submissions +#' +#' This function retrieves all assignment and quiz submissions for a course and combines them +#' into a single data frame. This is inspired by Jon Vik's work and provides a comprehensive +#' view of all student submissions in a course. +#' +#' @param canvas An object containing the Canvas API key and base URL, obtained through the `canvas_authenticate` function. +#' @param course_id The ID of the course for which to retrieve all submissions. +#' @param per_page (Optional) The number of submissions to retrieve per page of results (default is 100). +#' @param include_quizzes (Optional) Whether to include quiz submissions (default is TRUE). +#' @param include_assignments (Optional) Whether to include assignment submissions (default is TRUE). +#' +#' @return A data frame containing all submissions (assignments and quizzes) for the specified course. +#' @export +#' +get_course_gradebook <- function(canvas, course_id, per_page = 100, include_quizzes = TRUE, include_assignments = TRUE) { + gradebook_data <- list() + + # Get assignments and their submissions + if (include_assignments) { + tryCatch({ + assignments <- get_assignments(canvas, course_id) + + if (nrow(assignments) > 0) { + assignment_submissions <- list() + + for (i in 1:nrow(assignments)) { + assignment_id <- assignments$id[i] + assignment_name <- assignments$name[i] + + tryCatch({ + submissions <- get_submissions(canvas, course_id, "assignments", assignment_id, per_page) + + if (length(submissions) > 0 && nrow(submissions) > 0) { + submissions$assignment_name <- assignment_name + submissions$assignment_id <- assignment_id + submissions$submission_type <- "assignment" + assignment_submissions[[as.character(assignment_id)]] <- submissions + } + }, error = function(e) { + message("Warning: Failed to get submissions for assignment ", assignment_id, ": ", e$message) + }) + } + + if (length(assignment_submissions) > 0) { + gradebook_data$assignments <- do.call(rbind, assignment_submissions) + } + } + }, error = function(e) { + message("Warning: Failed to get assignments: ", e$message) + }) + } + + # Get quizzes and their submissions + if (include_quizzes) { + tryCatch({ + quizzes <- get_course_quizzes(canvas, course_id, per_page) + + if (nrow(quizzes) > 0) { + quiz_submissions <- list() + + for (i in 1:nrow(quizzes)) { + quiz_id <- quizzes$id[i] + quiz_name <- quizzes$title[i] + + tryCatch({ + submissions <- get_submissions(canvas, course_id, "quizzes", quiz_id, per_page) + + if (length(submissions) > 0 && nrow(submissions) > 0) { + submissions$quiz_name <- quiz_name + submissions$quiz_id <- quiz_id + submissions$submission_type <- "quiz" + quiz_submissions[[as.character(quiz_id)]] <- submissions + } + }, error = function(e) { + message("Warning: Failed to get submissions for quiz ", quiz_id, ": ", e$message) + }) + } + + if (length(quiz_submissions) > 0) { + gradebook_data$quizzes <- do.call(rbind, quiz_submissions) + } + } + }, error = function(e) { + message("Warning: Failed to get quizzes: ", e$message) + }) + } + + # Combine all submissions + if (length(gradebook_data) > 0) { + combined_data <- do.call(rbind, gradebook_data) + combined_data$course_id <- course_id + return(combined_data) + } else { + message("No submissions found for course ", course_id) + return(data.frame()) + } +} \ No newline at end of file diff --git a/R/get_quiz_submissions.R b/R/get_quiz_submissions.R new file mode 100644 index 0000000..53117f2 --- /dev/null +++ b/R/get_quiz_submissions.R @@ -0,0 +1,33 @@ +#' Get assignment submissions +#' +#' This function retrieves submissions for a specific assignment from the Canvas LMS API. +#' This is a convenience wrapper around get_submissions() for assignments. +#' +#' @param canvas An object containing the Canvas API key and base URL, obtained through the `canvas_authenticate` function. +#' @param course_id The ID of the course for which to retrieve assignment submissions. +#' @param assignment_id The ID of the assignment for which to retrieve submissions. +#' @param per_page (Optional) The number of submissions to retrieve per page of results (default is 100). +#' +#' @return A data frame of submissions for the specified assignment. +#' @export +#' +get_assignment_submissions_new <- function(canvas, course_id, assignment_id, per_page = 100) { + return(get_submissions(canvas, course_id, "assignments", assignment_id, per_page)) +} + +#' Get quiz submissions +#' +#' This function retrieves submissions for a specific quiz from the Canvas LMS API. +#' This is a convenience wrapper around get_submissions() for quizzes. +#' +#' @param canvas An object containing the Canvas API key and base URL, obtained through the `canvas_authenticate` function. +#' @param course_id The ID of the course for which to retrieve quiz submissions. +#' @param quiz_id The ID of the quiz for which to retrieve submissions. +#' @param per_page (Optional) The number of submissions to retrieve per page of results (default is 100). +#' +#' @return A data frame of submissions for the specified quiz. +#' @export +#' +get_quiz_submissions <- function(canvas, course_id, quiz_id, per_page = 100) { + return(get_submissions(canvas, course_id, "quizzes", quiz_id, per_page)) +} \ No newline at end of file diff --git a/R/get_submissions.R b/R/get_submissions.R new file mode 100644 index 0000000..fa974f3 --- /dev/null +++ b/R/get_submissions.R @@ -0,0 +1,59 @@ +#' Get submissions for assignments or quizzes +#' +#' This function retrieves submissions for assignments or quizzes from a specific course in the Canvas LMS API. +#' It can handle both assignment submissions and quiz submissions using the correct API endpoints. +#' +#' @param canvas An object containing the Canvas API key and base URL, obtained through the `canvas_authenticate` function. +#' @param course_id The ID of the course for which to retrieve submissions. +#' @param type The type of submission to retrieve. Must be either "assignments" or "quizzes". +#' @param id The ID of the assignment or quiz for which to retrieve submissions. +#' @param per_page (Optional) The number of submissions to retrieve per page of results (default is 100). +#' +#' @return A data frame of submissions for the specified assignment or quiz. +#' @export +#' +get_submissions <- function(canvas, course_id, type, id, per_page = 100) { + # Validate the type parameter + if (!type %in% c("quizzes", "assignments")) { + stop("type must be 'quizzes' or 'assignments'") + } + + # Construct the API endpoint URL + url <- paste0(canvas$base_url, "/api/v1/courses/", course_id, "/", + type, "/", id, "/submissions?per_page=", per_page) + + # Make the API request + response <- httr::GET(url, httr::add_headers(Authorization = paste("Bearer", canvas$api_key))) + + # Use pagination helper to get all pages + responses <- paginate(response, canvas$api_key) + + # Parse and combine all results + submissions_list <- lapply(responses, function(resp) { + content_text <- httr::content(resp, "text", encoding = "UTF-8") + + if (type == "quizzes") { + # For quizzes, the response structure is different - need to access the first element + submissions <- jsonlite::fromJSON(content_text, flatten = TRUE) + if (is.list(submissions) && length(submissions) > 0) { + submissions <- submissions[[1]] + } + } else { + # For assignments, parse normally + submissions <- jsonlite::fromJSON(content_text, flatten = TRUE) + } + + # Convert to data frame if not already + if (length(submissions) > 0 && !is.data.frame(submissions)) { + submissions <- as.data.frame(submissions) + } + + return(submissions) + }) + + # Combine all submissions + submissions <- dplyr::bind_rows(submissions_list) + + # Return the submissions data + return(submissions) +} \ No newline at end of file