Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions vignettes/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.html
*.R
215 changes: 215 additions & 0 deletions vignettes/a-000-introduction_to_gradethis.Rmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
---
title: "An Introduction to gradethis"
output: rmarkdown::html_vignette
vignette: |-
>
%\VignetteIndexEntry{An Introduction to gradethis}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---

**gradethis** is a package to to grade code and provide meaningful feedback in
[learnr][learnr] interactive tutorials or
as a standalone code grader.
It gives instructors the ability to capture code submitted by a student and
check it in one of 3 ways:

1. `grade_code`:
**Compares the code** to see if the student code exactly matches the solution code provided
by the instructor.
2. `grade_result`:
**Checks the result** to see if the student returns a matching result,
regardless the code.
3. `grade_conditions`:
**Compares all result conditions** to see if a student's function passes all test cases,
analogous to running unittests.

You can see the slides for
[satRday 2020](http://dc2020.netlify.com/)
on `gradethis`
[here](https://speakerdeck.com/chendaniely/satrday-grading-code-with-gradethis).

# Using `gradethis` in `learnr` documents

`gradethis` can be used to grade exercises within `learnr` documents.
This gives the student meaningful feedback as they work through the interactive `learnr` document.
`learnr` and `gradethis` interact with one another with a series of named chunks.
Depending on the suffix used in the chunk name, e.g., `-solution`, `-check`,
`gradethis` will use one of the grading methods mentioned above.

## A convenient addin

Since a (unique) chunk name is tied together with each `learnr` example,
`gradethis` comes with an [RStudio addin][addin]
that easily adds the chunks needed for each type of check.
This will conviently generate a random chunk label with the corresponding chunk suffixes
depending on which grading method the instructor plans to use.

You can even bind these functions to a [keyboard shortcut][addin-keyboard_shortcut]!

## Grade code

In order to grade and compare the student's code to the solution,
there needs the base chunk, a `-solution` chunk, and a `-check` chunk.
`-hint` chunks are optional.
For example,

````markdown
`r ''````{r grade_code, exercise = TRUE}
# student code
____
```

`r ''````{r grade_code-hint-1}
# hint text
""
```

`r ''````{r grade_code-hint-2}
# hint text
""
```

`r ''````{r grade_code-solution}
# solution code

```

`r ''````{r grade_code-check}
# check code
gradethis::grade_code()
```
````

The `-solution` chunk is where the instructor's solution code to be compared will be placed.
The call to `grade_code` within the `-check` chunk is where the instructor can modify the default
success or failure message.


## Grade result

Instead of just comparing the code expressions to one another,
another way to check the student's code is comparing at the student's final solution.
In this case, we do not need a `-solution` chunk.
For example,


````markdown
`r ''````{r grade_result, exercise = TRUE}
# student code
____
```

`r ''````{r grade_result-hint-1}
# hint text
""
```

`r ''````{r grade_result-hint-2}
# hint text
""
```

`r ''````{r grade_result-check}
gradethis::grade_result(
gradethis::pass_if(~ identical(.result, 1), \"YAY!\"),
gradethis::fail_if(~ identical(.result, 2), \"Try Again.\")
)
```
````

In this case, in the `-check` chunk we call `check_result`,
and pass in various `pass_if` and `fail_if` conditions.
The **first** condition that matches will return the corresponding feedback, i.e.,
if a `pass_if` case matches then the student's solution passed,
if a `fail_if` case matches then the student's solution failed.

The `pass_if`, `fail_if`, and `check_result` functions all have parameters to customize the
message to the student.

## Test result

The last way to grade a student's solution is by matching against all instructor procided conditions.
The chunk setup is similar to the `grade_result` case, for example:

````markdown
`r ''````{r grade_conditions, exercise = TRUE}
# student code
____
```

`r ''````{r grade_conditions-hint-1}
# hint text
""
```

`r ''````{r grade_conditions-hint-2}
# hint text
""
```

`r ''````{r grade_conditions-check}
gradethis::grade_conditions(
gradethis::pass_if(~ identical(.result, 1)),
gradethis::fail_if(~ identical(.result, 2))
)
```
````

The difference here is that `grade_conditions` is called in the `-check` chunk.
All `pass_if` and `fail_if` conditions must match in this case.
This is similar to providing a set of unit tests for a student provided function.

# Using `gradethis` as a standlone grader

```{r}
library(gradethis)
```

The grading functions, `grade_code`, `grade_result`, and `grade_conditions` can also be used
outside the `learnr` context.
The instructor will have to manually provide the
result (in the case of `grade_result`, and `grade_conditions`) or
a `quote`d expression (in the case of `grade_code`) manually.

All of the grading function will return a `gradethis::graded` object
with a `message`, `correct` status.

## Grade code

When grading code, `quote`d expressions must be passed.

```{r}
student_code <- quote(sqrt(exp(3)))
solution_code <- quote(sqrt(log(2)))
```

```{r}
gradethis::grade_code(grader_args = list(
user_quo = student_code,
solution_quo = solution_code
)
)
```

## Grade result

To manually grade a result, the student result needs to be passed as a
`last_value` in the `learnr_args` parameter.

```{r}
student_solution <- 5
```

```{r}
gradethis::grade_result(
pass_if(5, "You also got a value of 5!"),
learnr_args = list(last_value = student_solution)
)
```



[learnr]: https://rstudio.github.io/learnr
[addin]: https://rstudio.github.io/rstudioaddins/
[addin-keyboard_shortcut]: https://rstudio.github.io/rstudioaddins/#keyboard-shorcuts
74 changes: 74 additions & 0 deletions vignettes/a-010-using_equal.Rmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
title: "Do not use == to compare .result"
output: rmarkdown::html_vignette
vignette: |-
>
%\VignetteIndexEntry{Do not use == to compare .result}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---

It is highly encouraged to make comparisons within `gradethis` to **not** use `==`,
and insead make comparisons using the `identical()` or `all.equal() `functions.

There are 2 main reasons for this.

1. Most (if not all) instructor comparisons are expected to return a single boolean value,
using `==` returns a vector of boolean values, which can have a `length()` greater than 1.
This is probably unexpected,
so it is better to use `identical` or `all.equal` which will return a boolean of length 1.
2. There is a bug when comparing expressions greater than 60 characters long (see bottom for links)

# Be explicit

In the first case, if the solution is a vector of values, the "truthfulness" of the comparison is ambiguous,

```{r}
student <- c(1, 2, 3)
solution <- c(1, 2, 4)

student == solution
```

`gradethis` will return a warning, and implicitely wrap the vector around `all`,
but it is better for the instructor to write the boolean condition correctly.

```{r}
all(student == solution)
```

```{r}
identical(student, solution)
```

# Bug in `==`

When comparing longer than `60L` characters,
only the first `60` characters are actually compared.

```{r}
# 63 characters
u <- quote(f(x1234567890123456789012345678901234567890123456789012345, 1))
s <- quote(f(x1234567890123456789012345678901234567890123456789012345, 2))
u == s
```


```{r}
# 64 characters
u <- quote(f(x12345678901234567890123456789012345678901234567890123456, 1))
s <- quote(f(x12345678901234567890123456789012345678901234567890123456, 2))
u == s
```

In this case, expressions should always be compared with `identical`:

```{r}
identical(u, s)
```


You can read more about it here: https://daniel.rbind.io/2019/08/06/inconsistencies-with-in-r/

More details are found in this issue: https://github.com/rstudio-education/gradethis/issues/28