Skip to content

Commit

Permalink
First functioning rendering of docx and pdf outputs #5
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin-Jung committed May 11, 2024
1 parent 720a1f1 commit 3afe219
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 35 deletions.
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Imports:
Suggests:
markdown,
rmarkdown,
officer,
spelling,
testthat (>= 3.0.0)
Encoding: UTF-8
Expand Down
2 changes: 1 addition & 1 deletion R/app_ui.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ app_ui <- function(request) {
waiter::spin_balance(), "Loading ..."), color = "#3c8dbc"),
# freshTheme = odpscp_theme(), # Theme designed with fresh
# Other options
dark = FALSE,
dark = NULL,
scrollToTop = TRUE,
fullscreen = FALSE,
help = TRUE, # Default enable tooltips
Expand Down
26 changes: 11 additions & 15 deletions R/mod_Export.R
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,13 @@ mod_Export_server <- function(id, results){

# Check for mandatory outputs and highlight them in text
shiny::observeEvent(input$downloadData, {
# # Check the value of all mandatory fields
# output$missingtext <- shiny::renderText({
# # validate(
# # need(input$sldr > 5,"Require > 5")
# # )
# test
# })
# # Check the value of all mandatory fields
# output$missingtext <- shiny::renderText({
# # validate(
# # need(input$sldr > 5,"Require > 5")
# # )
# test
# })
})

# Get output format
Expand Down Expand Up @@ -179,20 +179,16 @@ mod_Export_server <- function(id, results){
yaml::write_yaml(protocol, file = file)
# yaml::read_yaml("../../../Downloads/test.yaml")
} else if(oftype() == "docx"){
# Create document from results
message("Not yet implemented...")
# Create document from results, everything handled by function
protocol_to_document(results,file = file,format = "docx")
# saveRDS(protocol, "test.rds")
} else if(oftype() == "pdf"){
# Create document from results
message("Not yet implemented...")
protocol_to_document(results,file = file,format = "pdf")
}
}
)

# Render the whole protocol
# output$protocolmarkdown <- renderUI(
# {includeMarkdown(knitr::knit("protocol_preview.Rmd", quiet = T))}
# )

})
}

Expand Down
25 changes: 15 additions & 10 deletions R/mod_Prioritization.R
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ mod_Prioritization_ui <- function(id){
allow users to integrate various features, constraints
and targets in a single prioritization. Some make use of mathematical
optimization, others of heuristics or multi-criteria ranking approaches.
Some of the most commonly used SCP software solutions are listed below."),
Some of the most commonly used SCP software solutions are listed below.
If the software for this study is not found in the list below,
please select 'Other'."),
shinyWidgets::pickerInput(
inputId = ns("software"),
label = "Used software",
Expand Down Expand Up @@ -87,20 +89,23 @@ mod_Prioritization_ui <- function(id){
# Objective functions
shiny::br(),
bs4Dash::box(
title = "Benefit functions",
title = "Outcome identification",
closable = FALSE,
width = 12,
solidHeader = TRUE,
status = "secondary",
collapsible = FALSE,
shiny::p("In many optimizations benefits can accrue in varying ways, for example
through maximizing the targets achieved. If known or specific to the study,
provide information on benefit function used.",
"Benefit functions in prioritizations are for example those minimize marginal
losses from cell removal, minimize an average shortfall or maximize the number
of targets (a constraint) achieved."),
shiny::p("Reference: Arponen, A., Heikkinen, R. K., Thomas, C. D., & Moilanen, A. (2005). The value of biodiversity in reserve selection: representation, species weighting, and benefit functions. Conservation Biology, 19(6), 2009-2014."),
shiny::textAreaInput(inputId = ns("benefitfunctions"), label = "What is being optimized and how?",
shiny::p("In most prioritization exercises the way outcomes are identified is
usually determined by the used algorithm approach (e.g. heuristic, optimization) and
the question that is being asked for the decision variable."),
shiny::p("In this field we record - if known - how the software makes decisions,
e.g. what is being optimized or ranked and how. A typical example is the use
of minimum set problems (in Marxan and others) to identify those areas where
all feature targets are reached while minimizing a cost (area, opportunity cost, ...)"),
shiny::strong("References:"),
shiny::br("Arponen, A., Heikkinen, R. K., Thomas, C. D., & Moilanen, A. (2005). The value of biodiversity in reserve selection: representation, species weighting, and benefit functions. Conservation Biology, 19(6), 2009-2014."),
shiny::br("Schuster, R., Hanson, J. O., Strimas-Mackey, M., & Bennett, J. R. (2020). Exact integer linear programming solvers outperform simulated annealing for solving conservation planning problems. PeerJ, 8, e9258."),
shiny::textAreaInput(inputId = ns("outcomefunctions"), label = "How are prioritization outcomes identified?",
placeholder = 'If known, please provide further detail.',
height = "60px", width = "100%", resize = "vertical")
),
Expand Down
125 changes: 122 additions & 3 deletions R/utils_format_protocol.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
#' A utility function to format a filled out protocal into a different format.
#' Supported options are:
#' * data.frame
#' * html
#'
#' @param results A filled out protocol in [`list`] or reactive format.
#' @param format The output format that the results should have. Available options
#' include \code{"data.frame"} or \code{"list"}.
#' @param path_protocol The filepath to the actual protocol template
#' @param path_protocol The filepath to the actual protocol template.
#' @return The return value, if any, from executing the utility.
#'
#' @noRd
Expand Down Expand Up @@ -69,7 +68,7 @@ format_protocol <- function(results, format = "data.frame",path_protocol = NULL)
#' A helper function that converts a list to a table. Used internally
#' by format_protocol.
#'
#' @param protocol A filled out protocol in [`list`] format.
#' @param path_protocol A filled out protocol in [`list`] format.
#' @return The return value, if any, from executing the utility.
#'
#' @noRd
Expand Down Expand Up @@ -107,3 +106,123 @@ protocol_to_table <- function(path_protocol = NULL){
}
return(results)
}

#' Format a results list to document
#'
#' @description
#' This helper function create a markdown document that is then rendered in
#' different outputs using the \code{"pandoc"} R-package.
#'
#' Most documentation and helper texts are taken from the protocol text with
#' only the results being entered in this sheet.
#'
#' @param results A filled out protocol in [`list`] format.
#' @param file A [`character`] with the output file name.
#' @param format A [`character`] indicating the output format. Either \code{"html"},
#' \code{"docx"} or \code{"pdf"}.
#' @return NULL
#'
#' @noRd
protocol_to_document <- function(results, file, format = "docx", path_protocol = NULL){
assertthat::assert_that(is.character(path_protocol) || is.null(path_protocol))
# Check for other inputs
assertthat::assert_that(is.list(results),
is.character(file))
# Match the format
format <- match.arg(format, c("html", "docx", "pdf"), several.ok = FALSE)
if(tools::file_ext(file) != "docx") file <- paste0(tools::file_path_sans_ext(file), ".", "docx")

# If is null, load protocol
template <- load_protocol(path_protocol)

# Formatted document header
fpar <- officer::fpar(
officer::ftext(text = paste0(template$protocol$name, " Protocol Version: ", template$protocol$version),
prop = officer::fp_text(font.size = 24,bold = TRUE))
)

# Create basic file
doc <- officer::read_docx() |>
officer::body_add_fpar(value = fpar) |>
officer::body_add_fpar(value = officer::fpar( officer::ftext(text = paste0("Created through ", template$protocol$repository),
prop = officer::fp_text(font.size = 14,bold = FALSE)) )) |>
officer::body_add_fpar(value = officer::fpar( officer::ftext(text = paste0("Generated on ", Sys.Date() ),
prop = officer::fp_text(font.size = 14,bold = FALSE)))) |>
officer::body_add_par("Protocol content", style = "heading 1") |>
officer::body_add_toc(level = 2) |>
officer::body_add_break()

# Now per group and element add to output
for(g in names(results)){ # g = names(results)[1]

# Add a new header for group
doc <- doc |> officer::body_add_par(value = tools::toTitleCase(g), style = "heading 1")

for(el in names(results[[g]])){ # el = names(results[[g]])[1]

# Get the name and description of the target element
w <- get_protocol_elementgroup(el)
if(is.null(w)) next() # Skip?
sub <- template[[w[['group']]]][[w[['element']]]]

# --- #
## Now add to output specifically
# The question
fpar <- officer::fpar(
officer::ftext(text = paste0(sub$question),
prop = officer::fp_text(font.size = 14, bold = TRUE))
)
doc <- doc |> officer::body_add_fpar(value = fpar)
# A description there in
fpar <- officer::fpar(
officer::ftext(text = paste0(sub$description),
prop = officer::fp_text(font.size = 10,italic = TRUE))
)
doc <- doc |> officer::body_add_fpar(value = fpar)

# Small linebreak
doc <- doc |> officer::body_add_par(value = "", style = "Normal")

# --- #
# Parse the result
res <- results[[g]][[el]]
if(is.list(res)) {
if(length(res)>0){
# Tables
ft <- flextable::flextable(res) |>
flextable::set_table_properties(layout = "autofit")
doc <- doc |> officer::body_add_flextable(value = ft)
} else {
res <- "Not specified"
}
}
# If multiple entries, paste together via -
if(length(res)>1) res <- paste(res, collapse = " - ")

if(is.logical(res)) res <- ifelse(res, "Yes", "No")
if(is.na(res)) res <- "Not specified"

fpar <- officer::fpar(
officer::ftext(text = res,
prop = officer::fp_text(font.size = 12,italic = FALSE))
)
doc <- doc |> officer::body_add_fpar(value = fpar)

# Small linebreak
doc <- doc |> officer::body_add_par(value = "", style = "Normal")

}
}

# Generate output to file
doc |> print(target = file)

# Transform the output to different type depending on setting
if(format != "docx"){
rmarkdown::pandoc_convert(
input = file,
to = format,
output = paste0(tools::file_path_sans_ext(file), ".", format)
)
}
}
10 changes: 4 additions & 6 deletions inst/01_protocol.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -514,8 +514,6 @@ specification:
popexample: "A description where or how the feature layers came about, if they are
the result of some modelling process or similar."



#### Context ####
context:
decisionvariable:
Expand Down Expand Up @@ -655,10 +653,10 @@ prioritization:

benefits:
render-nr: 3
render-id: 'benefitfunctions'
render-id: 'outcomefunctions'
render-group: 'study_software'
question: 'What is being optimized and how?'
description: 'Was a specific benefit function being used the optimization, or are there any specifics on how benefits were identified?'
question: 'What is being identified and how?'
description: 'Was a specific benefit or objectiv function being used in the prioritization, or are there any specifics on how outcomes were identified?'
fieldtype: 'textbox'
mandatory: false
popexample: "Minimize the marginal losses from cell removal, an average shortfall or maximize the number of targets (a constraint) achieved."
Expand Down Expand Up @@ -690,7 +688,7 @@ prioritization:
fieldtype_conditional_render-id: 'otheridentification'
fieldtype_conditional: 'textbox'
mandatory: true
popexample: "The final priorities are identified by overlaying the optimization results with priority areas identified by stakeholders."
popexample: "The final priorities are identified by overlaying individual optimization results with priority areas identified by stakeholders."

performance:
render-nr: 1
Expand Down
8 changes: 8 additions & 0 deletions inst/glossary_table.csv
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,11 @@ Comprehensiveness,,Defined as a set of priority areas that include at a minimum
Cost-efficiency,,"A cost-efficient network of priority areas is one that is comprehensive, representative, and/or adequate for the least possible cost. Cost-efficiency is important because it facilitates future expansion of a network of priority areas through the prudent use of conservation resources and is more likely to be defensible in light of competing interests","Wilson, K.A., Cabeza, M. and Klein, C.J., 2009. Fundamental concepts of spatial conservation prioritization. Spatial conservation prioritization: Quantitative methods and computational tools, pp.16-27."
Irreplaceability,,Measures the value of the inclusion of a planning unit in a solution relative to specified targets and other constraints.,"Wilson, K.A., Cabeza, M. and Klein, C.J., 2009. Fundamental concepts of spatial conservation prioritization. Spatial conservation prioritization: Quantitative methods and computational tools, pp.16-27."
Constraint,,A constraint placed on whether or to what extent a given area can be selected as priority.,Authors
Pressure,,A human activity that immediately degrades one or more biodiversity focal values.,"Salafsky et al. (2024) A standard lexicon of terms for area-based conservation version 1.0, Conservation Biology, https://doi.org/10.1111/cobi.14269"
Theory of Change,,The explicit or implicit set of assumptions that outlines how a given action will lead to desired intermediate and ultimate outcomes.,"Salafsky et al. (2024) A standard lexicon of terms for area-based conservation version 1.0, Conservation Biology, https://doi.org/10.1111/cobi.14269"
Indicator,,"A measure of a given variable, often used to assess progress toward the target in a goal or objective or the achievement of a criterion in an effectiveness framework. Can be the result of a modelling process. In the context of SCP often used for evaluating solutions in terms of their efficiency.","Adapted from Salafsky et al. (2024) A standard lexicon of terms for area-based conservation version 1.0, Conservation Biology, https://doi.org/10.1111/cobi.14269"
Gap analysis,,The process of evaluating an existing protected area network or management scheme in terms of its value in conserving biodiversity or other features.,Authors
Zones,z,"A zone specifies the extent over which a decision is to be applied. Most SCP software make use of only one zone, however some allow multiple zones and are able to account accounting for the relationships between them.","Authors, https://marxansolutions.org"
Decision variable,x,A variable in a problem formulation used for some SCP applications and typically determining the output allocation (e.g. binary allocation of potential protected area).,Authors
Objective function,,In the context of decision theory and mathematical optimization a specific function (usually minimize or maximize) that determines the outcome of the decision variable subject to all constraints.,Authors
Benefit function,,"Usually used for (meta-)heuristic algorithms that estimate the benefit, through a specific step, linear or non-linear function, of carrying out a given action with an increasing amount of the decision variable selected.","Arponen, A., Heikkinen, R. K., Thomas, C. D., & Moilanen, A. (2005). The value of biodiversity in reserve selection: representation, species weighting, and benefit functions. Conservation Biology, 19(6), 2009-2014."

0 comments on commit 3afe219

Please sign in to comment.