I want to upload the HTML files generated by the nops_eval()
command to Moodle.
I have found a very good explanation on how to do a bulk upload of feedback files to a Moodle Assignment: https://telsupport.brookes.ac.uk/articles/how-do-i-bulk-upload-feedback-files-to-a-moodle-assignment/
However, the naming convention that nops_eval()
applies to the folders containing the HTML file is not the one I need. When I change a folder by hand to the following naming convention:
Fullname_idnumber_assignsubmission_file_
i.e., for example
Edward Green_6028662_assignsubmission_file_
it works for me (following the instructions above).
Downloading the grading worksheet (CSV) in our university gives me the full name and the id-number in the following format (one line per student, all info in one cell):
Identificativo,Nome,"Codice identificativo","Indirizzo email",Stato,Valutazione,"Valutazione massima","La valutazione può essere cambiata","Ultima modifica (consegna)","Ultima modifica (valutazione)",Commenti
Partecipante6028662,"EDWARD GREEN",789012,egreen@student.unversity.xx,"Nessuna consegna",,"100,00",Sì,-,-,
Is there a way I can tell nops_eval()
to format the feedback folders accordingly?
The "quick & dirty" solution is to proceed in the following two steps:
nops_eval()
.nops_eval()
can then be loaded, transformed and re-saved to be suitable for import into Moodle.To obtain a CSV registration file as described in the official NOPS tutorial I will start out from the grading sheet you posted, say grading_sheet.csv
, and produce the file Exam-2025-06-25.csv
that can then be used as input for nops_eval()
.
## read grading sheet
x <- read.csv("grading_sheet.csv")
x <- x[, 3:1]
names(x) <- c("registration", "name", "id")
## omit "Partecipante" in id column
x$id <- gsub("Partecipante", "", x$id, fixed = TRUE)
## add a leading zero to the registration column
x$registration <- sprintf("%07d", x$registration)
## write everything to semicolon-separated CSV file
write.table(x, file = "Exam-2025-06-25.csv", sep = ";", quote = FALSE, row.names = FALSE)
The output Exam-2025-06-25.csv
has then the following format:
registration;name;id
0789012;EDWARD GREEN;6028662
The reason for pre-fixing the registration with a zero is that that exams2nops()
currently has a minimum seven digits but by using exams2nops(..., reglength = 6)
you can at least fix the first digit to be 0. For more details see:
In your case you could also use the id from Codice identificativo instead of the registration Identificativo for both registration and id. Not sure what is the most intuitive solution for your students.
(Remark: It would also be possible to convert "EDWARD GREEN" to "Edward Green" programmatically if that is desired.)
When you run nops_eval()
as described in the tutorial linked above, it will produce an output nops_eval.zip
that contains an individual HTML report for the students with the file path
6028662/Exam-2025-06-25.html
that needs to be converted to
EDWARD GREEN_6028662_assignsubmission_file_.html
if I understand you correctly. You can do this via
transform_zip_for_moodle("Exam-2025-06-25.csv", "nops_eval.zip")
using the following function definition:
transform_zip_for_moodle <- function(
register = dir(pattern = "\\.csv$"),
eval = "nops_eval.zip"
) {
## set up and switch to temporary directory
odir <- getwd()
tdir <- tempfile()
dir.create(tdir)
setwd(tdir)
on.exit(setwd(odir))
## extract ZIP in temporary directory
unzip(file.path(odir, "nops_eval.zip"))
## original and new file names
x <- read.csv2(file.path(odir, register), colClasses = "character")
ofile <- file.path(x$id, gsub("\\.csv$", ".html", register))
nfile <- sprintf("%s_%s_assignsubmission_file_.html", x$name, x$id)
if(!all(file.exists(ofile))) stop("the 'eval' zip file does not contain the html reports expected from the 'register' csv file")
## rename files, zip again, overwrite original ZIP file
for(i in seq_along(ofile)) file.rename(ofile[i], nfile[i])
zip("nops_eval.zip", nfile)
file.copy("nops_eval.zip", file.path(odir, "nops_eval.zip"), overwrite = TRUE)
## return new file names in ZIP invisibly
invisible(nfile)
}
Rather than applying the hot fix to the nops_eval.zip
after it was created, it would be better to create the correct file in the first place. In principle, this is possible by setting up your own nops_eval_write_moodle()
function and to plug that into nops_eval(..., flavor = "moodle")
. In fact, one of the reasons that my co-author Kenji Sato modularized the nops_eval_write_xxx()
flavors was to be able to plug in a Moodle writer. Unfortunately, we did not complete this work...probably because we wanted to implement too many other convenience features along with it, including a more modular HTML layout etc., and never were satisfied with the details.
If anyone wants to set up a proper nops_eval_write_moodle()
you should be able to write and test this on your own without modifying the exams
package. And then you could let us know about this.