Skip to content

Commit

Permalink
Improving Hidden Evaluation Options (#36)
Browse files Browse the repository at this point in the history
* Wordsmithing.

* Fix option typo.

* Rename output templates to match with context cell option

* Add a template that shows output-only.

* Incorporate the new output template into the filter.

* Avoid the plot.new() request by removing the grid.

* Add details on the `context` set of options and
  • Loading branch information
coatless authored Sep 17, 2023
1 parent b9931c1 commit 8b43f0b
Show file tree
Hide file tree
Showing 8 changed files with 257 additions and 36 deletions.
File renamed without changes.
5 changes: 0 additions & 5 deletions _extensions/webr/webr-context-internal.html

This file was deleted.

90 changes: 90 additions & 0 deletions _extensions/webr/webr-context-output.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<div id="webr-code-output-{{WEBRCOUNTER}}" aria-live="assertive">
<pre style="visibility: hidden"></pre>
</div>
<script type="module">
// Retrieve webR code cell information
const outputDiv = document.getElementById("webr-code-output-{{WEBRCOUNTER}}");

// Function to execute the code (accepts code as an argument)
async function executeOutputOnlyCode(codeToRun) {
// Create a canvas variable for graphics
let canvas = undefined;

// Initialize webR
await globalThis.webR.init();

// Setup a webR canvas by making a namespace call into the {webr} package
await webR.evalRVoid("webr::canvas(width={{WIDTH}}, height={{HEIGHT}})");

// Capture output data from evaluating the code
const result = await webRCodeShelter.captureR(codeToRun, {
withAutoprint: true,
captureStreams: true,
captureConditions: false//,
// env: webR.objs.emptyEnv, // maintain a global environment for webR v0.2.0
});

// Start attempting to parse the result data
try {

// Stop creating images
await webR.evalRVoid("dev.off()");

// Merge output streams of STDOUT and STDErr (messages and errors are combined.)
const out = result.output.filter(
evt => evt.type == "stdout" || evt.type == "stderr"
).map((evt) => evt.data).join("\n");

// Clean the state
const msgs = await webR.flush();

// Output each image stored
msgs.forEach(msg => {
// Determine if old canvas can be used or a new canvas is required.
if (msg.type === 'canvas'){
// Add image to the current canvas
if (msg.data.event === 'canvasImage') {
canvas.getContext('2d').drawImage(msg.data.image, 0, 0);
} else if (msg.data.event === 'canvasNewPage') {
// Generate a new canvas element
canvas = document.createElement("canvas");
canvas.setAttribute("width", 2 * {{WIDTH}});
canvas.setAttribute("height", 2 * {{HEIGHT}});
canvas.style.width = "700px";
canvas.style.display = "block";
canvas.style.margin = "auto";
}
}
});

// Nullify the outputDiv of content
outputDiv.innerHTML = "";

// Design an output object for messages
const pre = document.createElement("pre");
if (/\S/.test(out)) {
// Display results as text
const code = document.createElement("code");
code.innerText = out;
pre.appendChild(code);
} else {
// If nothing is present, hide the element.
pre.style.visibility = "hidden";
}
outputDiv.appendChild(pre);

// Place the graphics on the canvas
if (canvas) {
const p = document.createElement("p");
p.appendChild(canvas);
outputDiv.appendChild(p);
}
} finally {
// Clean up the remaining code
webRCodeShelter.purge();
}
}

// Run the code
executeOutputOnlyCode(`{{WEBRCODE}}`)
</script>
9 changes: 9 additions & 0 deletions _extensions/webr/webr-context-setup.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script type="module">
// Initialization WebR
await globalThis.webR.init();

// Run R code without focusing on storing data.
await globalThis.webR.evalRVoid(`
{{WEBRCODE}}
`)
</script>
37 changes: 24 additions & 13 deletions _extensions/webr/webr.lua
Original file line number Diff line number Diff line change
Expand Up @@ -185,14 +185,19 @@ function readTemplateFile(template)
return content
end

-- Obtain the editor template file at webr-editor.html
function editorTemplateFile()
return readTemplateFile("webr-editor.html")
-- Obtain the editor template file at webr-context-interactive.html
function interactiveTemplateFile()
return readTemplateFile("webr-context-interactive.html")
end

-- Obtain the internal template file at webr-context-internal.html
function internalTemplateFile()
return readTemplateFile("webr-context-internal.html")
-- Obtain the output template file at webr-context-output.html
function outputTemplateFile()
return readTemplateFile("webr-context-output.html")
end

-- Obtain the setup template file at webr-context-setup.html
function setupTemplateFile()
return readTemplateFile("webr-context-setup.html")
end

-- Obtain the initialization template file at webr-init.html
Expand All @@ -201,9 +206,11 @@ function initializationTemplateFile()
end

-- Cache a copy of each public-facing templates to avoid multiple read/writes.
editor_template = editorTemplateFile()
interactive_template = interactiveTemplateFile()

output_template = outputTemplateFile()

internal_template = internalTemplateFile()
setup_template = setupTemplateFile()
----

-- Define a function that escape control sequence
Expand Down Expand Up @@ -442,12 +449,16 @@ function enableWebRCodeCell(el)
local cell_context = el.attributes.context

-- Decide the correct template
-- Make sure we perform a copy of each template
-- Make sure we perform a copy of each template
local copied_code_template = nil
if is_variable_empty(cell_context) then
copied_code_template = editor_template
else
copied_code_template = internal_template
if is_variable_empty(cell_context) or cell_context == "interactive" then
copied_code_template = interactive_template
elseif cell_context == "setup" then
copied_code_template = setup_template
elseif cell_context == "output" then
copied_code_template = output_template
else
error("The `context` option must contain either: `interactive`, `setup`, or `output`.")
end

-- Make the necessary substitutions into the template
Expand Down
4 changes: 2 additions & 2 deletions docs/index.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ fit = lm(mpg ~ am, data = mtcars)
summary(fit)
```

At its core, [webR](https://docs.r-wasm.org/webr/latest/) is all about empowering you to run R code directly in your web browser, completely bypassing the need for an external R server. The best part? This incredible capability extends far beyond its integration with the [Quarto extension](https://github.com/coatless/quarto-webr), giving you the freedom to harness the power of webR independently.
At its core, the [quarto-webr extension](https://github.com/coatless/quarto-webr) is designed to empower you to run R code directly in your web browser using familiar reporting tools, all without the need for an external R server. Moreover, the extension abstracts away the need to know HTML or JavaScript to use webR. However, it's worth noting that you can also choose to unlock the full potential of webR and create more complex applications independently by directly using [webR's JavaScript API](https://docs.r-wasm.org/webr/latest/evaluating.html), granting you unparalleled freedom to harness the power of R in innovative ways.

Let's dive in and kickstart your journey with interactive code cells by creating your very first [webR-powered Quarto document](webr-first-steps.qmd)!
With this in mind, let's dive in and kickstart your journey with interactive code cells by creating our very first [webR-powered Quarto document](webr-first-steps.qmd)!
1 change: 0 additions & 1 deletion docs/webr-code-cell-demos.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ plot(
mpg ~ wt,
data = mtcars,
col = "blue",
panel.first = grid(8, 8),
xlab = "Miles/(US) gallon",
ylab = "Weight (1000 lbs)",
main = "Miles per Gallon and Weight of Cars",
Expand Down
147 changes: 132 additions & 15 deletions docs/webr-internal-cell.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,25 @@ filters:
- webr
---

## Hidden Code Execution
In `quarto-webr`, you have the ability to execute code cells without displaying the code or its output. This feature can be particularly useful for preloading variables, loading datasets, creating visualizations, or checking student solutions without revealing the code to end users.

In `quarto-webr`, you have the ability to execute code cells without displaying the code or its output. This feature can be particularly useful for preloading variables, loading datasets, or checking student solutions without revealing the code to end users. Unfortunately, this feature is native
# Using the `context` Option

### Hidden Evaluation without Output
Withholding code from the user requires the use of a custom `quarto-webr` cell option called `context`. The `context` option is unique to `quarto-webr` as it extends the capabilities beyond Quarto's built-in cell options.

There are three different contexts supported by `quarto-webr`. By default, the `interactive` context is used if the `context` option is not specified in the code cell. These options are summarized in the table below:

| `quarto-webr` Context | Quarto Cell Option | Description |
|-----------------------|--------------------|-------------|
| `interactive` (default) | `include: true` | Show both code and results |
| `output` | `echo: false` | Suppress showing code, but display the results |
| `setup` | `include: false` | Suppress showing both code and results |

::: {.callout-caution}
Please note that the contents of the hidden code cell can be viewed if the web page's source code is inspected.
:::

## Hidden Evaluation without Output

You can create a hidden setup code cell within the document by using the special comment `#| context: setup`. The code in this cell executes in the background without displaying the code or its output.

Expand All @@ -38,16 +52,83 @@ meaning_of_life = 42
meaning_of_life
```

::: {.callout-caution}
Please note that the contents of the hidden code cell can be viewed if the web page's source code is inspected.
:::
## Hidden Evaluation without Output

In `quarto-webr`, you have the capability to create hidden setup code cells within your document using the special comment `#| context: setup`. The code within these cells executes discreetly in the background without displaying the code or its output.

```{webr-r}
#| context: setup
meaning_of_life = 42
```

### Hidden Loading of a Dataset
In the example above, we've pre-loaded the `meaning_of_life` variable with a value of `42`. If you proceed to run the subsequent code cell, you'll observe the value of `meaning_of_life` being displayed as `42`.

You can use the setup hidden code cell to pre-load and manipulate an entire dataset. This allows users to directly interact with the loaded data.
```{webr-r}
#| context: setup
meaning_of_life = 42
```

```{webr-r}
#| context: hidden
meaning_of_life
```

By incorporating the `setup` hidden code cell for data loading and preprocessing, you enhance the user experience by providing them with an accessible and interactive environment for working with the data while maintaining a clutter-free and organized document structure.


## Hidden Evaluation with Output

You also have the choice of crafting an output-only code cell within your `quarto-webr` document, achieved by incorporating the special comment `#| context: output`. The code inside this cell executes quietly in the background and reveals its output when the execution is complete. The output can take the form of either text or graphics.

```{{webr-r}}
#| context: output
plot(
mpg ~ wt,
data = mtcars,
col = "blue",
xlab = "Miles/(US) gallon",
ylab = "Weight (1000 lbs)",
main = "Miles per Gallon and Weight of Cars",
sub = "Source: 1974 Motor Trend US magazine."
)
```

```{webr-r}
#| context: output
plot(
mpg ~ wt,
data = mtcars,
col = "blue",
xlab = "Miles/(US) gallon",
ylab = "Weight (1000 lbs)",
main = "Miles per Gallon and Weight of Cars",
sub = "Source: 1974 Motor Trend US magazine."
)
```

```{{webr-r}}
#| context: output
matrix(c(1, 2, 3, 4), nrow = 2)
```

```{webr-r}
#| context: output
matrix(c(1, 2, 3, 4), nrow = 2)
```

By using `output` code cells, you maintain a streamlined and comprehensible document, focusing on the outcome rather than the intricate data processing steps. This approach will enhance the readability and clarity of your content, making it more accessible and informative to your audience.

# Sample Cases

Let's explore some sample cases to see practical applications of the `quarto-webr` extension's `context` option. These examples demonstrate how to effectively hide code and output in your Quarto documents, enhancing readability and interactivity. Each case showcases a different use of the `context` option, offering valuable insights into its versatility.

## Hidden Loading of a Dataset

The `setup` hidden code cell is a powerful tool for seamlessly pre-loading and preprocessing an entire dataset within your `quarto-webr` document. This capability enables users to work directly with the loaded data without any distracting code or output.

In the following example, we demonstrate the process of loading and preprocessing a dataset. First, we download the dataset from an external source and save it as `'penguins.csv'` in the virtual webR file system. Next, we read the data into a data frame named `df_penguins`. All these operations occur silently in the background, ensuring that your document remains clean and focused on the data's application.

```{webr-r}
#| context: setup
# Download a dataset
download.file(
Expand All @@ -56,7 +137,7 @@ download.file(
)
# Read the data
df = read.csv("penguins.csv")
df_penguins = read.csv("penguins.csv")
```

```{webr-r}
Expand All @@ -69,26 +150,61 @@ download.file(
)
# Read the data
df = read.csv("penguins.csv")
df_penguins = read.csv("penguins.csv")
```

```{webr-r}
# Display the head of the data
head(df)
head(df_penguins)
```

::: {.callout-note}
If the setup code relies on specific R packages, we recommend specifying the required packages in the document's YAML. This approach informs users that the webpage is not yet ready and communicates a clear status update at the top. For example:
If the setup code relies on specific R packages, we **strongly** recommend specifying the required packages in the document's YAML. This approach informs users that the webpage is not yet ready and communicates a clear status update at the top. For example:

```yaml
---
webr:
packages: ['ggplot2', 'dplyr']
---
```

Learn more on the [Using R Packages](webr-using-r-packages.qmd) documentation page.
:::

### Hidden Solution Checking of Student Work
### Hidden Summarization

You can use the output hidden code cell to generate summarized information about retrieved or manipulated data. This powerful feature enables you to process and summarize data without displaying the intermediary steps or code, keeping your document clean and focused on the results.

For instance, in the code cell below, we transform the `mtcars` dataset, converting variables, and then promptly produce a summary of the modified data.

```{{webr-r}}
#| context: output
mtcars2 <- within(mtcars, {
vs <- factor(vs, labels = c("V", "S"))
am <- factor(am, labels = c("automatic", "manual"))
cyl <- ordered(cyl)
gear <- ordered(gear)
carb <- ordered(carb)
})
summary(mtcars2)
```

```{webr-r}
#| context: output
mtcars2 <- within(mtcars, {
vs <- factor(vs, labels = c("V", "S"))
am <- factor(am, labels = c("automatic", "manual"))
cyl <- ordered(cyl)
gear <- ordered(gear)
carb <- ordered(carb)
})
summary(mtcars2)
```


## Hidden Solution Checking of Student Work

:::{.callout-warning}
Please be aware that any solution written in a webR hidden code cell can be obtained by viewing the document's HTML source code. It is not recommended for formal assessments such as exams, quizzes, or homework.
Expand Down Expand Up @@ -167,4 +283,5 @@ check(problem = "1a", answer = student_solution)

# Fin

With hidden code execution and solution checking, webR provides a powerful tool for creating interactive and educational content within your Quarto HTML documents.
With hidden code execution and solution checking, webR provides a powerful tool for creating interactive and educational content within your Quarto HTML documents.

0 comments on commit 8b43f0b

Please sign in to comment.