## ========== svm_ui.R ==========

## 1.  plot 列表 ----------------------------------------------------------
svm_plots <- c(
  "none", "dist", "correlations", "scatter", "vip", "pred_plot", "pdp", "dashboard", "residuals", "coef", "influence"
)
names(svm_plots) <- c(
  i18n$t("None"),
  i18n$t("Distribution"),
  i18n$t("Correlations"),
  i18n$t("Scatter"),
  i18n$t("Permutation Importance"),
  i18n$t("Prediction plots"),
  i18n$t("Partial Dependence"),
  i18n$t("Dashboard"),
  i18n$t("Residuals vs fitted"),
  i18n$t("Coefficient plot"),
  i18n$t("Influential observations")
)

## 2.  函数缺省参数 -------------------------------------------------------
svm_args <- as.list(formals(svm))
## 3.  用户输入收集 -------------------------------------------------------
svm_inputs <- reactive({
  svm_args$data_filter <- if (input$show_filter) input$data_filter else ""
  svm_args$arr         <- if (input$show_filter) input$data_arrange else ""
  svm_args$rows        <- if (input$show_filter) input$data_rows   else ""
  svm_args$dataset     <- input$dataset
  for (i in r_drop(names(svm_args))) {
    svm_args[[i]] <- input[[paste0("svm_", i)]]
  }
  svm_args
})

## 4.  predict 参数 -------------------------------------------------------
svm_pred_args <- as.list(if (exists("predict.svm")) {
  formals(predict.svm)
} else {
  formals(e1071:::predict.svm)
})

svm_pred_inputs <- reactive({
  for (i in names(svm_pred_args)) {
    svm_pred_args[[i]] <- input[[paste0("svm_", i)]]
  }
  
  svm_pred_args$pred_cmd  <- svm_pred_args$pred_data <- ""
  if (input$svm_predict == "cmd") {
    svm_pred_args$pred_cmd <- gsub("\\s{2,}", " ", input$svm_pred_cmd) %>%
      gsub(";\\s+", ";", .) %>%
      gsub("\"", "\'", .)
  } else if (input$svm_predict == "data") {
    svm_pred_args$pred_data <- input$svm_pred_data
  } else if (input$svm_predict == "datacmd") {
    svm_pred_args$pred_cmd <- gsub("\\s{2,}", " ", input$svm_pred_cmd) %>%
      gsub(";\\s+", ";", .) %>%
      gsub("\"", "\'", .)
    svm_pred_args$pred_data <- input$svm_pred_data
  }
  svm_pred_args
})

## 5.  plot 参数 ---------------------------------------------------------
svm_plot_args <- as.list(if (exists("plot.svm")) {
  formals(plot.svm)
} else {
  formals(e1071:::plot.svm)
})

svm_plot_inputs <- reactive({
  for (i in names(svm_plot_args)) {
    svm_plot_args[[i]] <- input[[paste0("svm_", i)]]
  }
  svm_plot_args
})

## 6.  pred-plot 参数 ----------------------------------------------------
svm_pred_plot_args <- as.list(if (exists("plot.model.predict")) {
  formals(plot.model.predict)
} else {
  formals(radiant.model:::plot.model.predict)
})

svm_pred_plot_inputs <- reactive({
  for (i in names(svm_pred_plot_args)) {
    svm_pred_plot_args[[i]] <- input[[paste0("svm_", i)]]
  }
  svm_pred_plot_args
})

## 7.  响应变量 ----------------------------------------------------------
output$ui_svm_rvar <- renderUI({
  withProgress(message = i18n$t("Acquiring variable information"), value = 1, {
    if (input$svm_type == "classification") {
      vars <- two_level_vars()
    } else {
      isNum <- .get_class() %in% c("integer", "numeric", "ts")
      vars <- varnames()[isNum]
    }
  })
  
  init <- if (input$svm_type == "classification") {
    if (is.empty(input$logit_rvar)) isolate(input$svm_rvar) else input$logit_rvar
  } else {
    if (is.empty(input$reg_rvar)) isolate(input$svm_rvar) else input$reg_rvar
  }
  
  selectInput(
    inputId = "svm_rvar",
    label = i18n$t("Response variable:"),
    choices = vars,
    selected = state_single("svm_rvar", vars, init),
    multiple = FALSE
  )
})

## 8.  分类时选正类 ------------------------------------------------------
output$ui_svm_lev <- renderUI({
  req(input$svm_type == "classification")
  req(available(input$svm_rvar))
  levs <- .get_data()[[input$svm_rvar]] %>% as_factor() %>% levels()
  
  init <- if (is.empty(input$logit_lev)) isolate(input$svm_lev) else input$logit_lev
  selectInput(
    inputId = "svm_lev", label = i18n$t("Choose level:"),
    choices = levs,
    selected = state_init("svm_lev", init)
  )
})

## 9.  解释变量 ----------------------------------------------------------
output$ui_svm_evar <- renderUI({
  if (not_available(input$svm_rvar)) return()
  vars <- varnames()
  if (length(vars) > 0) vars <- vars[-which(vars == input$svm_rvar)]
  
  init <- if (input$svm_type == "classification") {
    if (is.empty(input$logit_evar)) isolate(input$svm_evar) else input$logit_evar
  } else {
    if (is.empty(input$reg_evar)) isolate(input$svm_evar) else input$reg_evar
  }
  
  selectInput(
    inputId = "svm_evar",
    label = i18n$t("Explanatory variables:"),
    choices = vars,
    selected = state_multiple("svm_evar", vars, init),
    multiple = TRUE,
    size = min(10, length(vars)),
    selectize = FALSE
  )
})

## 10.  权重变量 ---------------------------------------------------------
output$ui_svm_wts <- renderUI({
  isNum <- .get_class() %in% c("integer", "numeric", "ts")
  vars <- varnames()[isNum]
  if (length(vars) > 0 && any(vars %in% input$svm_evar)) {
    vars <- base::setdiff(vars, input$svm_evar)
    names(vars) <- varnames() %>% { .[match(vars, .)] } %>% names()
  }
  vars <- c("None", vars)
  
  selectInput(
    inputId = "svm_wts", label = i18n$t("Weights:"), choices = vars,
    selected = state_single("svm_wts", vars),
    multiple = FALSE
  )
})

## 11.  存储预测/残差名 --------------------------------------------------
output$ui_svm_store_pred_name <- renderUI({
  init <- state_init("svm_store_pred_name", "pred_svm") %>%
    sub("\\d{1,}$", "", .) %>%
    paste0(., ifelse(is.empty(input$svm_cost), "", input$svm_cost))
  textInput(
    "svm_store_pred_name",
    i18n$t("Store predictions:"),
    init
  )
})

output$ui_svm_store_res_name <- renderUI({
  req(input$dataset)
  textInput("svm_store_res_name", i18n$t("Store residuals:"), "", placeholder = i18n$t("Provide variable name"))
})

## 12.  预测与绘图重置 ---------------------------------------------------
observeEvent(input$dataset, {
  updateSelectInput(session, "svm_predict", selected = "none")
  updateSelectInput(session, "svm_plots",   selected = "none")
})
observeEvent(input$svm_type, {
  updateSelectInput(session, "svm_predict", selected = "none")
  updateSelectInput(session, "svm_plots",   selected = "none")
})

## 13.  预测控制 ----------------------------------------------------------
output$ui_svm_predict_plot <- renderUI({
  predict_plot_controls("svm")
})

## 14.  绘图数量 ----------------------------------------------------------
output$ui_svm_plots <- renderUI({
  req(input$svm_type)
  if (input$svm_type != "regression") {
    svm_plots <- head(svm_plots, -1)   # 去掉 regression 专用图
  }
  selectInput(
    "svm_plots", i18n$t("Plots:"),
    choices = svm_plots,
    selected = state_single("svm_plots", svm_plots)
  )
})

## 15.  绘图点数 ----------------------------------------------------------
output$ui_svm_nrobs <- renderUI({
  nrobs <- nrow(.get_data())
  choices <- c("1,000" = 1000, "5,000" = 5000, "10,000" = 10000, "All" = -1) %>% .[. < nrobs]
  selectInput(
    "svm_nrobs", i18n$t("Number of data points plotted:"),
    choices = choices,
    selected = state_single("svm_nrobs", choices, 1000)
  )
})

## 16.  刷新按钮 ----------------------------------------------------------
run_refresh(svm_args, "svm", tabs = "tabs_svm",
            label = i18n$t("Estimate model"),
            relabel = i18n$t("Re-estimate model"))

## 17.  主 UI 组装 --------------------------------------------------------
output$ui_svm <- renderUI({
  req(input$dataset)
  tagList(
    conditionalPanel(
      condition = "input.tabs_svm == 'Summary'",
      wellPanel(
        actionButton("svm_run", i18n$t("Estimate model"), width = "100%",
                     icon = icon("play", verify_fa = FALSE), class = "btn-success")
      )
    ),
    wellPanel(
      conditionalPanel(
        condition = "input.tabs_svm == 'Summary'",
        radioButtons(
          "svm_type",
          label = NULL,
          choices = c("classification", "regression") %>% {
            names(.) <- c(i18n$t("Classification"), i18n$t("Regression")); .
          },
          inline = TRUE
        ),
        uiOutput("ui_svm_rvar"),
        uiOutput("ui_svm_lev"),
        uiOutput("ui_svm_evar"),
        uiOutput("ui_svm_wts"),
        selectInput(
          "svm_kernel",
          label = i18n$t("Kernel:"),
          choices = c("linear", "polynomial", "radial", "sigmoid") %>% {
            names(.) <- c(i18n$t("Linear"), i18n$t("Polynomial"),
                          i18n$t("Radial"), i18n$t("Sigmoid")); .
          },
          selected = state_init("svm_kernel", "radial")
        ),
        fluidRow(
          column(6,
                 numericInput(
                   "svm_cost",
                   label = i18n$t("Cost (C):"),
                   min = 0.01, max = 100,
                   value = state_init("svm_cost", 1),
                   step = 0.01, width = "100%"
                 )
          ),
          column(6,
                 conditionalPanel(
                   "input.svm_kernel != 'linear'",
                   numericInput(
                     "svm_gamma",
                     label = i18n$t("Gamma:"),
                     min = 0.001, max = 10,
                     value = state_init("svm_gamma", "auto"),
                     step = 0.001, width = "100%"
                   )
                 )
          )
        ),
        fluidRow(
          column(6,
                 conditionalPanel(
                   "input.svm_kernel %in% c('polynomial', 'sigmoid')",
                   numericInput(
                     "svm_coef0",
                     label = i18n$t("Coef0:"),
                     min = 0, max = 100,
                     value = state_init("svm_coef0", 0),
                     width = "100%"
                   )
                 )
          ),
          column(6,
                 conditionalPanel(
                   "input.svm_type == 'regression'",
                   numericInput(
                     "svm_epsilon",
                     label = i18n$t("Epsilon (tube):"),
                     min = 0.001, max = 1,
                     value = state_init("svm_epsilon", 0.1),
                     step = 0.001, width = "100%"
                   )
                 )
          )
        ),
        numericInput(
          "svm_seed",
          label = i18n$t("Seed:"),
          value = state_init("svm_seed", 12345),
          width = "90px"
        ),
        conditionalPanel(
          "input.svm_type == 'classification'",
          checkboxInput(
            "svm_probability",
            label = i18n$t("Estimate class probabilities"),
            value = state_init("svm_probability", FALSE)
          )
        ),
      ),
      conditionalPanel(
        condition = "input.tabs_svm == 'Predict'",
        selectInput(
          "svm_predict",
          label = i18n$t("Prediction input type:"),
          choices = reg_predict,
          selected = state_single("svm_predict", reg_predict, "none")
        ),
        conditionalPanel(
          "input.svm_predict == 'data' | input.svm_predict == 'datacmd'",
          selectizeInput(
            inputId = "svm_pred_data", label = i18n$t("Prediction data:"),
            choices = c("None" = "", r_info[["datasetlist"]]),
            selected = state_single("svm_pred_data", c("None" = "", r_info[["datasetlist"]])),
            multiple = FALSE
          )
        ),
        conditionalPanel(
          "input.svm_predict == 'cmd' | input.svm_predict == 'datacmd'",
          returnTextAreaInput(
            "svm_pred_cmd", i18n$t("Prediction command:"),
            value = state_init("svm_pred_cmd", ""),
            rows = 3,
            placeholder = i18n$t("Type a formula to set values for model variables (e.g., carat = 1; cut = 'Ideal') and press return")
          )
        ),
        conditionalPanel(
          condition = "input.svm_predict != 'none'",
          checkboxInput("svm_pred_plot", i18n$t("Plot predictions"), state_init("svm_pred_plot", FALSE)),
          conditionalPanel(
            "input.svm_pred_plot == true",
            uiOutput("ui_svm_predict_plot")
          )
        ),
        conditionalPanel(
          "input.svm_predict == 'data' | input.svm_predict == 'datacmd'",
          tags$table(
            tags$td(uiOutput("ui_svm_store_pred_name")),
            tags$td(actionButton("svm_store_pred", i18n$t("Store"), icon = icon("plus", verify_fa = FALSE)), class = "top")
          )
        )
      ),
      conditionalPanel(
        condition = "input.tabs_svm == 'Plot'",
        uiOutput("ui_svm_plots"),
        conditionalPanel(
          condition = "input.svm_plots == 'pdp' | input.svm_plots == 'pred_plot'",
          uiOutput("ui_svm_incl"),
          uiOutput("ui_svm_incl_int")
        ),
        conditionalPanel(
          condition = "input.svm_plots == 'dashboard'",
          uiOutput("ui_svm_nrobs")
        )
      ),
      conditionalPanel(
        condition = "input.tabs_svm == 'Summary'",
        tags$table(
          tags$td(uiOutput("ui_svm_store_res_name")),
          tags$td(actionButton("svm_store_res", i18n$t("Store"), icon = icon("plus", verify_fa = FALSE)), class = "top")
        )
      )
    ),
    help_and_report(
      modal_title = i18n$t("Support Vector Machine (SVM)"),
      fun_name = "svm",
      help_file = inclMD(file.path(getOption("radiant.path.model"), "app/tools/help/svm.md"))
    )
  )
})

## 18.  绘图尺寸动态计算 -------------------------------------------------
svm_plot <- reactive({
  if (svm_available() != "available") return()
  if (is.empty(input$svm_plots, "none")) return()
  
  plot_width  <- 650
  if (input$svm_plots == "dashboard") {
    plot_height <- 750
  } else if (input$svm_plots %in% c("pdp", "pred_plot")) {
    nr_vars <- length(input$svm_incl) + length(input$svm_incl_int)
    plot_height <- max(250, ceiling(nr_vars / 2) * 250)
    if (length(input$svm_incl_int) > 0) {
      plot_width <- plot_width + min(2, length(input$svm_incl_int)) * 90
    }
  } else {
    plot_height <- 500
  }
  list(plot_width = plot_width, plot_height = plot_height)
})

svm_plot_width  <- function() svm_plot() %>% (function(x) if (is.list(x)) x$plot_width else 650)
svm_plot_height <- function() svm_plot() %>% (function(x) if (is.list(x)) x$plot_height else 500)
svm_pred_plot_height <- function() if (input$svm_pred_plot) 500 else 1

## 19.  输出注册 ----------------------------------------------------------
output$svm <- renderUI({
  register_print_output("summary_svm", ".summary_svm")
  register_print_output("predict_svm", ".predict_print_svm")
  register_plot_output("predict_plot_svm", ".predict_plot_svm",
                       height_fun = "svm_pred_plot_height")
  register_plot_output("plot_svm", ".plot_svm",
                       height_fun = "svm_plot_height",
                       width_fun  = "svm_plot_width")
  
  svm_output_panels <- tabsetPanel(
    id = "tabs_svm",
    tabPanel(i18n$t("Summary"), value = "Summary",
             download_link("dl_svm_coef"), br(),
             verbatimTextOutput("summary_svm")),
    tabPanel(i18n$t("Predict"), value = "Predict",
             conditionalPanel(
               "input.svm_pred_plot == true",
               download_link("dlp_svm_pred"),
               plotOutput("predict_plot_svm", width = "100%", height = "100%")
             ),
             download_link("dl_svm_pred"), br(),
             verbatimTextOutput("predict_svm")),
    tabPanel(i18n$t("Plot"), value = "Plot",
             download_link("dlp_svm"),
             plotOutput("plot_svm", width = "100%", height = "100%"))
  )
  
  stat_tab_panel(
    menu = i18n$t("Model > Estimate"),
    tool = i18n$t("Support Vector Machine (SVM)"),
    tool_ui = "ui_svm",
    output_panels = svm_output_panels
  )
})

## 20.  可用性检查 -------------------------------------------------------
svm_available <- reactive({
  req(input$svm_type)
  if (not_available(input$svm_rvar)) {
    if (input$svm_type == "classification") {
      i18n$t("This analysis requires a response variable with two levels and one\nor more explanatory variables. If these variables are not available\nplease select another dataset.\n\n") %>%
        suggest_data("titanic")
    } else {
      i18n$t("This analysis requires a response variable of type integer\nor numeric and one or more explanatory variables.\nIf these variables are not available please select another dataset.\n\n") %>%
        suggest_data("diamonds")
    }
  } else if (not_available(input$svm_evar)) {
    if (input$svm_type == "classification") {
      i18n$t("Please select one or more explanatory variables.") %>% suggest_data("titanic")
    } else {
      i18n$t("Please select one or more explanatory variables.") %>% suggest_data("diamonds")
    }
  } else {
    "available"
  }
})

## 21.  模型估计 ----------------------------------------------------------
.svm <- eventReactive(input$svm_run, {
  svmi <- svm_inputs()
  svmi$envir <- r_data
  withProgress(message = i18n$t("Estimating SVM"), value = 1,
               do.call(svm, svmi))
})

## 22.  summary ------------------------------------------------------------
.summary_svm <- reactive({
  if (not_pressed(input$svm_run)) return(i18n$t("** Press the Estimate button to estimate the model **"))
  if (svm_available() != "available") return(svm_available())
  summary(.svm())
})

## 23.  predict ------------------------------------------------------------
.predict_svm <- reactive({
  if (not_pressed(input$svm_run)) return(i18n$t("** Press the Estimate button to estimate the model **"))
  if (svm_available() != "available") return(svm_available())
  if (is.empty(input$svm_predict, "none")) return(i18n$t("** Select prediction input **"))
  if ((input$svm_predict == "data" || input$svm_predict == "datacmd") && is.empty(input$svm_pred_data))
    return(i18n$t("** Select data for prediction **"))
  if (input$svm_predict == "cmd" && is.empty(input$svm_pred_cmd))
    return(i18n$t("** Enter prediction commands **"))
  
  withProgress(message = i18n$t("Generating predictions"), value = 1, {
    spi <- svm_pred_inputs()
    spi$object <- .svm()
    spi$envir  <- r_data
    do.call(predict, spi)
  })
})

.predict_print_svm <- reactive({
  .predict_svm() %>% { if (is.character(.)) cat(., "\n") else print(.) }
})

## 24.  pred-plot ----------------------------------------------------------
.predict_plot_svm <- reactive({
  req(pressed(input$svm_run), input$svm_pred_plot, available(input$svm_xvar),
      !is.empty(input$svm_predict, "none"))
  withProgress(message = i18n$t("Generating prediction plot"), value = 1,
               do.call(plot, c(list(x = .predict_svm()), svm_pred_plot_inputs())))
})

## 25.  plot --------------------------------------------------------------
.plot_svm <- reactive({
  if (not_pressed(input$svm_run)) return(i18n$t("** Press the Estimate button to estimate the model **"))
  if (svm_available() != "available") return(svm_available())
  if (is.empty(input$svm_plots, "none")) return(i18n$t("Please select an SVM plot from the drop-down menu"))
  pinp <- svm_plot_inputs()
  pinp$shiny <- TRUE
  if (input$svm_plots == "dashboard") req(input$svm_nrobs)
  
  withProgress(message = i18n$t("Generating plots"), value = 1,
               do.call(plot, c(list(x = .svm()), pinp)))
})

## 26.  存储 --------------------------------------------------------------
observeEvent(input$svm_store_res, {
  req(pressed(input$svm_run))
  robj <- .svm()
  if (!is.list(robj)) return()
  fixed <- fix_names(input$svm_store_res_name)
  updateTextInput(session, "svm_store_res_name", value = fixed)
  withProgress(message = i18n$t("Storing residuals"), value = 1,
               r_data[[input$dataset]] <- store(r_data[[input$dataset]], robj, name = fixed))
})

observeEvent(input$svm_store_pred, {
  req(!is.empty(input$svm_pred_data), pressed(input$svm_run))
  pred <- .predict_svm()
  if (is.null(pred)) return()
  fixed <- fix_names(input$svm_store_pred_name)
  updateTextInput(session, "svm_store_pred_name", value = fixed)
  withProgress(message = i18n$t("Storing predictions"), value = 1,
               r_data[[input$svm_pred_data]] <- store(r_data[[input$svm_pred_data]], pred, name = fixed))
})

## 27.  report ------------------------------------------------------------
svm_report <- function() {
  if (is.empty(input$svm_evar)) return(invisible())
  
  outputs <- c("summary")
  inp_out <- list(list(prn = TRUE), "")
  figs <- FALSE
  
  if (!is.empty(input$svm_plots, "none")) {
    inp <- check_plot_inputs(svm_plot_inputs())
    inp_out[[2]] <- clean_args(inp, svm_plot_args[-1])
    inp_out[[2]]$custom <- FALSE
    outputs <- c(outputs, "plot")
    figs <- TRUE
  }
  
  if (!is.empty(input$svm_store_res_name)) {
    fixed <- fix_names(input$svm_store_res_name)
    updateTextInput(session, "svm_store_res_name", value = fixed)
    xcmd <- paste0(input$dataset, " <- store(", input$dataset, ", result, name = \"", fixed, "\")\n")
  } else {
    xcmd <- ""
  }
  
  if (!is.empty(input$svm_predict, "none") &&
      (!is.empty(input$svm_pred_data) || !is.empty(input$svm_pred_cmd))) {
    pred_args <- clean_args(svm_pred_inputs(), svm_pred_args[-1])
    
    if (!is.empty(pred_args$pred_cmd))
      pred_args$pred_cmd <- strsplit(pred_args$pred_cmd, ";\\s*")[[1]]
    else
      pred_args$pred_cmd <- NULL
    
    if (!is.empty(pred_args$pred_data))
      pred_args$pred_data <- as.symbol(pred_args$pred_data)
    else
      pred_args$pred_data <- NULL
    
    inp_out[[2 + figs]] <- pred_args
    outputs <- c(outputs, "pred <- predict")
    xcmd <- paste0(xcmd, "print(pred, n = 10)")
    if (input$svm_predict %in% c("data", "datacmd")) {
      fixed <- fix_names(input$svm_store_pred_name)
      updateTextInput(session, "svm_store_pred_name", value = fixed)
      xcmd <- paste0(xcmd, "\n", input$svm_pred_data, " <- store(",
                     input$svm_pred_data, ", pred, name = \"", fixed, "\")")
    }
    
    if (input$svm_pred_plot && !is.empty(input$svm_xvar)) {
      inp_out[[3 + figs]] <- clean_args(svm_pred_plot_inputs(), svm_pred_plot_args[-1])
      inp_out[[3 + figs]]$result <- "pred"
      outputs <- c(outputs, "plot")
      figs <- TRUE
    }
  }
  
  svm_inp <- svm_inputs()
  if (input$svm_type == "regression") svm_inp$lev <- NULL
  
  update_report(
    inp_main  = clean_args(svm_inp, svm_args),
    fun_name  = "svm",
    inp_out   = inp_out,
    outputs   = outputs,
    figs      = figs,
    fig.width = svm_plot_width(),
    fig.height= svm_plot_height(),
    xcmd      = xcmd
  )
}

## 28.  下载 --------------------------------------------------------------
dl_svm_pred <- function(path) {
  if (pressed(input$svm_run)) {
    write.csv(.predict_svm(), file = path, row.names = FALSE)
  } else {
    cat(i18n$t("No output available. Press the Estimate button to generate results"), file = path)
  }
}

download_handler(
  id = "dl_svm_pred",
  fun = dl_svm_pred,
  fn = function() paste0(input$dataset, "_svm_pred"),
  type = "csv",
  caption = i18n$t("Save SVM predictions")
)

download_handler(
  id = "dlp_svm_pred",
  fun = download_handler_plot,
  fn = function() paste0(input$dataset, "_svm_pred"),
  type = "png",
  caption = i18n$t("Save SVM prediction plot"),
  plot = .predict_plot_svm,
  width = plot_width,
  height = svm_pred_plot_height
)

download_handler(
  id = "dlp_svm",
  fun = download_handler_plot,
  fn = function() paste0(input$dataset, "_svm"),
  type = "png",
  caption = i18n$t("Save SVM plot"),
  plot = .plot_svm,
  width = svm_plot_width,
  height = svm_plot_height
)

## 29.  report / screenshot 监听 -----------------------------------------
observeEvent(input$svm_report, {
  r_info[["latest_screenshot"]] <- NULL
  svm_report()
})

observeEvent(input$svm_screenshot, {
  r_info[["latest_screenshot"]] <- NULL
  radiant_screenshot_modal("modal_svm_screenshot")
})

observeEvent(input$modal_svm_screenshot, {
  svm_report()
  removeModal()
})