Реактивно проверить, доступен ли файл - PullRequest
0 голосов
/ 16 апреля 2020

Я разрабатываю блестящее приложение, которое позволяет импортировать некоторые данные, манипулировать ими и наносить их на график. Если вы удовлетворены результатами анализа, вы можете сгенерировать файл .xlsx с помощью скрипта python (я использую сетку, чтобы он работал внутри блестящего приложения):

server <- function(input, output){
  observeEvent(input$run_py, {
    # pass variable from R to Py
    py$df <- avg.spread()
    py$var <- var.spread()
    py_run_file('py_export_excel.py', local = FALSE, convert = FALSE)
  })
}

Файл .xlsx не готово мгновенно, поэтому мне нужна другая кнопка, которая позволяет вам загрузить файл.

server <- function(input, output){
  output$downloadData <- downloadHandler(
    filename = "file.xlsx",
    content <- function(file) {
      file.copy("test.xlsx", file)
    }
)}

Все работает до этого момента, но мне не нравится идея нажимать две разные кнопки, чтобы сделать только одну вещь. Я хочу иметь реактивный обработчик, который постоянно проверяет, существует ли файл, и автоматически предлагает вам загрузить его. Есть ли способ сделать следующий код реактивным?

if(file.exists("test.xlsx")) {
  # download code
  ...
}

1 Ответ

0 голосов
/ 16 апреля 2020

Вы можете использовать invalidateLater для повторения (после задержки) реактивного блока. Попробуйте это:

library(shiny)
library(shinyjs)
.log <- function(...) message(format(Sys.time(), format = "[ %H:%M:%S ]"), " ", ...)
.maxtime <- 60
shinyApp(
  ui = fluidPage(
    useShinyjs(),
    actionButton("doit", "Long process ..."),
    downloadButton("dnld", "Download!")
  ),
  server = function(input, output, session) {
    .log("hello world")

    started <- reactiveVal(Sys.time()[NA])
    shinyjs::disable("dnld")
    # just for this answer
    if (file.exists("61240116.txt")) warning("file exists: '61240116.txt'", call. = FALSE)

    # this is your "long process", here I just delay and touch the
    # file; the important parts to preserve are 'disable' and
    # 'started' (or similar)
    observeEvent(input$doit, {
      shinyjs::disable("dnld")
      .log("starting a long process ...")
      started(Sys.time())
      system("sh -c '{ sleep 5 ; touch 61240116.txt; }'", wait = FALSE)
    })

    # this is the work-horse here: it starts whenever 'started' is
    # triggered, and keeps working as long as it has not been too long
    observe({
      req(started())
      d <- difftime(Sys.time(), started(), units = "secs")
      if (file.exists("61240116.txt")) {
        .log("... done!")
        started(Sys.time()[NA])
        shinyjs::enable("dnld")
      } else if (d > .maxtime) {
        .log("timed out")
        # might need to do some damage-control here
      } else {
        .log("... cycling, ", round(d, 1))
        invalidateLater(1000, session)
      }
    })

    # simple/naive download handler
    output$dnld <- downloadHandler(
      filename = function() "61240116.txt",
      content = function(file) file.copy("61240116.txt", file)
    )
  }
)

# Listening on http://127.0.0.1:5020
# [ 16:56:08 ] hello world
# [ 16:56:17 ] starting a long process ...
# [ 16:56:18 ] ... cycling, 0
# [ 16:56:19 ] ... cycling, 1
# [ 16:56:20 ] ... cycling, 2
# [ 16:56:21 ] ... cycling, 3
# [ 16:56:22 ] ... cycling, 4
# [ 16:56:23 ] ... cycling, 5
# [ 16:56:24 ] ... done!
  1. Запустите приложение, обратите внимание, что Download! отключен (первый shinyjs::disable);
  2. Нажмите кнопку Long process ..., обратите внимание на На консоли вы должны увидеть некоторые сообщения о состоянии.
  3. Примерно через 5 секунд кнопка загрузки должна стать активной, и теперь вы можете загрузить файл.

shiny page, before and after

«Техника» этого мини-приложения:

  • started двоякая: запустите циклическую c -инвализацию 1000 мс, а также сохраните эту не бегать вечно. (Конечно, возможно (если вы «установите» started в нескольких местах), чтобы сломать эту конструкцию.) Использование «времени» может быть не идеальным для вашей ситуации, и при этом оно не полностью защищено от ошибок (например, сильно загружено) системе может потребоваться больше времени).
  • Я установил и сбросил started на Sys.time()[NA] для трех целей: во-первых, это должно быть всегда POSIXt, а не что-то «недавнее»; во-вторых, difftime расстраивается, когда это просто число (например, 0), поскольку для этого требуется источник времени; и, в-третьих, req приведет к преждевременному завершению блока observe, если это какая-то форма NA (или что-то еще не верное ... см. ?req). Мы полагаемся на это поведение.
  • shinyjs используется для отключения кнопки загрузки, чтобы мы контролировали, когда что-то доступно для загрузки.
...