Skip to content

Commit cd4c0dd

Browse files
authored
Merge pull request #34 from federicazoe/minor-fixes-improvs
Added functionalities: - Feedback files can now be written as .qmd (#8) - Grade decomposition can now be written in feedback file (18) - Assist grading functions can be called with option to not provide student submissions: if file format cannot be opened in RStudio but one still wishes to annotate grades and feedback files with gradetools. (#22) - User can change desired feedback file format (#23) Improved grading experience: - Display question/component total points possible (#21) - Information when canceling grading (#26) - If there are no partially graded assignments, and if there are not then do not ask for those preferences (16) Fix bugs: - Preserve column order in final gradesheet (#19) - Fix rubric example created by create_rubric_template() (#38) - Grading breaks if no general feedback is provided (#39) - Handle missing files in multiple assignments (#13) Improved documentation: - Documentation update needed (#28) - Add preprint (#27) - Better explain which types of assignment files are possible to grade with gradetools (#37)
2 parents fa897c6 + e857245 commit cd4c0dd

33 files changed

+733
-478
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,7 @@ vignettes/*.pdf
4545
inst/doc
4646

4747
docs
48+
49+
# Folder with scripts to assist with package development
50+
develop-ignore/*
51+

DESCRIPTION

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
Package: gradetools
22
Type: Package
33
Title: Tools to Assist with Providing Grades and Personalized Feedback to Students
4-
Version: 0.2.0
4+
Version: 0.3.0
55
Authors@R: c(
66
person(given = "Federica Zoe", family = "Ricci", email = "federicazoericci@gmail.com", role = c("cre", "aut"), comment = c(ORCID = "0000-0002-2560-4543")),
7-
person(given = "Catalina", family = "Medina", role = c("aut"), comment = c(ORCID = "0000-0003-2847-8180")),
7+
person(given = "Catalina Mari", family = "Medina", role = c("aut"), comment = c(ORCID = "0000-0003-2847-8180")),
88
person(given = "Mine", family = "Dogucu", role = c("aut"), comment = c(ORCID = "0000-0002-8007-934X"))
99
)
1010
Description: Provides functions to assist with grading and providing feedback to students.
1111
This package has functions to help create an assignment rubric, assist with grading students' assignments and writing feedback to a file, regrade specified students and assignments, and push feedback and or GitHub issues to GitHub.
1212
License: GPL (>= 3)
1313
Encoding: UTF-8
14-
RoxygenNote: 7.1.2
14+
RoxygenNote: 7.3.2
1515
Imports:
1616
stringr,
1717
readr (>= 2.1.2),
@@ -21,7 +21,7 @@ Imports:
2121
fs,
2222
utils,
2323
svDialogs,
24-
rmarkdown
24+
quarto
2525
Suggests:
2626
knitr,
2727
testthat (>= 3.0.0)

NAMESPACE

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,17 @@ import(dplyr)
1010
import(fs)
1111
import(ghclass)
1212
import(readr)
13-
import(rmarkdown)
1413
import(rstudioapi)
1514
import(stringr)
1615
import(svDialogs)
1716
importFrom(fs,file_create)
1817
importFrom(fs,path_ext)
1918
importFrom(fs,path_ext_set)
19+
importFrom(quarto,quarto_render)
2020
importFrom(readr,read_csv)
2121
importFrom(readr,write_file)
2222
importFrom(stringr,str_detect)
23+
importFrom(stringr,str_split)
24+
importFrom(svDialogs,dlg_input)
2325
importFrom(svDialogs,dlg_message)
2426
importFrom(utils,write.csv)

R/add_rubric_item.R

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ add_rubric_item <- function(curr_q, rubric_path, rubric_list) {
2020
rubric_path,
2121
show_col_types = FALSE,
2222
col_types = readr::cols(
23-
name = col_character(),
24-
prompt_code = col_character(),
25-
prompt_message = col_character(),
26-
feedback = col_character(),
27-
.default = col_double()
23+
name = readr::col_character(),
24+
prompt_code = readr::col_character(),
25+
prompt_message = readr::col_character(),
26+
feedback = readr::col_character(),
27+
.default = readr::col_double()
2828
),
2929
skip_empty_rows = TRUE
3030
)
@@ -45,7 +45,7 @@ add_rubric_item <- function(curr_q, rubric_path, rubric_list) {
4545
existing_prompt_codes_message <- paste(
4646
"You are about to choose a prompt code.",
4747
"Prompt codes",
48-
str_c(existing_prompt_codes, collapse = ", "),
48+
stringr::str_c(existing_prompt_codes, collapse = ", "),
4949
"already exist.",
5050
"Valid prompt codes must begin with a digit and cannot include spaces."
5151
)
@@ -70,10 +70,10 @@ add_rubric_item <- function(curr_q, rubric_path, rubric_list) {
7070
# (verify it differs from current prompt codes)
7171
prompt_code_assigned <- FALSE
7272

73-
dlg_message(existing_prompt_codes_message, type = "ok")
73+
svDialogs::dlg_message(existing_prompt_codes_message, type = "ok")
7474

7575
while (prompt_code_assigned == FALSE) {
76-
prompt_code <- dlg_input(
76+
prompt_code <- svDialogs::dlg_input(
7777
paste(
7878
"Type the new PROMPT CODE and press [ok].",
7979
"To undo adding a rubric item, press [cancel].",
@@ -91,34 +91,34 @@ add_rubric_item <- function(curr_q, rubric_path, rubric_list) {
9191
"Please enter a new prompt code."
9292
)
9393

94-
dlg_message(prompt_code_must_start_with_a_digit, type = "ok")
94+
svDialogs::dlg_message(prompt_code_must_start_with_a_digit, type = "ok")
9595

9696
} else if (prompt_code %in% as.character(existing_prompt_codes)) {
9797
# Check for validity of prompt code
9898
type_different_prompt_code <- paste(
9999
"Prompt codes",
100-
str_c(existing_prompt_codes, collapse = ", "),
100+
stringr::str_c(existing_prompt_codes, collapse = ", "),
101101
"already exist and cannot be reset.",
102102
"To reset an existing prompt code, please interrupt grading and modify your rubric."
103103
)
104104

105-
dlg_message(type_different_prompt_code, type = "ok")
105+
svDialogs::dlg_message(type_different_prompt_code, type = "ok")
106106

107-
} else if (str_detect(prompt_code, " ")) {
107+
} else if (stringr::str_detect(prompt_code, " ")) {
108108
no_spaces_in_prompt_code <- paste(
109109
"Prompt codes cannot include spaces.",
110110
"Please enter a new prompt code."
111111
)
112112

113-
dlg_message(no_spaces_in_prompt_code, type = "ok")
113+
svDialogs::dlg_message(no_spaces_in_prompt_code, type = "ok")
114114

115115
} else {
116116
prompt_code_assigned <- TRUE
117117
}
118118
}
119119

120120
# Obtain prompt_message from user
121-
prompt_message <- dlg_input(
121+
prompt_message <- svDialogs::dlg_input(
122122
paste(
123123
"Type the new PROMPT MESSAGE and press [ok].",
124124
"To undo adding a rubric item, press [cancel].",
@@ -131,7 +131,7 @@ add_rubric_item <- function(curr_q, rubric_path, rubric_list) {
131131
}
132132

133133
# Obtain feedback from user
134-
feedback <- dlg_input(
134+
feedback <- svDialogs::dlg_input(
135135
paste(
136136
"Type the new FEEDBACK and press [ok].",
137137
"To undo adding a rubric item, press [cancel].",
@@ -161,11 +161,11 @@ add_rubric_item <- function(curr_q, rubric_path, rubric_list) {
161161
paste0("The total points for this question are: ", total_points)
162162
)
163163

164-
dlg_message(message_points, type = "ok")
164+
svDialogs::dlg_message(message_points, type = "ok")
165165

166166
while (points_assigned == FALSE) {
167167

168-
points_for_item <- dlg_input(
168+
points_for_item <- svDialogs::dlg_input(
169169
paste(
170170
paste("Type", points_type ,"and press [okay]."),
171171
"To undo adding a rubric item, press [cancel].",
@@ -184,7 +184,7 @@ add_rubric_item <- function(curr_q, rubric_path, rubric_list) {
184184
paste0("Recall that the total points for this question are: ", total_points)
185185
)
186186

187-
dlg_message(need_numeric_points, type = "ok")
187+
svDialogs::dlg_message(need_numeric_points, type = "ok")
188188

189189
} else {
190190
points_assigned <- TRUE
@@ -228,7 +228,7 @@ add_rubric_item <- function(curr_q, rubric_path, rubric_list) {
228228
sep = "\n"
229229
)
230230

231-
proceed <- ok_cancel_box(proceed_message)
231+
proceed <- svDialogs::ok_cancel_box(proceed_message)
232232

233233
if (proceed == TRUE) {
234234
if (negative_grading == TRUE) {
@@ -256,7 +256,7 @@ add_rubric_item <- function(curr_q, rubric_path, rubric_list) {
256256
)
257257
}
258258

259-
write_csv(rubric, rubric_path)
259+
readr::write_csv(rubric, rubric_path)
260260

261261
rubric_list <- import_rubric(rubric_path = rubric_path)
262262

R/assign_grade_write_feedback.R

Lines changed: 59 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#' @param grading_progress_log_row data frame with 1 row; The grading_progress_log is a data frame containing information for gradetools internal use. This row should correspond to the student that needs to be graded and have feedback written
44
#' @param rubric_list list of lists; each sub-list corresponds to a part of the assignment rubric and specifies the name of this part (e.g. "question_1"), the total points that can be earned for this part, the rubric etc. Rubric should be made using 'create_rubric_template()'.
55
#' @param rubric_prompts list of prompts; One prompt for each question plus one for overall feedback. This is produced by create_rubric_prompts
6+
#' @param write_grades_into_feedback logical, whether to write numeric grades into the feedback file, along with qualitative feedback (defaults to FALSE).
67
#'
78
#' @return list with\describe{
89
#' \item{grade}{A number, the total grade scored for this assignment by the student being graded (e.g. 3)}
@@ -18,16 +19,17 @@
1819
assign_grade_write_feedback <- function(
1920
grading_progress_log_row,
2021
rubric_list,
21-
rubric_prompts
22+
rubric_prompts,
23+
write_grades_into_feedback = FALSE
2224
) {
2325

2426
# Get feedback and grade corresponding to entered code
25-
feedback_code <- unlist(str_split(
27+
feedback_code <- unlist(stringr::str_split(
2628
grading_progress_log_row$feedback_codes,
2729
pattern = "&&&"
2830
))
2931

30-
questions_graded <- unlist(str_split(
32+
questions_graded <- unlist(stringr::str_split(
3133
grading_progress_log_row$graded_qs,
3234
pattern = "&&&"
3335
))
@@ -36,7 +38,7 @@ assign_grade_write_feedback <- function(
3638
question_matches <- order(match(questions_graded, names(rubric_prompts)))
3739

3840
num_questions <- sum(
39-
str_count(names(rubric_list), "question_")
41+
stringr::str_count(names(rubric_list), "question_")
4042
)
4143

4244
if (!is.null(rubric_list[[1]]$points_to_remove)) {
@@ -84,7 +86,7 @@ assign_grade_write_feedback <- function(
8486

8587
# Extract all codes given to this question
8688
# E.g. "2---77" into "2" and "77"
87-
q_feedback_code <- str_split(
89+
q_feedback_code <- stringr::str_split(
8890
feedback_code[questions_graded == q],
8991
pattern = "---"
9092
)[[1]]
@@ -94,20 +96,20 @@ assign_grade_write_feedback <- function(
9496

9597
# Write question comment if present
9698
if (!is.na(grading_progress_log_row$comments)) {
97-
comments <- unlist(str_split(
99+
comments <- unlist(stringr::str_split(
98100
grading_progress_log_row$comments,
99101
pattern = "&&&"
100102
))
101103

102-
comment_qs <- unlist(str_split(
104+
comment_qs <- unlist(stringr::str_split(
103105
grading_progress_log_row$comment_qs,
104106
pattern = "&&&"
105107
))
106108

107109
if (q %in% comment_qs) {
108110
q_fbk <- paste0(
109111
q_fbk, "\n",
110-
str_c(comments[which(comment_qs == q)], collapse = "\n"), "\n"
112+
stringr::str_c(comments[which(comment_qs == q)], collapse = "\n"), "\n"
111113
)
112114
}
113115

@@ -144,6 +146,10 @@ assign_grade_write_feedback <- function(
144146

145147
q_fbk <- paste(q_fbk, new_fbk, sep = "\n\n")
146148

149+
if (write_grades_into_feedback == TRUE & new_change != 0) {
150+
q_fbk <- paste(q_fbk, paste0("(", new_change, " points)"), sep = "\n")
151+
}
152+
147153
q_grade <- q_grade + new_change
148154

149155
}
@@ -159,6 +165,13 @@ assign_grade_write_feedback <- function(
159165
}
160166

161167
# Store question feedback, grade, and grade decomposition
168+
if (write_grades_into_feedback == TRUE) {
169+
q_fbk <- paste(
170+
q_fbk,
171+
paste0("Grade: ", q_grade, " (of ", q_info$total_points, ")"),
172+
sep = "\n\n"
173+
)
174+
}
162175
feedback <- paste(feedback, q_fbk, sep = "\n\n")
163176
overall_grade <- overall_grade + q_grade
164177
grade_decomposition <- c(grade_decomposition, q_grade)
@@ -172,19 +185,19 @@ assign_grade_write_feedback <- function(
172185
gf_comments <- NULL
173186

174187
if (!is.na(grading_progress_log_row$comments)) {
175-
comments <- unlist(str_split(
188+
comments <- unlist(stringr::str_split(
176189
grading_progress_log_row$comments,
177190
pattern = "&&&"
178191
))
179192

180-
comment_qs <- unlist(str_split(
193+
comment_qs <- unlist(stringr::str_split(
181194
grading_progress_log_row$comment_qs,
182195
pattern = "&&&"
183196
))
184197

185198
if ("general_feedback" %in% comment_qs) {
186199
gf_comments <- paste0(
187-
str_c(
200+
stringr::str_c(
188201
comments[which(comment_qs == "general_feedback")],
189202
collapse = "\n"
190203
),
@@ -195,7 +208,7 @@ assign_grade_write_feedback <- function(
195208

196209
}
197210

198-
gf_separated <- str_split(
211+
gf_separated <- stringr::str_split(
199212
feedback_code[questions_graded == "general_feedback"],
200213
pattern = "---"
201214
)[[1]]
@@ -245,40 +258,51 @@ assign_grade_write_feedback <- function(
245258
# Delete student's old feedback if it exists and write a new one
246259
# This is important because the rubric could have changed
247260
if(grading_progress_log_row$grading_status != "ungraded") {
248-
unlink(grading_progress_log_row$feedback_path_Rmd)
249-
unlink(grading_progress_log_row$feedback_path_to_be_knitted)
261+
unlink(grading_progress_log_row$feedback_path_qmd)
262+
unlink(grading_progress_log_row$feedback_path_to_be_rendered)
250263

251264
}
252265

253-
fs::file_create(grading_progress_log_row$feedback_path_Rmd)
266+
fs::file_create(grading_progress_log_row$feedback_path_qmd)
254267

255-
# Determing type of file to knit feedback to
268+
# Determining type of file to knit feedback to
256269
feedback_file_ext <- as.character(fs::path_ext(
257-
grading_progress_log_row$feedback_path_to_be_knitted)[1]
270+
grading_progress_log_row$feedback_path_to_be_rendered)[1]
258271
)
259272

260-
feedback_knit_type <- case_when(
261-
feedback_file_ext == "Rmd" ~ "html_document",
262-
feedback_file_ext == "html" ~ "html_document",
263-
feedback_file_ext == "docx" ~ "word_document",
264-
feedback_file_ext == "pdf" ~ "pdf_document",
265-
feedback_file_ext == "md" ~ "github_document",
266-
TRUE ~ "NA",
267-
)
273+
if(feedback_file_ext == "qmd") {
274+
feedback_file_ext <- "html"
275+
}
268276

269-
# Create YAML section in feedback file
270-
yaml <- paste(
271-
"---",
272-
'title: "Feedback"',
273-
paste0('output: ', feedback_knit_type),
274-
"---\n",
275-
sep = "\n"
276-
)
277+
if (feedback_file_ext == "Rmd") {
278+
grading_progress_log_row$feedback_path_qmd <- fs::path_ext_set(
279+
grading_progress_log_row$feedback_path_qmd,
280+
"Rmd"
281+
)
282+
283+
yaml <- paste(
284+
"---",
285+
"title: 'Feedback'",
286+
"output: html_document",
287+
"---\n",
288+
sep = "\n"
289+
)
290+
291+
} else {
292+
yaml <- paste(
293+
"---",
294+
"title: 'Feedback'",
295+
paste0("format: ", feedback_file_ext),
296+
"---\n",
297+
sep = "\n"
298+
)
299+
300+
}
277301

278302
# Write feedback in feedback file
279-
write_file(
303+
readr::write_file(
280304
x = paste0(yaml, "\n", feedback , "\n\n"),
281-
file = grading_progress_log_row$feedback_path_Rmd,
305+
file = grading_progress_log_row$feedback_path_qmd,
282306
append = TRUE
283307
)
284308

0 commit comments

Comments
 (0)