Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Load Reporter #1120

Merged
merged 18 commits into from
Apr 24, 2024
Merged

Conversation

Polkas
Copy link
Contributor

@Polkas Polkas commented Mar 2, 2024

connected with insightsengineering/teal.reporter#251

My work is done as a collaboration of UCB company with Roche.
insightengineering developers can edit this PR.

Objective: Support the Load Report functionality for TealRerportCard and TealSliceBlock

  • Update the to_list and from_list TealSliceBlock methods. They operate on a string output.
  • Export TealSliceBlock which is needed for the teal.reporter ReportCard.
  • set_id for the Reporter so we protect that it can be reloaded only for the same teal app.
  • Fix the wrong docs.

@Polkas Polkas changed the title Support Archiver Support Load Reporter Mar 8, 2024
@Polkas Polkas marked this pull request as ready for review March 9, 2024 19:23
Copy link
Contributor

github-actions bot commented Mar 9, 2024

CLA Assistant Lite bot ✅ All contributors have signed the CLA

@Polkas
Copy link
Contributor Author

Polkas commented Mar 10, 2024

I have read the CLA Document and I hereby sign the CLA

@gogonzo
Copy link
Contributor

gogonzo commented Apr 12, 2024

@Polkas could you please merge main to this branch. There are some changes in teal which are important in this context

@Polkas
Copy link
Contributor Author

Polkas commented Apr 12, 2024

@gogonzo done:)

@gogonzo gogonzo self-assigned this Apr 23, 2024
@gogonzo gogonzo added the core label Apr 23, 2024
Copy link
Contributor

@gogonzo gogonzo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much for fruitful collaboration 🔥

@gogonzo
Copy link
Contributor

gogonzo commented Apr 23, 2024

@Polkas we need to overcome some cicd issues. You will be able to merge soon 💪

@Polkas
Copy link
Contributor Author

Polkas commented Apr 23, 2024

We need to merge the teal.reporter PR first.

gogonzo added a commit to insightsengineering/teal.reporter that referenced this pull request Apr 24, 2024
closes #81 
continuation of
#177
linked to insightsengineering/teal#1120 Please
install this teal branch when testing the code

I created a new PR from the fork as I am no longer part of the
insightengineering group.
My work is done as a collaboration of UCB company with Roche.
insightengineering developers can edit this PR.

I followed a simple design, which was evaluated positively in [the
discussion](#81).

DONE:

- New modules `report_load_srv` and `report_load_ui`; similar direct
update for Previewer.
- REMOVE the `Archiver` Class as we not need it for this simplified
scenario.
- Improve `to_list` and `from_list` `Reporter` methods
- Add `set_id` and `get_id` `Reporter` methods. Optionally add id to a
Report which will be compared when it is rebuilt from a list. To test it
in the teal example app please download a report and then add a new
module or dataset to the app and try to load it back. The report can be
loaded back to teal app only with the same datasets and modules. The id
is added to the downloaded file name if exists.
- Improve `to_list` and `from_list` `ReportCard` methods (linked with
insightsengineering/teal#1120)
- Both already existing vignette apps are updated automatically.
- `warning(cond)` everywhere to be consistent. We should send the
error/warning to the R console when STH fails.
- Add `testServer` tests for report_load_srv/report_load_ui modules.
- UI tested with all 3 bootstrap versions.

Points to consider:

- The JSON format Report representation seems to be enough, so an
Archiver is unnecessary. The DB solution to save/load seems overcomplex
for the project.
- No update will be required to introduce it into teal modules. Simple
reporter is updated automatically and can be customized with a new
teal.reporter option.
- When reloading, the Report is validated by the "report_" file name
prefix later by the slot name "teal Report" and optionally by ID if it
is non-empty.

Example Teal App (play with bootstrap versions, simple reporter modules,
and add new data/module to confirm the report can not be then reloaded):

```r
library(teal.modules.general)
# one of c("3", "4", "5")
options("teal.bs_theme" = bslib::bs_theme(version = "4"))

data <- teal_data()
data <- within(data, {
  library(nestcolor)
  ADSL <- teal.modules.general::rADSL
})
datanames <- c("ADSL")
datanames(data) <- datanames
join_keys(data) <- default_cdisc_join_keys[datanames]

app <- teal::init(
  data = data,
  modules = teal::modules(
    teal.modules.general::tm_a_regression(
      label = "Regression",
      response = teal.transform::data_extract_spec(
        dataname = "ADSL",
        select = teal.transform::select_spec(
          label = "Select variable:",
          choices = "BMRKR1",
          selected = "BMRKR1",
          multiple = FALSE,
          fixed = TRUE
        )
      ),
      regressor = teal.transform::data_extract_spec(
        dataname = "ADSL",
        select = teal.transform::select_spec(
          label = "Select variables:",
          choices = teal.transform::variable_choices(data[["ADSL"]], c("AGE", "SEX", "RACE")),
          selected = "AGE",
          multiple = TRUE,
          fixed = FALSE
        )
      ),
      ggplot2_args = teal.widgets::ggplot2_args(
        labs = list(subtitle = "Plot generated by Regression Module")
      )
    )
  )
)
runApp(app, launch.browser = TRUE)
```

Example general shiny app (play with bootstrap versions, simple reporter
modules):

```r
library(shiny)
library(teal.reporter)
library(ggplot2)
library(rtables)
library(DT)
library(bslib)

ui <- fluidPage(
    # please, specify specific bootstrap version and theme
    theme = bs_theme(version = "4"),
    titlePanel(""),
    tabsetPanel(
        tabPanel(
            "main App",
            tags$br(),
            sidebarLayout(
                sidebarPanel(
                    uiOutput("encoding")
                ),
                mainPanel(
                    tabsetPanel(
                        id = "tabs",
                        tabPanel("Plot", plotOutput("dist_plot")),
                        tabPanel("Table", verbatimTextOutput("table")),
                        tabPanel("Table DataFrame", verbatimTextOutput("table2")),
                        tabPanel("Table DataTable", dataTableOutput("table3"))
                    )
                )
            )
        ),
        ### REPORTER
        tabPanel(
            "Previewer",
            reporter_previewer_ui("prev")
        )
        ###
    )
)
server <- function(input, output, session) {
    output$encoding <- renderUI({
        tagList(
            ### REPORTER
            teal.reporter::simple_reporter_ui("simple_reporter"),
            ###
            if (input$tabs == "Plot") {
                sliderInput(
                    "binwidth",
                    "binwidth",
                    min = 2,
                    max = 10,
                    value = 8
                )
            } else if (input$tabs %in% c("Table", "Table DataFrame", "Table DataTable")) {
                selectInput(
                    "stat",
                    label = "Statistic",
                    choices = c("mean", "median", "sd"),
                    "mean"
                )
            } else {
                NULL
            }
        )
    })
    plot <- reactive({
        req(input$binwidth)
        x <- mtcars$mpg
        ggplot(data = mtcars, aes(x = mpg)) +
            geom_histogram(binwidth = input$binwidth)
    })
    output$dist_plot <- renderPlot(plot())
    
    table <- reactive({
        req(input$stat)
        lyt <- basic_table() %>%
            split_rows_by("Month", label_pos = "visible") %>%
            analyze("Ozone", afun = eval(str2expression(input$stat)))
        build_table(lyt, airquality)
    })
    output$table <- renderPrint(table())
    
    table2 <- reactive({
        req(input$stat)
        data <- aggregate(
            airquality[, c("Ozone"), drop = FALSE], list(Month = airquality$Month), get(input$stat),
            na.rm = TRUE
        )
        colnames(data) <- c("Month", input$stat)
        data
    })
    output$table2 <- renderPrint(print.data.frame(table2()))
    output$table3 <- renderDataTable(table2())
    
    ### REPORTER
    reporter <- Reporter$new()
    card_fun <- function(card = ReportCard$new(), comment) {
        if (input$tabs == "Plot") {
            card$set_name("Plot Module")
            card$append_text("My plot", "header2")
            card$append_plot(plot())
            card$append_rcode(
                paste(
                    c(
                        "x <- mtcars$mpg",
                        "ggplot2::ggplot(data = mtcars, ggplot2::aes(x = mpg)) +",
                        paste0("ggplot2::geom_histogram(binwidth = ", input$binwidth, ")")
                    ),
                    collapse = "\n"
                ),
                echo = TRUE,
                eval = FALSE
            )
        } else if (input$tabs == "Table") {
            card$set_name("Table Module rtables")
            card$append_text("My rtables", "header2")
            card$append_table(table())
            card$append_rcode(
                paste(
                    c(
                        "lyt <- rtables::basic_table() %>%",
                        'rtables::split_rows_by("Month", label_pos = "visible") %>%',
                        paste0('rtables::analyze("Ozone", afun = ', input$stat, ")"),
                        "rtables::build_table(lyt, airquality)"
                    ),
                    collapse = "\n"
                ),
                echo = TRUE,
                eval = FALSE
            )
        } else if (input$tabs %in% c("Table DataFrame", "Table DataTable")) {
            card$set_name("Table Module DF")
            card$append_text("My Table DF", "header2")
            card$append_table(table2())
            # Here r code added as a regular verbatim text
            card$append_text(
                paste0(
                    c(
                        'data <- aggregate(airquality[, c("Ozone"), drop = FALSE], list(Month = airquality$Month), ',
                        input$stat,
                        ", na.rm = TRUE)\n",
                        'colnames(data) <- c("Month", ', paste0('"', input$stat, '"'), ")\n",
                        "data"
                    ),
                    collapse = ""
                ), "verbatim"
            )
        }
        if (!comment == "") {
            card$append_text("Comment", "header3")
            card$append_text(comment)
        }
        card
    }
    teal.reporter::simple_reporter_srv("simple_reporter", reporter = reporter, card_fun = card_fun)
    teal.reporter::reporter_previewer_srv("prev", reporter)
    ###
}

if (interactive()) shinyApp(ui = ui, server = server)
```

---------

Signed-off-by: Maciej Nasinski <[email protected]>
Co-authored-by: Dawid Kałędkowski <[email protected]>
Co-authored-by: Dawid Kałędkowski <[email protected]>
Signed-off-by: Dawid Kałędkowski <[email protected]>
@gogonzo gogonzo enabled auto-merge (squash) April 24, 2024 08:04
auto-merge was automatically disabled April 24, 2024 08:16

Head branch was pushed to by a user without write access

@gogonzo gogonzo merged commit 93d04af into insightsengineering:main Apr 24, 2024
23 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators Apr 24, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants