Grader.jl Documentation
Grader is an assignment autograder for Julia. It has been designed to be used with the PrairieLearn learning system, but it could also be used with any other learning system.
Examples
Simple example
Here is a simple example of how to use Grader:
using Grader
# This is the code with the correct answer:
goldencode = """
x=2
y = x * 3
"""
# This is the code with the student's answer:
studentcode = """
x=2
y = x + x + x
"""
p = Problem()
golden = @rungolden! p goldencode
student = @runstudent! p studentcode
grade!(p, "y", "check y", 2, :($student.y ≈ $golden.y), "y is incorrect")
p
# output
Problem
gradable: Bool true
score: Float64 1.0
message: String ""
output: String ""
images: Array{Grader.Image}((0,))
tests: Array{Grader.TestResult}((1,))
Example with incorrect answer
Here's an example of what it looks like when the student gets the wrong answer:
using Grader
# This is the code with the correct answer:
goldencode = """
x=2
y = x * 3
"""
# This is the code with the student's answer:
studentcode = """
x=2
y = x + x + 2x
"""
p = Problem()
golden = @rungolden! p goldencode
student = @runstudent! p studentcode
grade!(p, "y", "check y", 2, :($student.y ≈ $golden.y), "y is incorrect")
p.tests[1]
# output
Grader.TestResult
name: String "y"
description: String "check y"
points: Float64 0.0
max_points: Float64 2.0
message: String "y is incorrect"
output: String ""
images: Array{Grader.Image}((0,))
We can also forbid students from using certain symbols (e.g libraries):
using Grader
studentcode = """
using LinearAlgebra
x=2
y = x + x + 2x
"""
p = Problem()
@runstudent! p studentcode [:LinearAlgebra]
p.output[1:63]
# output
"error running student code:\nUsing LinearAlgebra is not allowed."
Example with plot
This is a more complex example that grades a plot and writes out the output as JSON.
using Grader
using LinearAlgebra, JSON
studentcode = """
using Plots
x=1:10
y = x.^2
xy = plot(x,y)
"""
p = Problem()
student = @runstudent! p studentcode
grade!(p, "XY Plot", "Data length", 1,
quote
xlen = length($student.xy[1][1][:x])
ylen = length($student.xy[1][1][:y])
xlen == ylen == 10
end,
"the number of data points in x or y is incorrect")
grade!(p, "XY Plot", "Y values", 3,
quote
ynorm = $norm($student.xy[1][1][:y])
ynorm ≈ 159.16343801262903
end,
"The Y values are not correct")
# Output to JSON and print the result
b = IOBuffer()
pl_JSON(b, p)
JSON.print(JSON.parse(String(take!(b))), 4)
# output
{
"images": [],
"score": 1.0,
"output": "",
"message": "",
"gradable": true,
"tests": [
{
"images": [],
"name": "XY Plot",
"points": 1.0,
"output": "",
"message": "",
"max_points": 1.0,
"description": "Data length"
},
{
"images": [],
"name": "XY Plot",
"points": 3.0,
"output": "",
"message": "",
"max_points": 3.0,
"description": "Y values"
}
]
}
API Documentation
Grader.Image
Grader.Problem
Grader.TestResult
Grader.asmodexpr
Grader.fill_answers
Grader.grade!
Grader.pl_JSON
Grader.@rungolden!
Grader.@runstudent!
Grader.Image
— TypeRepresents an image with a label and url.
Grader.Problem
— TypeRepresent a problem for grading.
Fields:
- Gradable: whether the problem is gradable, i.e. whether the all relavent code has executed without any errors
- Score: the score of the problem, as a fraction of the maximum possible score
- Message: Any message associated with the graded problem
- Output: The output of the problem grading
- Images: Any images associated with the problem grading
- Tests: A list of tests associated with the problem
Grader.TestResult
— TypeRepresents the result of a grade!
action.
Grader.asmodexpr
— MethodReturn an expression representing the given code inside of a module.
Grader.fill_answers
— Methodfill_answers(code::AbstractString, answerkey::Dict)::String
Fill the answer key into a code template and return the resulting code string.
For example, consider the code template below, that gives the radius of a circle and asks the student to fill in the area and perimeter. In the template, the area and perimeter are given as variables a
and p
with values missing
, and the student is meant to replace missing
with the actual answer.
code = "# Calculate the area and perimeter of a circle with radius 2.\n"*
"r = 2\n"*
"a = missing # Area\n"*
"p = missing # Perimeter\n"
answer_code = fill_answers(code, Dict(
:(a = missing) => :(a = π * r^2),
:(p = missing) => :(p = 2π * r)));
p = Problem()
answer = @runstudent! p answer_code
grade!(p, "area", "calculate area", 1, :($answer.a ≈ 4π), "area is incorrect")
grade!(p, "perimeter", "calculate perimeter", 1, :($answer.p ≈ 4π), "perimeter is incorrect")
println("Answer key score is $(p.score).")
# output
Answer key score is 1.0.
Grader.grade!
— Methodgrade!(p::Problem, name::String, description::String, points::Real, expr::Expr, msg_if_incorrect::String)
Add a grade for problem p
. This grade will have the given name
and description
in the grader output, and will be associated with the number of points
.
The function will evaluate the given expression expr
; if it evaluates to true, the given number of points
will be awarded, otherwise zero points will be awarded and msg_if_incorrect
will be logged to the problem p
.
Grader.pl_JSON
— Methodpl_JSON(io::IO, p::Problem)
Write a the contents of the Problem
p
to the IO stream io
as a PrairieLearn-compatible JSON file.
Grader.@rungolden!
— Macrorungolden! p::Problem goldencode::AbstractString
Run the provided code string inside a module and return the module. If an error occurs it will be logged in Problem
p
as a problem with the "golden" code.
Grader.@runstudent!
— Macro@runstudent! p::Problem studentcode::AbstractString
Run the provided code string inside a module and return the module. If an error occurs it will be logged in Problem
p
as a problem with the "student" code.