-
Notifications
You must be signed in to change notification settings - Fork 1
/
08-additionalCode.Rmd
executable file
·205 lines (147 loc) · 11.3 KB
/
08-additionalCode.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# Additional Coding Practices {#additionalCoding}
The following coding practices arise much less frequently than our [Specific Coding Practices](#specificCoding) and [HTML/CSS](#htmlCSS). However, being aware of them will help you out in the long run.
## Plot Caching {#plotCachec}
Of these less frequent practices, Plot Caching is perhaps the most useful. Plot caching refers to the practice of your app to generate a plot once, save it, and then quickly serve the plot again and again to users. In essence, this is a method for improving the efficiency of an app. However, this is not something that always needs to be used.
The following cases represent some of the most common places where using plot caching is beneficial:
+ If your app includes a computationally intense plot (i.e., lots of data and/or layers) whose underlying data is fixed.
+ If your app has a dynamic graph that the user explores and needs to move back and forth between various states.
These cases represent a place where the performance of your app can suffer. To improve performance, you should consider using the `renderCachedPlot` function rather than `renderPlot`. This function will store a copy of each plot on the sever and provide that stored copy to new instances of the app. This cuts down on server demands and speeds up your app. For the second category, this allows the users to move more quickly to a previously examined state (i.e., low to no hang time).
There are two key arguments to `renderCachedPlot`: `expr` and `cacheKeyExpr`. The `expr` is the code chunk which will generate the plot or the name of plot object you've created elsewhere.
The `cacheKeyExpr` is the listing of all `reactive` elements that you want to tie the plot to. For example, if we set `cacheKeyExpr = { list(input$sampleSize, input$selectedData) }`, then the app will tie each saved version of the plot to the values of `sampleSize` and `selectedData`. The user sets these two inputs to a new pairing, your app would re-generate the plot. However, when the user sets them to a pairing that they've already looked at, your app immediately re-displays the saved plot. Plot caching also looks across all of the users, thus speeding up your app's plotting for all users.
## Minimize External Files {#exFiles}
Try to minimize the number and size of external files you're attaching to your App. If you're working on an existing app, remove any external files that are no longer necessary.
Wherever possible try to place external files into the `www` directory/folder that is at the same level as your `app.R` file.
If there are any external files (e.g., `*.csv`, `*.txt`, `*.dat`, `*.jpg`) that are not being used, delete them from the repository.
## Metadata {#metadatac}
As of boastUtils `v0.1.10`, your App will need the following metadata in a file named [DESCRIPTION](https://github.com/EducationShinyAppTeam/App_Template/blob/master/DESCRIPTION) (no file extension). This data is used to inform Learning Record Stores (LRS) about your App as well as provide information to instructors using the Instructor App (unreleased). See also our section on the [DESCRIPTION](#description) file in our chapter on the [App Template](#template).
**DESCRIPTION**
```{r metadatac, echo = TRUE, eval = FALSE}
Title: Sample App - A Lengthy Title
ShortName: Sample App
Date: 2021-05-13
Lifecycle: experimental
Authors@R: c(
person(given = "Neil", family = "Hatfield", email = "[email protected]", role = c("aut", "cre")),
person(given = "Robert", family = "Carey", email = "[email protected]", role = c("aut"))
)
Chapter: Sample Chapter
Description: This app is focused on the common types of xyz.
LearningObjectives: c(
"The student will learn to understand Concept A in way z.",
"The student will learn to understand Concept B as description y."
)
DisplayMode: Normal
URL: https://psu-eberly.shinyapps.io/Sample_App
BugReports: https://github.com/EducationShinyAppTeam/Sample_App/issues
License: CC-BY-NC-SA-4.0
Tags: simulation
Type: Shiny
```
## Other Languages (Python, JavaScript, etc.) {#otherLanguages}
Generally speaking, we want to keep the vast majority of our work within the R language (with the necessary HTML and CSS spots). Due to our turnover of coders, we won't always be able to ensure that we get someone who is proficient in additional languages such as Python, Rust, Netlogo, JavaScript, etc.
See if what you're trying to do can first be done in R. If not, set up a time to speak with Neil to see about the appropriate way to go about using another language.
## Using rLocker {#rlockerc}
Because these apps are being developed for educational purposes, it is beneficial to collect data (logfiles) based on learning objectives and outcomes. The [rLocker](https://github.com/EducationShinyAppTeam/rLocker) package was created to aid in this process. It is important to know that the package itself does not perform feats of magic and will require some tuning to get right.
If you are following the directions we've laid out in this Style Guide, there is no additional action that you have to take. We provide the following information just for those individuals who would like to learn more about `rLocker`.
### Installing
```{r, eval = FALSE}
library(devtools)
devtools::install_github("EducationShinyAppTeam/rLocker")
```
[View Documentation](https://github.com/EducationShinyAppTeam/rLocker#Installation)
### Setup
Core configuration is included in the [boastUtils](https://github.com/EducationShinyAppTeam/boastUtils) package. Simply use the boastApp wrapper function instead of shinyApp and you're done! See [Creating an App](https://github.com/EducationShinyAppTeam/boastUtils#creating-an-app) for more information on how to use boastApp.
### Usage
The main purpose of this package is to help create meaningful- structured- xAPI data. xAPI (Experience API) can be thought of as the "Who did what, where did they do it, and when did they do it?" in your App. Statements are often structured as Actor, Verb, and Object that can also Result in something. For more on xAPI, check out [What is the Experience API?](https://xapi.com/overview/) or experiment with Statements in the [xAPI Lab](https://adlnet.github.io/xapi-lab/).
**For example:**
> **Bob** (Actor) **clicked** (Verb) **submit** (Object).
**In assessments:**
> **Neil** (Actor) **answered** (Verb) **Question 1** (Object) **correctly** with the answer **true** (Result).
This is a good way to think about it when beginning to write collection functions. Which brings us to our next part, writing collection functions.
Out of the box, `rocker` does not provide any collection functions for apps, only creation and storage mechanisms. Why is this? Every app is different! You know your app the best and what interactions are possible. Before storing data in a Learning Record Store (LRS) like [Learning Locker](https://www.ht2labs.com/learning-locker-community/overview/), it is important to transform it in a way that makes sense.
**boastUtils**
If using the `boastApp` wrapper for your project, your App will automatically generate and store a few of the more typical Statements. Please refrain from creating additional versions of the following statement descriptions:
| Statement | Description |
|:------------|:----------------------------------------------|
| launched | User has started the app. |
| experienced | User has visited a tab (page) within the app. |
| exited * | User has left the app. |
An App should only have one `launched` and `exited` event but can have multiple experiences per session. Therefore, you may use `experienced` in other places within your App.
\* Requires `boastUtils >= 0.1.5`.
**Sample generator function**
```{r, eval = FALSE}
.generateStatement <- function(
session,
verb = NA,
object = NA,
description = NA,
interactionType = NA,
response = NA,
success = NA,
completion = FALSE)
{
statement <- rLocker::createStatement(list(
verb = verb,
object = list(
id = paste0(getCurrentAddress(session), "#", object),
name = paste0(APP_TITLE),
description = paste0("Question ", activeQuestion, ": ", description),
interactionType = interactionType
),
result = list(
success = success,
response = response,
completion = completion
)
))
return(rLocker::store(session, statement))
}
```
**Sample event observer**
```{r, eval = FALSE}
observeEvent(session$input[[id]], {
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# BEGIN VALIDATION #
# #
# For this example, #
# We will check if someone answered a question in our game correctly. #
# Your code should be different here! #
# #
# The input element we're observing. #
object <- id
# The user's action verb. Most likely going to be answered in this case. #
# Run rLocker::getVerbList() if you are unsure of the options. #
verb <- "answered"
# The correct answer to the question. #
answer <- gameSet[id, "answer"]
# The user's answer to the question. #
response <- input$ans
# A description of the question or the full question itself (if short). #
description <- gameSet[id,]$question
# The type of question it is. #
# Run rLocker::getInteractionTypes() if you are unsure of the options. #
interactionType <- ifelse(
gameSet[id,]$format == "numeric", "numeric", "choice"
)
# Was the question answered successfully? #
success <- input$ans == answer
# Did this event trigger the completion of your activity? #
completion <- ifelse(.appState == "continue", FALSE, TRUE)
# #
# END VALIDATION #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
.generateStatement(
session,
object = object,
verb = verb,
description = description,
response = response,
interactionType = interactionType,
success = success,
completion = completion
)
}
```
Additional code can be found in the [examples](https://github.com/EducationShinyAppTeam/rLocker/tree/master/inst/examples) folder as well as the main [README](https://github.com/EducationShinyAppTeam/rLocker/blob/master/README.md) of the rLocker project; apps such as the [Hypothesis_Testing_Game](https://github.com/EducationShinyAppTeam/Hypothesis_Testing_Game) have already been outfitted with rLocker. The statement generator above will eventually make its way into `boastUtils` once enough feedback is collected. If you are ever confused about how it works, feel free to reach out to Bob ([rpc5102](mailto:[email protected])).
**Did you know?**
You can run `help(package = "rLocker")` to view the help files for this package or press `F1` while typing a function name to see the documentation for that specific function.