闪亮的无功输出未按预期更新

时间:2019-01-23 16:49:01

标签: r shiny

我创建了一个闪亮的应用程序,可从节点列表中提取软件组件及其版本。这样做的目的是在可能的情况下使我们所有的节点保持一致,此应用程序可帮助我们查看哪些节点不一致。

当前,您可以在“基准” handontable中修改版本,它将通过更改以及handsontable中的BaselineStats列来动态更新下面的数据透视表。这按预期工作。我被要求增加上传csv文件的功能,该文件将覆盖基准表,因此用户不必在每次加载应用程序时更改这些“基准”版本。

此外,有些组件是100%一致的。当前,这些选项未出现在“基准”操作列表中(因为这是显示不一致的工具),但是我添加了一个复选框,以便用户仍可以报告那些100%一致的组件。

由于某种原因,fileUpload和checkboxInput都没有更新,无论我在代码中戳多少戳,我都无法弄清楚为什么。

server.R

library(shiny)
library(rhandsontable)
library(rpivotTable)
library(dplyr)
library(stringr)
library(lubridate)

shinyServer(function(input, output) {

  # Create dataframe
df.consistency <- structure(list(Node = structure(c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 
                                    2L, 3L, 3L, 3L, 3L, 4L, 4L, 4L, 4L), .Label = c("A", "B", "C", 
                                                                                    "D"), class = "factor"), Component = structure(c(3L, 4L, 1L, 2L, 3L, 
                                                                                                                                     4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L), .Label = c("docker.version", 
                                                                                                                                                                                             "kernel.version", "os.name", "os.version"), class = "factor"), 
                 Version = structure(c(10L, 3L, 1L, 6L, 10L, 3L, 1L, 7L, 10L, 
                                     5L, 1L, 8L, 10L, 4L, 2L, 9L), .Label = c("1.12.1", "1.13.1", 
                                                                              "16.04", "17.04", "18.04", "3.10.0", "3.11.0", "3.12.0", 
                                                                              "3.13.0", "RedHat"), class = "factor")), class = "data.frame", row.names = c(NA, 
                                                                                                                                                                     -16L))

# Get Date Time
Report.Date <- Sys.Date()

df.baseline <- reactive({

  inputFile <- input$uploadBaselineData

  if(!is.null(inputFile)){

    read.csv(inputFile$datapath, header = input$header)

  } else{
    if(input$showConsistent == FALSE){

      # Count the number of occurrences for Version and Component, then remove the Components that are consistent (not duplicated => nn == 1) and then remove nn column
      df.clusterCons.countComponent <- df.consistency %>%
        add_count(Version, Component) %>%
        add_count(Component) %>%
        filter(nn > 1) %>%
        select(-nn)

      # Change back to dataframe after grouping
      df.clusterCons.countComponent <- as.data.frame(df.clusterCons.countComponent)

      # Components and Versions are shown for every node/cluster. 
      # Reduce this df to get only a unique Component:Version combinations
      df.clusterCons.dist_tbl <- df.clusterCons.countComponent %>%
        distinct(Component, Version, .keep_all = TRUE)

      #Create a df that contains only duplicated rows (rows that are unique i.e. versions are consistent, are removed)
      df.clusterCons.dist_tbl.dup <- df.clusterCons.dist_tbl %>%
        filter(Component %in% unique(.[["Component"]][duplicated(.[["Component"]])]))

      #Create a baseline df to be used to filter larger dataset later 
      #(baseline = max(n) for Version -- but must retain Component since that is the parameter we will use to filter on later)
      df.clusterCons.baseline <- df.clusterCons.dist_tbl.dup[order(df.clusterCons.dist_tbl.dup$Component, df.clusterCons.dist_tbl.dup$n, decreasing = TRUE),]
      df.clusterCons.baseline <- df.clusterCons.baseline[!duplicated(df.clusterCons.baseline$Component), ]
      df.clusterCons.baseline <- df.clusterCons.baseline %>% 
        select(Component, Version)



    }
    else{
      # Count the number of occurrences for Version and Component, then remove the Components that are consistent (not duplicated => nn == 1) and then remove nn column
      df.clusterCons.countComponent <- df.consistency %>%
        add_count(Version, Component) %>%
        add_count(Component) %>%
        select(-nn)

      # Change back to dataframe after grouping
      df.clusterCons.countComponent <- as.data.frame(df.clusterCons.countComponent)

      # Components and Versions are shown for every node/cluster. 
      # Reduce this df to get only a unique Component:Version combinations
      df.clusterCons.dist_tbl <- df.clusterCons.countComponent %>%
        distinct(Component, Version, .keep_all = TRUE)

      df.clusterCons.baseline <- df.clusterCons.dist_tbl[order(df.clusterCons.dist_tbl$Component, df.clusterCons.dist_tbl$n, decreasing = TRUE),]
      df.clusterCons.baseline <- df.clusterCons.baseline[!duplicated(df.clusterCons.baseline$Component), ]
      df.clusterCons.baseline <- df.clusterCons.baseline %>% 
        select(Component, Version)
    }
  }
})


df.componentVersionCounts <- df.consistency %>%
  add_count(Component) %>%
  rename("CountComponents" = n) %>%
  add_count(Component, Version) %>%
  rename("CountComponentVersions" = n) %>%
  mutate("BaselineStats" = paste0("Baseline: ", round(CountComponentVersions / CountComponents * 100, 2), "% of Total: ", CountComponents)) %>%
  select(Component, Version, BaselineStats) %>%
  distinct(.keep_all = TRUE)

df.componentVersions_tbl <- reactive({
  df.componentVersions_tbl <- df.baseline() %>%
    distinct(Component, .keep_all = TRUE) %>%
    select(Component, Version) %>%
    left_join(df.componentVersionCounts, by = c("Component" = "Component", "Version" = "Version"))

})

# Report Date Output
output$reportDate <- renderText({
  return(paste0("Report last run: ", Report.Date))
})

# handsontable showing baseline and allowing for an updated baseline
output$baseline_table <- rhandsontable::renderRHandsontable({

  rhandsontable(df.componentVersions_tbl(), rowHeaders = NULL) %>%
    hot_col("Component", readOnly = TRUE) %>%
    hot_col("BaselineStats", readOnly = TRUE) %>%
    hot_cols(columnSorting = TRUE) %>%
    hot_context_menu(allowRowEdit = FALSE, allowColEdit = FALSE, filters = TRUE)

})

observe({
  hot = isolate(input$baseline_table)
  if(!is.null(input$baseline_table)){
    handsontable <- hot_to_r(input$baseline_table)

    df.clusterCons.baseline2 <- handsontable %>%
      select(-BaselineStats)

    df.componentVersions_tbl <- df.clusterCons.baseline2  %>%
      left_join(df.componentVersionCounts, by = c("Component" = "Component", "Version" = "Version"))

    output$baseline_table <- rhandsontable::renderRHandsontable({

      rhandsontable(df.componentVersions_tbl, rowHeaders = NULL) %>%
        hot_col("Component", readOnly = TRUE) %>%
        hot_col("BaselineStats", readOnly = TRUE) %>%
        hot_cols(columnSorting = TRUE) %>%
        hot_context_menu(allowRowEdit = FALSE, allowColEdit = FALSE, filters = TRUE)

    })

    df.clusterIncons <- anti_join(df.consistency, handsontable, by = c("Component" = "Component", "Version" = "Version"))
    df.clusterIncons <- df.clusterIncons

    # Pivot Table showing data with inconsistencies 
    output$pivotTable <- rpivotTable::renderRpivotTable({
      rpivotTable::rpivotTable(df.clusterIncons, rows = c("Cluster", "Node"), cols = "Component", aggregatorName = "List Unique Values", vals = "Version", 
                               rendererName = "Table", 
                               inclusions = list(Component = list("os.version", "os.name", "kernel.version", "docker.version")))


    })

    output$downloadBaselineData <- downloadHandler(
      filename = function() {
        paste('baselineData-', Sys.Date(), '.csv', sep='')
      },
      content = function(file) {
        baseline_handsontable <- handsontable %>%
          select(-BaselineStats)
        write.csv(baseline_handsontable, file, row.names = FALSE)
      }
    )


    output$downloadPivotData <- downloadHandler(
      filename = function() {
        paste('pivotData-', Sys.Date(), '.csv', sep='')
      },
      content = function(file) {
        write.csv(df.clusterIncons, file, row.names = FALSE)
      }
    )

  }
})

})

ui.R

library(shiny)
library(shinydashboard)
library(rhandsontable)
library(rpivotTable)

dashboardPage(

  dashboardHeader(title = "Test Dashboard", titleWidth = "97%"),

  dashboardSidebar(
    collapsed = TRUE,
    sidebarMenu(
      menuItem("App", tabName = "app", icon = icon("table"))
    )
  ),

  dashboardBody(

    tabItems(
      tabItem("app",
              fluidRow(
                box(width = 3, background = "light-blue",
                    "This box includes details to the user about how the application works", br(), br(), br(), 
                    verbatimTextOutput("reportDate")
                ),
                box(width = 7, status = "info", title = "Version baselines based on greatest occurance",
                    rHandsontableOutput("baseline_table", height = "350px")
                ),
                column(width = 2, 
                       fluidRow(
                         fileInput("uploadBaselineData", "Upload Other Baseline Data:", multiple = FALSE, 
                                   accept = ".csv")
                       ),
                       fluidRow(
                         downloadButton("downloadBaselineData", "Download Baseline Data")
                       ),
                       br(), 
                       fluidRow(
                         downloadButton("downloadPivotData", "Download Pivot Table Data")
                       ),
                       br(), 
                       fluidRow(
                         checkboxInput("showConsistent", "Show Consistent Components in baseline")
                       )
                )
              ),
              fluidRow(
                box(width = 12, status = "info", title = "Nodes with versions inconsistent with baseline",
                    div(style = 'overflow-x: scroll', rpivotTable::rpivotTableOutput("pivotTable", height = "500px"))
                )
              )
              )
    )
)
    )

我经常与反应性打交道,但是我不经常使用观察或隔离,所以这可能是我遇到问题的地方。我也尝试了新的reactlog软件包,但是我仍然不确定前进的方向。

这是我单击复选框或上传新基线数据之前反应日志输出的图片: Reactlog Output 1 之后: enter image description here

1 个答案:

答案 0 :(得分:1)

实际上,Shiny App的给定结构非常复杂,并且无法有效利用反应性。因此,首先我们可以从一个简单的应用开始,以确保基本组件正常运行,然后添加更多组件。

一些问题

  • 所包含的数据帧df.consistency会干扰您要添加的实际电抗组件。例如,if/else流是有问题的,因为它总是跳到第一个else,因为启动应用程序时csv不存在,并且读取它的表达式不正确,但是{{1} }始终可用。

  • 重复了两次定义的相同组件,例如df.consistency

  • 使用output$baseline_table,您传递了一个未定义的参数read.csv(如果您从示例here中选取此参数,则它指向复选框,但是在这里无效)

最小应用

如果要从最小的应用程序开始,则可以从以下代码开始。这将使您能够:

  • 使用默认数据或上传header = input$header以覆盖默认数据。
  • 在中间的csv中查看结果。

注意:

  • rhandsontable是反应性的,这就是为什么使用它的其他表达式也是反应性的。

  • 如果要根据复选框进行baseline_data的不同计算,可以在表达式内添加df.componentVersionCounts来编写两种情况的计算。

if/else