-
Notifications
You must be signed in to change notification settings - Fork 239
/
Copy pathhttp-handlers.R
226 lines (173 loc) · 5.91 KB
/
http-handlers.R
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
register_http_handlers <- function(session, metadata) {
session$userData$learnr_state <- reactiveVal("start")
# parent environment for completions (see discussion in setup_exercise_handler
# for why this is chosen as the completion/execution parent)
server_envir <- parent.env(parent.env(parent.frame()))
# environment used for hosting state (e.g. for chunks)
state <- new.env(parent = emptyenv())
# initialize handler
session$registerDataObj("initialize", NULL, function(data, req) {
# parameters
location <- json_rpc_input(req)$location
# initialize session state
identifiers <- initialize_session_state(session, metadata, location, req)
# data payload to return
data <- list(
identifiers = identifiers
)
# Now that we've initialized the session state, emit the start event
event_trigger(session, "session_start")
session$userData$learnr_state("initialized")
# return identifers
list(
status = 200L,
headers = list(
'Content-Type' = 'application/json'
),
body = json_rpc_result(data)
)
})
# restore state handler
session$registerDataObj("restore_state", NULL, rpc_handler(function(input) {
# forward any client stored objects into our own storage
if (!is.null(input))
initialize_objects_from_client(session, input)
# get state objects
state_objects <- get_all_state_objects(session, exercise_output = FALSE)
# create submissions from state objects
submissions <- submissions_from_state_objects(state_objects)
# create video progress from state objects
video_progress <- video_progress_from_state_objects(state_objects)
# create progress events from state objects
progress_events <- progress_events_from_state_objects(state_objects)
# get client state
client_state <- get_client_state(session)
session$userData$learnr_state("restored")
# return data
list(
client_state = client_state,
submissions = submissions,
video_progress = video_progress,
progress_events = progress_events
)
}))
# remove state handler
session$registerDataObj("remove_state", NULL, rpc_handler(function(input) {
remove_all_objects(session)
}))
# event recording
session$registerDataObj("record_event", NULL, rpc_handler(function(input) {
# Augment the data with the label
input$data$label <- input$label
record_event(session = session,
event = input$event,
data = input$data)
}))
# # question submission handler
# session$registerDataObj("question_submission", NULL, rpc_handler(function(input) {
# cat("--- question_submission handler called\n")
#
# # extract inputs
# label <- input$label
# question <- input$question
# answers <- input$answers
# correct <- input$correct
#
# # fire event
# question_submission_event(session = session,
# label = label,
# question = question,
# answers = answers,
# correct = correct)
# }))
# video progress handler
session$registerDataObj("video_progress", NULL, rpc_handler(function(input) {
# extract inputs
video_url <- input$video_url
time <- input$time
total_time <- input$total_time
# fire event
event_trigger(
session,
"video_progress",
data = list(
video_url = video_url,
time = time,
total_time = total_time
)
)
}))
# exercise skipped event
session$registerDataObj("section_skipped", NULL, rpc_handler(function(input) {
# extract inputs
sectionId <- input$sectionId
# fire event
event_trigger(
session,
"section_skipped",
data = list(sectionId = sectionId)
)
}))
# client state handler
session$registerDataObj("set_client_state", NULL, rpc_handler(function(input) {
save_client_state(session, input)
}))
# help handler
session$registerDataObj("help", NULL, rpc_handler(function(input) {
}))
# setup chunk handler
session$registerDataObj("initialize_chunk", NULL, rpc_handler(function(input) {
params <- input
# evaluate setup code to prime environment
label <- as.character(params$label)
code <- paste(params$setup_code, collapse = "\n")
# no setup chunk / label? nothing to do
if (!(nzchar(label) && nzchar(code)))
return()
# evaluate code in environment to prime
Encoding(code) <- "UTF-8"
state[[label]] <- new.env()
eval(parse(text = code, encoding = "UTF-8"), envir = state[[label]])
}))
# completion handler
session$registerDataObj("completion", NULL, rpc_handler(function(input) {
# read params
line <- as.character(input$contents)
label <- as.character(input$label)
Encoding(line) <- "UTF-8"
auto_complete_r(line, label, state)
}))
# diagnostics handler
session$registerDataObj("diagnotics", NULL, rpc_handler(function(input) {
}))
# this is a "bat signal" to let the JS side know that the Shiny
# server is ready to handle http requests
session$sendCustomMessage("tutorial_isServerAvailable", "true")
}
# return a rook wrapper for a function that takes a list and returns a list
# (list contents are automatically converted to/from JSON for rook as required)
rpc_handler <- function(handler) {
function(data, req) {
# get the input
input <- json_rpc_input(req)
# call the handler
result <- handler(input)
# return the result as JSON
list(
status = 200L,
headers = list(
'Content-Type' = 'application/json'
),
body = json_rpc_result(result)
)
}
}
# get the json from a request body
json_rpc_input <- function(req) {
input_stream <- req[["rook.input"]]
jsonlite::fromJSON(input_stream$read_lines())
}
# helper for returning JSON
json_rpc_result <- function(x) {
jsonlite::toJSON(x, auto_unbox = TRUE, null = "null", force = TRUE)
}