【问题标题】:How to persist changes in a text file using shiny?如何使用闪亮持久保存文本文件中的更改?
【发布时间】:2017-04-04 04:43:58
【问题描述】:

我有一个用于注释文本文件的闪亮小应用程序。

  1. UI 提供fileInput 来选择.txt 文件。其中一个文件是应用程序启动时的默认文件。
  2. NextPrevious 按钮允许用户一次显示一个句子的文件内容。
  3. 用户可以选择句子中的任何文本并单击Add Markup 按钮对句子进行注释。操作按钮触发 javascript 函数 addMarkup()
  4. 句子标记后显示。

我只在这里发布闪亮的应用程序代码。应用程序的完整代码在github repository

  library(shiny)
  ui <- fluidPage(
  tags$head(tags$script(src="textselection.js")),
  titlePanel("Corpus Annotation Utility"),
  sidebarLayout(
    sidebarPanel(
      fileInput('fileInput', 'Select Corpus', accept = c('text', 'text','.txt')),
      actionButton("Previous", "Previous"),
      actionButton("Next", "Next"),
      actionButton("mark", "Add Markup")
    ),
    mainPanel(
     tags$h1("Sentence: "),
     htmlOutput("sentence"),
     tags$h1("Sentence marked up: "),
     htmlOutput("sentenceMarkedUp") 
    )
  )
)
server <- function(input, output) {
    sourceData <- reactive({
   corpusFile <- input$fileInput
   if(is.null(corpusFile)){
     return(readCorpus('data/news.txt'))
   }
  readCorpus(corpusFile$datapath)
  })

 corpus <- reactive({sourceData()}) 
 values <- reactiveValues(current = 1)
  observeEvent(input$Next,{
    if(values$current >=1 & values$current < length(corpus())){
      values$current <- values$current + 1
    }
  })
  observeEvent(input$Previous,{
    if(values$current > 1 & values$current <= length(corpus())){
      values$current <- values$current - 1
    }
  })
  output$sentence <- renderText(corpus()[values$current])
}
shinyApp(ui = ui, server = server)  

readCorpus() 函数如下所示:

readCorpus <- function(pathToFile){
  con <- file(pathToFile) 
  sentences <- readLines(con, encoding = "UTF-8")
  close(con)
  return(sentences)
}

我的问题是如何在句子被注释后将它们保存到文件中?

更新: 我已经浏览了Persistent data storage in Shiny apps,并希望我能够遵循有关持久存储的文档。但是,我仍然不确定如何在标记后捕获句子。

【问题讨论】:

  • 1) 在上面的代码和github上,我都找不到“标记代码”;没有使用input$mark 的功能,....我错过了什么,或者它可能在MyUtils.R 内没有共享? 2)关于持久化数据存储环节:您正在寻找本地文件系统解决方案?
  • @BigDataScientist 1) 用 JS 完成
  • 我已更新 github 存储库以包含 MyUtils.R。关于持久数据存储 客户可以将文件保存在自己的机器上是理想的。
  • 这里的问题是您的标记只是在模仿用户的选择,因此它没有在句子本身中硬编码,因此很难保存。您应该尝试使用span,就像我在原始问题here 中建议的那样。这将硬编码字符串中的选择并使其易于保存。
  • 你需要为每个句子捕获多个注释吗?

标签: r shiny


【解决方案1】:

这里有两个问题 - 保留更改,然后保存输出。我用一点 JS 和一点 R 代码解决了这个问题。我将在 Github 上提交请求以提交更广泛的代码。然而,这是它的核心。

在您用于选择事物的 Javascript 中,您可以使用 Shiny.onInputChange() 更新 input 向量的元素。这样做,您可以为语料库创建一个reactiveValues 项目,然后使用您界面中的输入更新它。

在下面,您会注意到我从使用文本节点切换到仅使用内部 HTML。使用一个节点和firstChild,就像你以前一样,你最终会在第一个注释之后截断句子(因为它只选择&lt;mark&gt;之前的东西。这样做似乎效果更好。

window.onload = function(){
  document.getElementById('mark').addEventListener('click', addMarkup);
}

function addMarkup(){
  var sentence = document.getElementById("sentence").innerHTML,
  selection="";
  if(window.getSelection){
    selection = window.getSelection().toString();
  }
  else if(document.selection && document.selection.type != "Control"){
    selection = document.selection.createRange().text;
  }
  if(selection.length === 0){
    return;
  }
  marked = "<mark>".concat(selection).concat("</mark>");
  result = sentence.replace(selection, marked);
  document.getElementById("sentence").innerHTML = result;
  Shiny.onInputChange("textresult",result);
}

接下来,我尝试简化您的server.R 代码。您正在使用响应式上下文从另一个响应式上下文中提取(sourceDatacorpus),这似乎没有必要。所以,我试着重构了一下。

library(shiny)
source("MyUtils.R")
ui <- fluidPage(
  tags$head(tags$script(src="textselection.js")),
  titlePanel("Corpus Annotation Utility"),
  sidebarLayout(
    sidebarPanel(
      fileInput('fileInput', 'Select Corpus', accept = c('text', 'text','.txt')),
      actionButton("Previous", "Previous"),
      actionButton("Next", "Next"),
      actionButton("mark", "Add Markup"),
      downloadButton(outputId = "save",label = "Download")),
    mainPanel(
      tags$h1("Sentence: "),
      htmlOutput("sentence"))
  )
)

server <- function(input, output) {
  corpus <- reactive({
    corpusFile <- input$fileInput
    if(is.null(corpusFile)) {
      return(readCorpus('data/news.txt'))
    } else {
      return(readCorpus(corpusFile$datapath))
    }
  })

  values <- reactiveValues(current = 1)
  observe({
    values$corpus <- corpus()
  })
  output$sentence <- renderText(values$corpus[values$current])

  observeEvent(input$Next,{
    if(values$current >=1 & values$current < length(corpus())) {
      values$current <- values$current + 1
    }
  })
  observeEvent(input$Previous,{
    if(values$current > 1 & values$current <= length(corpus())) {
      values$current <- values$current - 1
    }
  })
  observeEvent(input$mark,{
    values$corpus[values$current] <- input$textresult
  })
  output$save <- downloadHandler(filename = "marked_corpus.txt",
                                 content = function(file) {

                                   writeLines(text = values$corpus,
                                              con = file,
                                              sep = "\n")
                                 })
}

现在,代码有一些变化。从文件加载基本相同。我对isolate 的怀疑是正确的——用observe 替换它可以完成我想做的事情,而isolate 只会给你初始负载。无论如何,我们使用 observe 将语料库值加载到您创建的 reactiveValues 对象中 - 这是为了给我们一个传播数据更改的地方。

我们保留前进和后退的剩余逻辑。但是,我们更改了输出的呈现方式,使其查看reactiveValues 对象。然后,我们创建一个观察者,用我们更新的 Javascript 的输入更新reactiveValues 对象。发生这种情况时,数据将被永久存储,您还可以在字符串中标记多个序列(尽管我没有对嵌套标记或删除标记做过任何事情)。最后,添加了一个保存功能 - 将生成的字符串与用于显示标记区域的&lt;mark&gt; 一起保存。

如果您加载以前标记的文件,标记将再次显示。

【讨论】:

    猜你喜欢
    • 2015-01-17
    • 1970-01-01
    • 1970-01-01
    • 2014-08-24
    • 2020-10-18
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多