---
title: "Creating constraints file in TestDesign package"
author: Sangdon Lim
output: html_document
vignette: >
  %\VignetteIndexEntry{Creating constraints file in TestDesign package}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

<br/>

### Introduction

This document explains how to create a constraint file. In test assembly, practitioners often want to select the items that satify various types of constraints. As of *TestDesign* version 0.2, constraints can be read in from a `.csv` spreadsheet file. The file is expected to be in the following structure:

```{r echo = FALSE, message = FALSE}
library(knitr)
library(kableExtra)
library(TestDesign)
```



```{r echo = FALSE}
knitr::kable(constraints_science_raw[1:5, ]) %>%
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive")) %>%
  column_spec(1, "5em") %>%
  column_spec(2, "5em") %>%
  column_spec(3, "5em") %>%
  column_spec(4, "10em") %>%
  column_spec(5, "3em") %>%
  column_spec(6, "3em") %>%
  column_spec(7, "3em")
```

The constraint file should have 7 columns, named as `CONSTRAINT`, `TYPE`, `WHAT`, `CONDITION`, `LB`, `UB`, `ONOFF` on the first row of the file. Beginning from the second row, each row should have the corresponding values for the 7 columns. A convenient way to creating the file is to use a spreadsheet application (e.g. Excel), work on the contents from there, and then saving it as a `.csv` file.

<br/>

***

#### CONSTRAINT

This column serves as indices for the constraints. A single numeric value should be put into each row, incrementing by 1.

<br/>

***

#### TYPE

This column specifies the type of constraint. Following values are expected: `Number`, `Order`, `Enemy`, `Include`, `Exclude`, `AllorNone`.

<br/>

* `Number` specifies the constraint to be applied to the number of selected items (if `WHAT` column is `Item`), or to the number of selected item sets (if `WHAT` column is `Stimulus`). For example, the following row tells the solver to select a total of 30 items.

```{r, echo = FALSE}
knitr::kable(constraints_science_raw[1, ], row.names = FALSE) %>%
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive")) %>%
  column_spec(1, "5em") %>%
  column_spec(2, "5em", background = 'cyan') %>%
  column_spec(3, "5em") %>%
  column_spec(4, "10em") %>%
  column_spec(5, "3em") %>%
  column_spec(6, "3em") %>%
  column_spec(7, "3em")
```

<br/>
<br/>

* `Order` specifies the selection to be made in ascending order. The following row tells the solver to select the items in ascending `LEVEL`, based on the supplied item attributes.

```{r, echo = FALSE}
knitr::kable(constraints_science_raw[32, ], row.names = FALSE) %>%
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive")) %>%
  column_spec(1, "5em") %>%
  column_spec(2, "5em", background = 'cyan') %>%
  column_spec(3, "5em") %>%
  column_spec(4, "10em") %>%
  column_spec(5, "3em") %>%
  column_spec(6, "3em") %>%
  column_spec(7, "3em")
```

<br/>
<br/>

* `Enemy` specifies the items (or item sets) matching the condition to be treated as enemy items. To tell the solver to select at most one of the two items:

```{r, echo = FALSE}
knitr::kable(constraints_science_raw[33, ], row.names = FALSE) %>%
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive")) %>%
  column_spec(1, "5em") %>%
  column_spec(2, "5em", background = 'cyan') %>%
  column_spec(3, "5em") %>%
  column_spec(4, "10em") %>%
  column_spec(5, "3em") %>%
  column_spec(6, "3em") %>%
  column_spec(7, "3em")
```

<br/>
<br/>

* `Include` specifies the items matching the condition to be included in selection. For example, the following row tells the solver to include the two items `SC00003` and `SC00004`:

```{r, echo = FALSE}
knitr::kable(constraints_science_raw[34, ], row.names = FALSE) %>%
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive")) %>%
  column_spec(1, "5em") %>%
  column_spec(2, "5em", background = 'cyan') %>%
  column_spec(3, "5em") %>%
  column_spec(4, "10em") %>%
  column_spec(5, "3em") %>%
  column_spec(6, "3em") %>%
  column_spec(7, "3em")
```

<br/>
<br/>

* `Exclude` specifies the items matching the condition to be excluded in selection. The following row tells the solver to exclude the items satisfying `PTBIS < 0.15`, based on the supplied item attributes.

```{r, echo = FALSE}
knitr::kable(constraints_science_raw[35, ], row.names = FALSE) %>%
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive")) %>%
  column_spec(1, "5em") %>%
  column_spec(2, "5em", background = 'cyan') %>%
  column_spec(3, "5em") %>%
  column_spec(4, "10em") %>%
  column_spec(5, "3em") %>%
  column_spec(6, "3em") %>%
  column_spec(7, "3em")
```

<br/>
<br/>

* `AllOrNone` specifies the items matching the condition to be either all included or all excluded. To tell the solver to either select the items `SC00005` and `SC00006` at the same time or exclude them at the same time:

```{r, echo = FALSE}
knitr::kable(constraints_science_raw[36, ], row.names = FALSE) %>%
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive")) %>%
  column_spec(1, "5em") %>%
  column_spec(2, "5em", background = 'cyan') %>%
  column_spec(3, "5em") %>%
  column_spec(4, "10em") %>%
  column_spec(5, "3em") %>%
  column_spec(6, "3em") %>%
  column_spec(7, "3em")
```

<br/>

***

#### WHAT

This column specifies where the constraint is applied. The expected values are `Item` or `Stimulus`.

<br/>

***

#### CONDITION

This column specifies the condition of the constraint. An R expression returning a logical value (`TRUE` or `FALSE`) is expected. The variables supplied in item attributes can be used in the expression as variable names.

Some examples are:

* `"STANDARD %in% c(2, 4)"` tells the solver to select when `STANDARD` is either 2 or 4.
* `"STANDARD %in% c(2, 4) & DOK >= 3"` tells the solver to select when `STANDARD` is either 2 or 4, and also `DOK` is at least 3.
* `!is.na(FACIT)` tells the solver to select when `FACIT` is not empty.
* Leave it empty to not specify any condition. This is useful in constraining the total number of items.

Also, `Per Stimulus` can be used to specify the number of items to select in each stimulus. For example, the following row tells the solver to select 4 to 6 items per stimulus:

```{r, echo = FALSE}
knitr::kable(constraints_reading_raw[3, ], row.names = FALSE) %>%
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive")) %>%
  column_spec(1, "5em") %>%
  column_spec(2, "5em") %>%
  column_spec(3, "5em") %>%
  column_spec(4, "10em", background = 'cyan') %>%
  column_spec(5, "3em") %>%
  column_spec(6, "3em") %>%
  column_spec(7, "3em")
```

<br/>

***

#### LB and UB

These two columns specify the lower and upper bounds on the number of selected items. These columns should be specified when `TYPE` is `Number`, and should be left empty otherwise.

Some example rows are provided.

* To select a total of 12 items:

```{r, echo = FALSE}
knitr::kable(constraints_fatigue_raw[1, ], row.names = FALSE) %>%
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive")) %>%
  column_spec(1, "5em") %>%
  column_spec(2, "5em") %>%
  column_spec(3, "5em") %>%
  column_spec(4, "10em") %>%
  column_spec(5, "3em", background = 'cyan') %>%
  column_spec(6, "3em", background = 'cyan') %>%
  column_spec(7, "3em")
```

<br/>
<br/>

* To select 15 to 30 items satisfying `DOK >= 2`:

```{r, echo = FALSE}
knitr::kable(constraints_reading_raw[17, ], row.names = FALSE) %>%
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive")) %>%
  column_spec(1, "5em") %>%
  column_spec(2, "5em") %>%
  column_spec(3, "5em") %>%
  column_spec(4, "10em") %>%
  column_spec(5, "3em", background = 'cyan') %>%
  column_spec(6, "3em", background = 'cyan') %>%
  column_spec(7, "3em")
```

<br/>

***

#### ONOFF

Set this to `OFF` to turn off the constraint from being applied. `ON` or leaving it blank applies the constraint. The following example specifies the order constraint to be not applied.

```{r, echo = FALSE}
knitr::kable(constraints_reading_raw[18, ], row.names = FALSE) %>%
  kableExtra::kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive")) %>%
  column_spec(1, "5em") %>%
  column_spec(2, "5em") %>%
  column_spec(3, "5em") %>%
  column_spec(4, "10em") %>%
  column_spec(5, "3em") %>%
  column_spec(6, "3em") %>%
  column_spec(7, "3em", background = 'cyan')
```

<br/>
