R Shiny How to indicate stale state when inputs have changed but score has not updated - r

I have a shiny app with many inputs & a scoring function. There is no action button per se. Any input change triggers a re-score.
Sometimes a user has changed the input but it takes the score some time to update. This may mislead the user in associating the new state with the old score.
I want to fix this by having some display cue e.g. a "Score is being computed" message or gray out UI etc. dynamically whenever the score shown is stale.
How do I achieve this? Any thoughts?
fn_run<-reactive({
scorer(inputs......)
})
output$score<-renderPrint(cat(fn_run()$score))
sidebarPanel(title="Scoring Outputs",width = 3,
h3(textOutput("title")),
h3(textOutput("score"),style="color:blue"),
)
Mostly the delay is discernible when I publish the app to shinyapps.io and not palpable on local shiny runs.

You can do this with some CSS and the shinyjs package.
The following blog has what I think you want:
http://deanattali.com/blog/advanced-shiny-tips/
The relevant code is found here:
https://github.com/daattali/advanced-shiny/blob/master/loading-screen/app.R
This will show you how to shade the whole screen whilst the app is loading.

Related

When is a widget property available to an iPython / Jupyter cell?

I'm getting ready to create a communications widget for use in a Jupyter application. I'm trying to understand when the widget's "value" property can be accessed ... it looks like the "value" can be read anytime, but won't match the widget model "value" until cell execution stops (i.e., the widget's browser state isn't updated back to the widget's kernel state).
To test this, I tried creating a small slider widget, waiting for 10 seconds, and then reading the widget "value" property ... all in the same cell. In the 10 seconds, the user (i.e., me) has time to change the slider to something like "5".
Here's a small test that can be run in a cell. When the sleep() happens, I move the slider to value "5".
from ipywidgets import *
import time
slider = IntSlider(
value=7,
min=1,
max=10.0,
step=1,
description="Input:",
)
display(slider)
time.sleep(10) # move slider to 5
print("done " + str(slider.value))
I expected "done 5" but got "done 7", implying to me that "value" is updated only after the cell completes.
Is this always true? And is there a way to force synchronization between the widget's browser state and its state in the kernel?
(I do get the expected "done 5" if I move the print() to the following cell.)
Thanks!
There is basically at present no way to automatically force code running in a cell
to wait for an event or change in a widget. Things like time.sleep(3) will only
freeze the cell and a slider created in the same cell will not display until the
sleep is complete.
The user could create a slider in cell 1 and then execute a sleep in cell 2 and then adjust the slider and the code in cell 2 may see the change after the sleep
but in this case the synchronization is directed by the user and is not automatic.
You can also start Python code from a widget event, but the Python code does not "run in a cell" and the "prints" will not go to the standard cell output area but
the output can be captured in other ways,.
In the following screenshot I use the "Output" widget to capture output from a widget event
The basic problem is that all communication between widgets and kernels is via
one-way messages -- there are no "return values" or "acknowledgements" of any kind.
Please see https://github.com/AaronWatters/jp_proxy_widget/blob/master/notebooks/Tutorial.ipynb for more discussion.
As it turns out, there does seem to be a library that uses something like asyncio to achieve an inline wait. It's called jupyter-ui-poll at https://github.com/Kirill888/jupyter-ui-poll. The author says it's available on PyPI at https://pypi.org/project/jupyter-ui-poll/
Very promising! ... otherwise, I'm forced to agree with you.

Can't Edit/Update Certain Items In Database (Table)

I have database that I have multiple orders entered into. Everything seems to be working fine except for a few old entries which will not accept updates/changes to their Fields.
Note: The majority of the Fields are Strings with Possible Values entered via a DropDown Box.
So if I open Order A I can make adjustments just fine and those changes persist even after closing the page and coming back or refreshing.
But if I open Order B, I can make changes via the dropdowns and it looks like they have adjusted, however if I leave the page or refresh all the changes have reverted back.
One piece of info that may be helpful is that each of these orders has at least one Field that contains an entry that is no longer a Possible Value (the original entries were removed/changed per request of the client).
Maybe they are "locked" because of this? Is there a way to look at an error log for a Published app?
I can delete the "corrupt" entries and recreate them (since there are currently only a few), but I would prefer to find a better solution in case this happens again in the future.
Any help would be greatly appreciated.
It's a bug. Such field level value updates should get through.
As workaround you can update prohibited(not possible anymore) values with allowed ones in OnSave Model's Event like:
switch (record.Field) {
case "old_value_1":
record.Field = "new_value_1";
break;
case "old_value_2":
record.Field = "new_value_2";
break;
...
}
Sorry for the inconvenience.
Each deployment has its own log. Have you tried "App Settings > DEPLOYMENTS > (click on the desployment) > VIEW LOGS"?

How can I get the date from the draggable vertical bar in timevis?

I found the timevis package to be very useful for displaying time periods, and it is working as expected to display my data.
I have used the addCustomTime() function to add a draggable vertical line, which also works great, however, I find that I can't get the current value of that line.
The underlying js widget exposes functions to interact with the elements, but I haven't been able to access them successfully. I have attempted to use shinyjs to catch the events emitted by the timeline, but I wasn't able to get that to work either.
I am fairly comfortable in R and shiny, but very much a newb in terms of the js, so I suspect I am just missing a trick on this.
In my ui.R, I create a timevisOutput object:
fluidPage(
timevisOutput("timeline")
)
I am trying to listen to the "timechanged" event: http://visjs.org/docs/timeline/#Events
When I add listeners with shinyjs in my server.R, I am able to see "mouseenter" events, but the "timechanged" handler doesn't seem to fire:
onevent("mouseenter", "timeline", print("timeline: mouseenter"))
onevent("timechanged", "timeline", print("Saw timechanged!"))
The visjs documentation has this snippet on "how to listen for a select event":
timeline.on('select', function (properties) {
alert('selected items: ' + properties.items);
});
I tried adding that in a shinyjs::extendShinyjs() call, but that doesn't work either. I added an example to listen for keydown events that did work:
$(document).keypress(function(e) { alert('Key pressed: ' + e.which); });
So that makes me think that I'm not referencing the timeline object correctly. The $(document) in there makes me think that I'm not aware of a way to properly get at the timeline element.
Since I can see mouseenter events for the timeline, but can't see its timechanged events, I think I need the .on() call, however, I don't think I'm referencing the timeline element properly/.
After #timelyportfolio's first response:
That was a great write up, thanks! I was able to confirm what the event looks; I have already been able to listen to the _selected event, but it's nice to see.
I am trying to get a custom time value, which uses a draggable line in the chart, it's added like this:
addCustomTime("mytimevis", Sys.Date(), "CustomTimeId")
The doc for the underlying javascript widget (http://visjs.org/docs/timeline), shows an event ("timechanged"), and a method ("getCustomTime()") to get access to its value, but I don't see the event in the trace, nor can I figure out how to make the method call work.
The "timechanged" event does not show up in the trace, which makes me think I need to make the .on() call from the widget's doc in order to enable that event:
timeline.on('select', function (properties) {
alert('selected items: ' + properties.items);
});
I have been working to get that method call to work, since ideally I'd like to capture its change, and also if I can get that working, I should be able to get the getCustomTime() method working as well!
assumption
I will answer assuming that you would like to get the data in R. If I assumed incorrectly, let me know and I will adjust.
message with shiny.trace
If you are not aware, one trick I often use is options(shiny.trace=TRUE) which will fill your screen with all the messages sent over the websocket. Here is what I see when I run the following code.
library(timevis)
data <- data.frame(
id = 1:4,
content = c("Item one" , "Item two" ,"Ranged item", "Item four"),
start = c("2016-01-10", "2016-01-11", "2016-01-20", "2016-02-14 15:00:00"),
end = c(NA , NA, "2016-02-04", NA)
)
tv <- timevis(data)
# now let's see what messages we get in Shiny
library(shiny)
options(shiny.trace=TRUE)
ui <- timevisOutput("mytimevis")
server <- function(input,output,session) {
output$mytimevis <- renderTimevis({tv})
}
shinyApp(ui,server)
I highlighted the messages pertaining to select. This tells us we can observe or react to mytimevis_selected.
observe select event
Now, let's turn off options(shiny.trace=FALSE) and print in the R console when we receive a selected message.
options(shiny.trace=FALSE)
ui <- timevisOutput("mytimevis")
server <- function(input,output,session) {
output$mytimevis <- renderTimevis({tv})
observeEvent(
input$mytimevis_selected,
{
print(input$mytimevis_selected)
}
)
}
shinyApp(ui,server)
add timechanged handler
Based on comments and re-reading your question, I now understand that you would like to add an event handler for timechanged. I think this code will help you over the hump.
library(timevis)
library(shiny)
library(htmltools)
library(htmlwidgets)
library(magrittr)
tv <- timevis() %>%
addCustomTime(Sys.Date() - 1, "yesterday") %>%
# add an event handler since this is not one
# timevis provides
htmlwidgets::onRender(
"
function(el,x) {
// this will be our instance
var tv = this.timeline;
tv.on('timechanged', function(id, time) {
//uncomment debugger if you want to stop here
//debugger;
Shiny.onInputChange(el.id + '_timechanged', {id:id, time:time})
});
}
"
)
shinyApp(
ui = fluidPage(
timevisOutput("timeline"),
actionButton("btn", "Add time bar 24 hours ago")
),
server = function(input, output) {
output$timeline <- renderTimevis(
tv
)
observeEvent(input$timeline_timechanged, {
str(input$timeline_timechanged)
})
}
)
follow up
Please let me know if I headed down the wrong path or if none of this makes any sense. I feel your pain, and troubleshooting this blend of technology can be very tricky.
(Disclaimer: I wrote the timevis package)
First of all: Kenton's answer is excellent, and OP's attempts were great as well.
This is more of an informational post.
Why what you tried didn't work
The reason onevent("timechanged", ...) doesn't work is because the onevent() function works for any standard javascript events, but it doesn't work for specific events that different plugins provide.
To bind a custom event such as that one, you'd have write some short custom javascript code. So looking at the visjs documentation and finding the timeline.on('select', ...) code is the correct path, but (as you suspected) you weren't referencing the timeline object correctly.
I made sure to export the actual underlying object just for this reason, so that people like you could manipulate it in its raw format. To access the timeline object from timevis, you use $(id)[0].widget.timeline. Since the id of your timeline is timeline and you wanted to call the on() function, then you can do something like $("#timeline")[0].widget.timeline.on('select', ...).
Why this functionality doesn't exist
When I looked at the documentation of visjs I decided to have 4 events that the widget would export to shiny automatically whenever they changed: the data, the selected items, the IDs of all items, and the currently visible time window. I decided not to use the on('timechanged') event because it behaved differently from all the rest: it would only return one item at a time.
What do I mean by that? For example, input$timeline_data always returns the entire data in this moment; input$timeline_select always tells you what are all the selected items at this moment; input$timeline_timechanged would get fired every time a different vertical bar gets dragged, and therefore will contain only the last dragged bar, not all of them, and it wouldn't give you a way to ask for the time in one specific bar. Does that make sense?
Of course it's possible to write some code that keeps track of all the bars, but that gets very messy very quickly (for example, it'd have to make sure to get updated when a bar is added/removed). So I chose to not go down that rabbithole.

R Shiny display app is busy in a widget

I am developing a R Shiny application involving Twitter data fetching. As the process could last some time I would like to indicate that the application is busy doing something, so the user doesn't thing the page is frozen.
In my case, I store some reactive values this way:
rv <- reactiveValues()
rv$analysisStarted <- FALSE
rv$analysisAvailable <- FALSE
Then, in UI, the user must press an actionButton in order to start processing. Then, I would like to indicate somewhere the Server is working...
observeEvent(input$analysisButton, {
rv$analysisStarted <- TRUE
rv$analysisAvailable <- FALSE
#Processing Twitter info...
rv$followersAnalysisStarted <- FALSE
rv$followersAnalysisAvailable <- TRUE
})
If, in UI.r, I place a textOutput and create the corresponding output method this way, it does NOT work:
output$text <- renderText({
if (rv$analysisStarted) {
"Server is working..."
} else if (rv$analysisAvailable) {
"Your report is ready :) "
} else {
"Enter the data to search and press analysisButton"
}
})
What I have noticed is when the analysis begins, the label changes to a gray color, but it doesn't update the text until the process is over.
What should be the proper coding of this feature?
Is it possible to redraw the text output within observeEvent?
Is it possible with the raw shiny library or requires shinyjs, which I am also using?
Any help would be grateful.
In your case, it would be helpful a progressbar to check the state. This element would be appreciate by your users to indicate the state. Unfortunately, the progress bar would have to code before the functionality. It is a trick, you see a progress bar but this object stops when the function starts.
Here is the tutorial: http://shiny.rstudio.com/gallery/progress-bar-example.html
From my point of view, the progress bar is the best way to inform the web users of the website state but it is not completely perfect. Further you can change the style of the bar with CSS to customize and select your own colors, size...

Qualtrics.SurveyEngine.addOnload runs twice in preview mode in Qualtrics survey software

Whenever I try to test a Qualtrics survey in preview mode, Qualtrics.SurveyEngine.addOnload will be called twice. This is not a problem for conditional events (as in most of the examples), but a large problem for unconditional code a timed page change (this will be triggered twice as well). See the following snippet:
Qualtrics.SurveyEngine.addOnload(function()
{
$('NextButton') && $('NextButton').hide();
var that = this;
var timeOutInterval=1000+Math.trunc(Math.random()*10000);
alert(timeOutInterval); //for Testing only
var myVar;
myVar = setTimeout(function(){ that.clickNextButton();}, timeOutInterval);
});
If I launch the survey, this will lead to a page change after 1-11 seconds. If I preview the survey, this change will happen as well, followed by a second change. The alert will be shown twice as well.
Does anyone have a solution, how this functionality could be tested in preview mode?
I've run into Survey Preview issues with JFE as well. There are ways to get around JFE mode and preview in non-JFE mode.
If only care about a specific set of questions in a block and don't care about the survey flow, the easiest solution is to use View Block. It does not use JFE. Go to the Block drop down and choose View Block.
If you need to preview the whole survey, there are tricks to 'break' JFE and force it to non-JFE mode. These tricks seem to be a moving target as Qualtrics makes changes. The best one (easiest) I've found that is working for me today on my Qualtrics account (notice all the qualifiers) is to add an end of survey object to the survey flow, click custom, and check the "Override Survey Options" box.
If that doesn't work, I've found that once a survey gets over a certain size, it doesn't use JFE mode anymore. I don't know what the limit is, but if you add a bunch of fake questions after your end of survey you can trick it that way as well.
Qualtrics links jQuery as of current writing (albeit the shorthand $ is reserved for the prototype.js library).
Following should skip execution of addOnload javascript in the mobile preview:
Qualtrics.SurveyEngine.addOnload(function()
{
if(jQuery(this.questionContainer).parents('.MobilePreviewFrame').length)
{
console.log('Mobile Preview - skipping rest of addOnload');
return true;
};
console.log("Running addOnload()");
// The rest of your code. Log statements can obviously be removed
});
Hope this is helpful
It seems Qualtrics now defaults to JFE mode for live surveys as well. We have been able to resolve this by adding the query string &Q_JFE=0 to the end of our survey URLs, like so:
https://uleidenss.eu.qualtrics.com/SE/?SID=SV_123432434343&Q_JFE=0
This had the additional benefit of solving our issue with JFE mode breaking several of our long time running Qualtrics JQuery experiments.

Resources