Repository: prisma-flowdiagram/PRISMA2020 Branch: main Commit: c6f9cac1dc21 Files: 48 Total size: 2.2 MB Directory structure: gitextract_rzi0l72b/ ├── .Rbuildignore ├── .github/ │ ├── .gitignore │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── feature_request.md │ └── workflows/ │ ├── build-and-check-CRAN.yml │ ├── check-and-deploy.yml │ └── docker-build-push.yml ├── .gitignore ├── CITATION.cff ├── DESCRIPTION ├── Dockerfile ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── PRISMA2020.Rproj ├── R/ │ ├── PRISMA_flowdiagram.R │ ├── globals.R │ └── utils.R ├── README.md ├── code-of-conduct.md ├── contributing.md ├── inst/ │ ├── CITATION │ ├── extdata/ │ │ ├── PRISMA.csv │ │ ├── Template.html │ │ └── citation.ris │ └── shiny-examples/ │ └── PRISMA_flowdiagram/ │ ├── app.R │ ├── rsconnect/ │ │ └── shinyapps.io/ │ │ └── estech/ │ │ └── prisma_flowdiagram.dcf │ └── www/ │ ├── Haddaway_et_al_2022.ris │ ├── PRISMA.csv │ ├── kofi.js │ └── labels.js ├── man/ │ ├── PRISMA_add_hyperlink_.Rd │ ├── PRISMA_calc_filetype_.Rd │ ├── PRISMA_data.Rd │ ├── PRISMA_flowdiagram.Rd │ ├── PRISMA_format_number_.Rd │ ├── PRISMA_format_reasons_.Rd │ ├── PRISMA_gen_tmp_svg_.Rd │ ├── PRISMA_get_height_.Rd │ ├── PRISMA_get_pos_.Rd │ ├── PRISMA_insert_js_.Rd │ ├── PRISMA_interactive_.Rd │ ├── PRISMA_parse_reasons_.Rd │ ├── PRISMA_save.Rd │ ├── read_PRISMAdata.Rd │ └── sr_flow_interactive.Rd └── shiny-server.conf ================================================ FILE CONTENTS ================================================ ================================================ FILE: .Rbuildignore ================================================ ^.*\.Rproj$ ^\.Rproj\.user$ ^\.github$ .png inst/shiny-examples LICENSE.md CITATION.cff code-of-conduct.md contributing.md Dockerfile shiny-server.conf ================================================ FILE: .github/.gitignore ================================================ *.html ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms open_collective: esmarconf ko_fi: chriscpritchard ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - OS: [e.g. iOS] - Browser [e.g. chrome, safari] - Version [e.g. 22] **Smartphone (please complete the following information):** - Device: [e.g. iPhone6] - OS: [e.g. iOS8.1] - Browser [e.g. stock browser, safari] - Version [e.g. 22] **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/workflows/build-and-check-CRAN.yml ================================================ on: push: tags: - 'v*' name: Publish Release, Build & Check for CRAN jobs: R-CMD-Build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: r-lib/actions/setup-r@v2 with: r-version: 'release' use-public-rspm: true - uses: r-lib/actions/setup-pandoc@v2 - uses: r-lib/actions/setup-tinytex@v2 - uses: r-lib/actions/setup-r-dependencies@v2 with: extra-packages: | devtools rcmdcheck - name: Build run: R CMD build . - uses: actions/upload-artifact@v3 with: name: package path: "*.tar.gz" R-CMD-Check-As-CRAN: runs-on: ubuntu-latest needs: R-CMD-Build steps: - uses: actions/checkout@v3 - uses: r-lib/actions/setup-r@v2 with: r-version: 'devel' use-public-rspm: true - uses: r-lib/actions/setup-pandoc@v2 - uses: r-lib/actions/setup-tinytex@v2 - uses: r-lib/actions/setup-r-dependencies@v2 with: extra-packages: | devtools rcmdcheck - uses: actions/download-artifact@v3 with: name: package - name: Check run: R CMD check --as-cran *.tar.gz - uses: actions/upload-artifact@v3 with: name: check path: "*.Rcheck" release: runs-on: ubuntu-latest needs: R-CMD-Check-As-CRAN permissions: contents: write steps: - uses: actions/checkout@v3 - uses: actions/download-artifact@v3 - uses: ncipollo/release-action@v1 with: artifacts: "**/*.tar.gz,**/*.Rcheck/*.log,**/*.Rcheck/*-manual.pdf" deploy-release: needs: release uses: prisma-flowdiagram/r-workflows/.github/workflows/deploy.yml@v1 with: install-package: true rsconnect-account-name: "estech" rsconnect-app-name: "PRISMA_flowdiagram" working-directory: "./inst/shiny-examples/PRISMA_flowdiagram" environment: "release" renvironment-contents: | PRISMA_ANALYTICS=TRUE KOFI_DONATE=TRUE secrets: rsconnect-account-token: ${{ secrets.TOKEN }} rsconnect-account-secret: ${{ secrets.SECRET }} ================================================ FILE: .github/workflows/check-and-deploy.yml ================================================ on: release: types: [released] pull_request: types: [opened, synchronize, reopened] issue_comment: types: [created] push: branches: - main - master name: Check & Deploy jobs: R-CMD-Check: if: (github.event_name != 'issue_comment') uses: prisma-flowdiagram/r-workflows/.github/workflows/R-CMD-check.yml@v1 pkgdown: needs: R-CMD-check if: (github.event_name == 'push') uses: prisma-flowdiagram/r-workflows/.github/workflows/pkgdown.yml@v1 deploy-latest: needs: pkgdown if: (github.event_name == 'push') uses: prisma-flowdiagram/r-workflows/.github/workflows/deploy.yml@v1 with: install-package: true rsconnect-account-name: "estech" rsconnect-app-name: "PRISMA_flowdiagram_latest" working-directory: "./inst/shiny-examples/PRISMA_flowdiagram" environment: "latest" renvironment-contents: | PRISMA_ANALYTICS=TRUE KOFI_DONATE=TRUE secrets: rsconnect-account-token: ${{ secrets.TOKEN }} rsconnect-account-secret: ${{ secrets.SECRET }} check-pr-deploy: runs-on: ubuntu-latest if: github.event_name == 'issue_comment' outputs: triggered: ${{ steps.check-pr.outputs.triggered }} steps: - uses: khan/pull-request-comment-trigger@v1.1.0 id: check-pr with: trigger: '!deploy' reaction: rocket env: GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' get-pull-ref: runs-on: ubuntu-latest needs: check-pr-deploy if: (github.event_name == 'issue_comment' && github.event.issue.pull_request) outputs: repository: ${{ fromJson(steps.get-repo.outputs.data).head.repo.full_name }} ref: ${{ fromJson(steps.get-repo.outputs.data).head.ref }} steps: - name: get-pull-request-ref id: get-repo uses: octokit/request-action@v2.x with: route: GET /repos/:repository/pulls/:issue_id repository: ${{ github.repository }} issue_id: ${{ github.event.issue.number }} env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" R-CMD-check-PR: needs: [check-pr-deploy,get-pull-ref] if: needs.check-pr-deploy.outputs.triggered == 'true' uses: prisma-flowdiagram/r-workflows/.github/workflows/R-CMD-check.yml@v1 with: repository: ${{ needs.get-pull-ref.outputs.repository || github.repository }} ref: ${{ needs.get-pull-ref.outputs.ref || github.ref }} deploy-pr-comment: needs: [get-pull-ref,R-CMD-check-PR] uses: prisma-flowdiagram/r-workflows/.github/workflows/deploy.yml@v1 with: repository: ${{ needs.get-pull-ref.outputs.repository || github.repository }} ref: ${{ needs.get-pull-ref.outputs.ref || github.ref }} install-package: true rsconnect-account-name: "prismaflow-test" rsconnect-app-name: "PRISMA_flowdiagram" working-directory: "./inst/shiny-examples/PRISMA_flowdiagram" environment: "pr" secrets: rsconnect-account-token: ${{ secrets.TOKEN }} rsconnect-account-secret: ${{ secrets.SECRET }} ================================================ FILE: .github/workflows/docker-build-push.yml ================================================ name: Docker on: push: branches: - master tags: - v* env: IMAGE_NAME: prisma-flowdiagram/prisma-flowdiagram-shinyapp jobs: docker: runs-on: ubuntu-latest if: github.event_name == 'push' steps: - name: Checkout uses: actions/checkout@v2 - name: Determine target image name id: image-name run: | IMAGE_ID=ghcr.io/$IMAGE_NAME # Strip git ref prefix from version VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') # Strip "v" prefix from tag name [[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//') # Use Docker `latest` tag convention [ "$VERSION" == "master" ] && VERSION=latest IMAGES= IMAGES="$IMAGE_ID:$VERSION"$'\n'$IMAGES # debug output echo images $IMAGES echo ::set-output name=images::"$IMAGES" - name: Set up QEMU uses: docker/setup-qemu-action@v1 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - name: Login to registry uses: docker/login-action@v1 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push uses: docker/build-push-action@v2 with: context: . platforms: linux/amd64,linux/arm64,linux/arm/v7 push: true tags: ${{ steps.image-name.outputs.images }} ================================================ FILE: .gitignore ================================================ .Rproj.user .Rhistory .RData .Ruserdata .vscode/launch.json **/.DS_Store ================================================ FILE: CITATION.cff ================================================ cff-version: 1.2.0 message: "If you use this software, please cite it as below." authors: - family-names: Haddaway given-names: Neal orcid: https://orcid.org/0000-0003-3902-2234 - family-names: McGuinness given-names: Luke A orcid: https://orcid.org/0000-0001-8730-9761 - family-names: Pritchard given-names: Chris C orcid: https://orcid.org/0000-0002-1143-9751 title: "PRISMA2020: R package and ShinyApp for producing PRISMA 2020 compliant flow diagrams" version: 1.1.0 doi: 10.5281/zenodo.4287834 date-released: 2023-02-06 license: MIT url: "https://github.com/prisma-flowdiagram/PRISMA2020" preferred-citation: type: article authors: - family-names: Haddaway given-names: Neal orcid: https://orcid.org/0000-0003-3902-2234 - family-names: Page given-names: Matthew J orcid: https://orcid.org/0000-0002-4242-7526 - family-names: Pritchard given-names: Chris C orcid: https://orcid.org/0000-0002-1143-9751 - family-names: McGuinness given-names: Luke A orcid: https://orcid.org/0000-0001-8730-9761 doi: "10.1002/cl2.1230" journal: "Campbell Systematic Reviews" title: "PRISMA2020: an R package and Shiny app for producing PRISMA 2020-compliant flow diagrams, with interactivity for optimised digital transparency and Open Synthesis" year: 2022 ================================================ FILE: DESCRIPTION ================================================ Package: PRISMA2020 Title: Make Interactive 'PRISMA' Flow Diagrams Version: 1.1.2 Authors@R: c( person(given = "Neal", family = "Haddaway", role = "aut", email = "nealhaddaway@gmail.com", comment = c(ORCID = "0000-0003-3902-2234")), person("Luke", "McGuinness", role = "aut", email = "luke.mcguinness@bristol.ac.uk", comment = c(ORCID = "0000-0001-8730-9761")), person("Chris", "Pritchard", role = c("aut", "cre"), email = "chris.pritchard@ntu.ac.uk", comment = c(ORCID = "0000-0002-1143-9751")), person("Brennan", "Chapman", role = "ctb"), person("Hossam", "Hammady", role= "ctb")) Description: Systematic reviews should be described in a high degree of methodological detail. The 'PRISMA' Statement calls for a high level of reporting detail in systematic reviews and meta-analyses. An integral part of the methodological description of a review is a flow diagram. This package produces an interactive flow diagram that conforms to the 'PRISMA2020' preprint. When made interactive, the reader/user can click on each box and be directed to another website or file online (e.g. a detailed description of the screening methods, or a list of excluded full texts), with a mouse-over tool tip that describes the information linked to in more detail. Interactive versions can be saved as HTML files, whilst static versions for inclusion in manuscripts can be saved as HTML, PDF, PNG, SVG, PS or WEBP files. Imports: DiagrammeR, DiagrammeRsvg, htmltools, htmlwidgets, rsvg, scales, shiny, shinyjs, stats, stringr, utils, xml2, webp, DT, rio, tools, zip License: MIT + file LICENSE URL: https://github.com/prisma-flowdiagram/PRISMA2020 BugReports: https://github.com/prisma-flowdiagram/PRISMA2020/issues Encoding: UTF-8 Roxygen: list(markdown = TRUE) RoxygenNote: 7.3.2 ================================================ FILE: Dockerfile ================================================ FROM rocker/shiny:latest LABEL maintainer="Hossam Hammady " # Install system dependencies RUN apt-get update -qq && \ apt-get install -y \ libssl-dev \ libfontconfig1-dev \ libcurl4-openssl-dev \ libxml2-dev \ libharfbuzz-dev \ libfribidi-dev \ libgit2-dev \ libfreetype6-dev \ libpng-dev \ libtiff-dev \ libv8-dev \ librsvg2-dev \ libwebp-dev \ pandoc # Install R packages RUN Rscript -e 'install.packages(c("DiagrammeR", "DiagrammeRsvg", "htmltools", "htmlwidgets", "scales", "shiny", "shinyjs", "stringr", "xml2", "DT", "rio", "zip", "rsvg", "webp", "devtools"))' # Copy the shiny server configuration COPY shiny-server.conf /etc/shiny-server/shiny-server.conf # Install the PRISMA2020 package COPY . /tmp/app RUN R CMD INSTALL /tmp/app # Copy the shiny app files RUN mkdir -p /srv/shiny-server/prisma/app && \ echo "OK" > /srv/shiny-server/prisma/healthz.txt COPY inst/shiny-examples/PRISMA_flowdiagram/. /srv/shiny-server/prisma/app/ # Clean up downloaded files RUN rm -rf /tmp/*/downloaded_packages /tmp/app ================================================ FILE: LICENSE ================================================ YEAR: 2020 COPYRIGHT HOLDER: Neal R Haddaway ================================================ FILE: LICENSE.md ================================================ Copyright 2020 Neal R Haddaway Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: NAMESPACE ================================================ # Generated by roxygen2: do not edit by hand export(PRISMA_data) export(PRISMA_flowdiagram) export(PRISMA_save) importFrom(DT,addRow) importFrom(rio,import) importFrom(shiny,column) importFrom(shinyjs,alert) importFrom(stats,median) importFrom(utils,apropos) importFrom(webp,read_webp) ================================================ FILE: PRISMA2020.Rproj ================================================ Version: 1.0 RestoreWorkspace: Default SaveWorkspace: Default AlwaysSaveHistory: Default EnableCodeIndexing: Yes UseSpacesForTab: Yes NumSpacesForTab: 2 Encoding: UTF-8 RnwWeave: Sweave LaTeX: pdfLaTeX BuildType: Package PackageUseDevtools: Yes PackageInstallArgs: --no-multiarch --with-keep.source PackageRoxygenize: rd,collate,namespace ================================================ FILE: R/PRISMA_flowdiagram.R ================================================ # Suppress R CMD check note #' @importFrom DT addRow #' @importFrom rio import #' @importFrom shiny column #' @importFrom shinyjs alert #' @importFrom stats median #' @importFrom utils apropos #' @importFrom webp read_webp NULL #' Plot interactive flow diagrams for systematic reviews #' @description Produces a PRISMA2020 style flow diagram #' for systematic reviews, with the option to add #' interactivity through tooltips (mouseover popups) and #' hyperlink URLs to each box. #' Data can be imported from the standard CSV template provided. #' @param data List of data inputs including numbers of studies, #' box text, tooltips, and urls for hyperlinks. #' Data inputted via the [PRISMA_data()] function. #' If inputting individually, see the necessary parameters #' listed in the [PRISMA_data()]) function and #' combine them in a list using `data <- list()`. #' @param interactive Logical argument TRUE or FALSE #' whether to plot interactivity (tooltips and hyperlinked boxes). #' @param previous Logical argument (TRUE or FALSE) specifying whether previous #' studies were sought. #' @param other Logical argument (TRUE or FALSE) specifying whether #' other studies were sought. #' @param detail_databases Logical argument (TRUE or FALSE) specifying whether #' to list specific databases. #' @param detail_registers Logical argument (TRUE or FALSE) specifying whether #' to list specific registers. #' @param meta_analysis Logical argument (TRUE or FALSE) specifying whether to #' display a box for meta-analysis #' @param font The font for text in each box. The default is 'Helvetica'. #' @param fontsize The font size for text in each box. The default is '12'. #' @param title_colour The colour for the upper middle title box (new studies). #' The default is 'Goldenrod1'. See 'DiagrammeR' colour scheme. #' . #' @param greybox_colour The colour for the left and right column boxes. The #' default is 'Gainsboro'. See 'DiagrammeR' colour scheme #' . #' @param main_colour The colour for the main box borders. The default is #' 'Black'. See 'DiagrammeR' colour scheme #' . #' @param arrow_colour The colour for the connecting lines. The default #' is 'Black'. See 'DiagrammeR' colour scheme #' . #' @param arrow_head The head shape for the line connectors. The default is #' 'normal'. See DiagrammeR arrow shape specification #' . #nolint #' @param arrow_tail The tail shape for the line connectors. The default is #' 'none'. See DiagrammeR arrow shape specification #' . #nolint #' @param side_boxes Whether or not to include the #' blue label boxes along the side #' @return A flow diagram plot. #' @examples #' csvFile <- system.file("extdata", "PRISMA.csv", package = "PRISMA2020") #' data <- read.csv(csvFile); #' data <- PRISMA_data(data); #' plot <- PRISMA_flowdiagram(data, #' fontsize = 12, #' interactive = TRUE, #' previous = FALSE, #' other = TRUE); #' plot #' @export PRISMA_flowdiagram <- function( #nolint data, interactive = FALSE, previous = TRUE, other = TRUE, detail_databases = FALSE, detail_registers = FALSE, meta_analysis = FALSE, fontsize = 7, font = "Helvetica", title_colour = "Goldenrod1", greybox_colour = "Gainsboro", main_colour = "Black", arrow_colour = "Black", arrow_head = "normal", arrow_tail = "none", side_boxes = TRUE) { # removes the need to attach() the data # https://stackoverflow.com/a/11505637 for (var in seq_len(length(data))) { assign(names(data)[var], data[[var]]) } # positional attributes, in inches diagram_start_x <- 0 diagram_start_y <- 0 prev_study_width <- 0 prev_study_offset <- 0 prev_study_height <- 0 total_studies_height <- 0 total_ma_height <- 0 other_identified_height <- 0 other_sought_reports_height <- 0 other_notretrieved_height <- 0 other_assessed_height <- 0 other_excluded_height <- 0 default_box_width <- 3.5 min_box_height <- 0.5 default_box_spacing <- 0.5 section_label_length <- 0.4 top_box_width <- default_box_width * 2 + default_box_spacing A <- "" #nolint Aedge <- "" #nolint bottomedge <- "" previous_nodes <- "" finalnode <- "" metanode <- "" prev_rank1 <- "" prevnode1 <- "" prevnode2 <- "" #wrap exclusion reasons dbr_excluded[, 1] <- stringr::str_wrap( dbr_excluded[, 1], width = 35 ) other_excluded[, 1] <- stringr::str_wrap( other_excluded[, 1], width = 35 ) # wrap specific database registers database_specific_results[, 1] <- stringr::str_wrap( database_specific_results[, 1], width = 35 ) register_specific_results[, 1] <- stringr::str_wrap( register_specific_results[, 1], width = 35 ) #remove previous box if both values are zero if (is.na(previous_studies) == TRUE && is.na(previous_reports) == TRUE) { previous <- FALSE } if (previous == TRUE) { #conditional studies and reports - empty text if blank if (is.na(previous_studies) == TRUE) { cond_prevstud <- "" } else { cond_prevstud <- stringr::str_wrap( paste0( previous_studies_text, " (n = ", previous_studies, ")" ), width = 40) } if (is.na(previous_reports) == TRUE) { cond_prevrep <- "" } else { cond_prevrep <- paste0( stringr::str_wrap( previous_reports_text, width = 40 ), "\n(n = ", previous_reports, ")" ) } if (is.na(previous_studies) == TRUE || is.na(previous_reports) == TRUE) { dbl_br <- "" } else { dbl_br <- "\n" } prev_study_label <- paste0(cond_prevstud, dbl_br, cond_prevrep) total_studies_label <- paste0( stringr::str_wrap( paste0( total_studies_text, " (n = ", total_studies, ")" ), width = 33 ), "\n", stringr::str_wrap( paste0( total_reports_text, " (n = ", total_reports, ")" ), width = 33 ) ) # we multiply by 2 because we need the width # from the start, not just the centre prev_study_width <- default_box_width * 2 prev_study_offset <- default_box_spacing prev_box_x <- PRISMA_get_pos_( diagram_start_x, default_box_spacing, default_box_width, prev_study_offset ) prev_study_height <- PRISMA_get_height_( stringr::str_count(prev_study_label, "\n"), min_box_height ) total_studies_height <- PRISMA_get_height_( stringr::str_count(total_studies_label, "\n"), min_box_height ) } # conditionals for the node labels if ( is.na(website_results) == TRUE && is.na(organisation_results) == TRUE && is.na(citations_results) == TRUE ) { other <- FALSE } if (other == TRUE) { # conditionals for the node labels if (is.na(website_results) == FALSE) { cond_websites <- paste0( "\n", website_results_text, " (n = ", website_results, ")" ) } else { cond_websites <- "" } if (is.na(organisation_results) == FALSE) { cond_organisation <- paste0( "\n", organisation_results_text, " (n = ", organisation_results, ")" ) } else { cond_organisation <- "" } if (is.na(citations_results) == FALSE) { cond_citation <- paste0( "\n", citations_results_text, " (n = ", citations_results, ")" ) } else { cond_citation <- "" } other_excluded_data <- PRISMA_format_reasons_(other_excluded) #nolint # labels other_identified_label <- paste0( "Records identified from:", cond_websites, cond_organisation, cond_citation ) other_sought_reports_label <- paste0( other_sought_reports_text, "\n(n = ", other_sought_reports, ")" ) other_notretrieved_label <- paste0( other_notretrieved_reports_text, "\n(n = ", other_notretrieved_reports, ")" ) other_assessed_label <- paste0( other_assessed_text, "\n(n = ", other_assessed, ")" ) other_excluded_label <- paste0( other_excluded_text, other_excluded_data ) # heights other_identified_height <- PRISMA_get_height_( stringr::str_count(other_identified_label, "\n"), min_box_height ) other_sought_reports_height <- PRISMA_get_height_( stringr::str_count(other_sought_reports_label, "\n"), min_box_height ) other_notretrieved_height <- PRISMA_get_height_( stringr::str_count(other_notretrieved_label, "\n"), min_box_height ) other_assessed_height <- PRISMA_get_height_( stringr::str_count(other_assessed_label, "\n"), min_box_height ) other_excluded_height <- PRISMA_get_height_( stringr::str_count(other_excluded_label, "\n"), min_box_height ) } if (is.na(new_studies) == FALSE) { cond_newstud <- paste0( stringr::str_wrap(new_studies_text, width = 40), "\n(n = ", new_studies, ")\n" ) } else { cond_newstud <- "" } if (is.na(new_reports) == FALSE) { cond_newreports <- paste0( stringr::str_wrap(new_reports_text, width = 40), "\n(n = ", new_reports, ")" ) } else { cond_newreports <- "" } if (is.na(total_studies_ma) == FALSE) { cond_total_studies_ma <- paste0( stringr::str_wrap( paste0( total_studies_ma_text, " (n = ", total_studies_ma, ")" ), width = 33 ), "\n" ) } else { cond_total_studies_ma <- "" } if (is.na(total_reports_ma) == FALSE) { cond_total_reports_ma <- stringr::str_wrap( paste0( total_reports_ma_text, " (n = ", total_reports_ma, ")" ), width = 33 ) } else { cond_total_reports_ma <- "" } if (detail_databases == TRUE) { db_specific_data <- PRISMA_format_reasons_(database_specific_results) #nolint } else { db_specific_data <- "" db_specific_data_nr <- "" } if (detail_registers == TRUE) { reg_specific_data <- PRISMA_format_reasons_(register_specific_results) #nolint } else { reg_specific_data <- "" reg_specific_data_nr <- "" } if (is.na(database_results) == FALSE) { cond_database <- paste0( "\n", database_results_text, " (n = ", database_results, ")", db_specific_data) } else { cond_database <- paste0("", db_specific_data_nr) } if (is.na(register_results) == FALSE) { cond_register <- paste0( "\n", register_results_text, " (n = ", register_results, ")", reg_specific_data) } else { cond_register <- paste0("", reg_specific_data_nr) } dbr_excluded_data <- PRISMA_format_reasons_(dbr_excluded) #nolint if (is.na(duplicates) == FALSE) { cond_duplicates <- paste0( stringr::str_wrap( paste0( duplicates_text, " (n = ", duplicates, ")" ), width = 42 ), "\n" ) } else { cond_duplicates <- "" } if (is.na(excluded_automatic) == FALSE) { cond_automatic <- paste0( stringr::str_wrap( paste0( excluded_automatic_text, " (n = ", excluded_automatic, ")" ), width = 42 ), "\n" ) } else { cond_automatic <- "" } if (is.na(excluded_other) == FALSE) { cond_exclother <- paste0( stringr::str_wrap( paste0( excluded_other_text, " (n = ", excluded_other, ")" ), width = 42 ) ) } else { cond_exclother <- "" } if ( is.na(duplicates) == TRUE && is.na(excluded_automatic) == TRUE && is.na(excluded_other) == TRUE ) { cond_duplicates <- "(n = 0)" } # labels for the nodes newstudy_newreports_label <- paste0(cond_newstud, cond_newreports) total_ma_label <- paste0(cond_total_studies_ma, cond_total_reports_ma) dbr_assessed_label <- paste0( dbr_assessed_text, "\n(n = ", dbr_assessed, ")" ) dbr_sought_label <- paste0( dbr_sought_reports_text, "\n(n = ", dbr_sought_reports, ")" ) dbr_screened_label <- paste0( records_screened_text, "\n(n = ", records_screened, ")" ) dbr_identified_label <- paste0( "Records identified from:", cond_database, cond_register ) dbr_excluded_label <- paste0( dbr_excluded_text, dbr_excluded_data ) dbr_notretrieved_label <- paste0( dbr_notretrieved_reports_text, "\n(n = ", dbr_notretrieved_reports, ")" ) dbr_screened_excluded_label <- paste0( records_excluded_text, "\n(n = ", records_excluded, ")" ) dbr_notscreened_label <- paste0( "Records removed before screening:\n", cond_duplicates, cond_automatic, cond_exclother ) # we set the height of various nodes here newstudy_newreports_height <- PRISMA_get_height_( stringr::str_count(newstudy_newreports_label, "\n"), min_box_height ) if (is.na(total_reports_ma) == TRUE && is.na(total_studies_ma) == TRUE) { meta_analysis <- FALSE } if (meta_analysis == TRUE) { total_ma_height <- PRISMA_get_height_( stringr::str_count(total_ma_label, "\n"), min_box_height ) } dbr_assessed_height <- PRISMA_get_height_( stringr::str_count(dbr_assessed_label, "\n"), min_box_height ) dbr_sought_height <- PRISMA_get_height_( stringr::str_count(dbr_sought_label, "\n"), min_box_height ) dbr_screened_height <- PRISMA_get_height_( stringr::str_count(dbr_screened_label, "\n"), min_box_height ) dbr_identified_height <- PRISMA_get_height_( stringr::str_count(dbr_identified_label, "\n"), min_box_height ) dbr_excluded_height <- PRISMA_get_height_( stringr::str_count(dbr_excluded_label, "\n"), min_box_height ) dbr_notretrieved_height <- PRISMA_get_height_( stringr::str_count(dbr_notretrieved_label, "\n"), min_box_height ) dbr_screened_excluded_height <- PRISMA_get_height_( stringr::str_count(dbr_screened_excluded_label, "\n"), min_box_height ) dbr_notscreened_height <- PRISMA_get_height_( stringr::str_count(dbr_notscreened_label, "\n"), min_box_height ) screening_box_height <- max( c( dbr_screened_height, dbr_screened_excluded_height ) ) + max( c( dbr_notretrieved_height, dbr_sought_height, other_sought_reports_height, other_notretrieved_height ) ) + max( c( dbr_assessed_height, dbr_excluded_height, other_assessed_height, other_excluded_height ) ) + default_box_spacing * 2 identification_box_height <- max( c( dbr_identified_height, dbr_notscreened_height, prev_study_height, other_identified_height ) ) + default_box_spacing included_box_height <- newstudy_newreports_height + total_studies_height + total_ma_height + default_box_spacing * 3 assessed_height <- max( c( dbr_assessed_height, other_assessed_height, dbr_excluded_height, other_excluded_height ) ) sought_height <- max( c( dbr_sought_height, other_sought_reports_height, dbr_notretrieved_height, other_notretrieved_height ) ) screened_height <- max( c( dbr_screened_height, dbr_screened_excluded_height ) ) identified_height <- max( c( dbr_identified_height, dbr_notscreened_height, other_identified_height, prev_study_height ) ) warning(included_box_height) # here we set the x and y co-ordinates for new studies # (other items depend on this being created) dbr_box_x <- PRISMA_get_pos_( diagram_start_x, prev_study_offset + default_box_spacing, prev_study_width, default_box_width ) dbr_removed_x <- PRISMA_get_pos_( dbr_box_x, default_box_spacing, default_box_width, default_box_width ) newstudy_newreports_y <- PRISMA_get_pos_( diagram_start_y, default_box_spacing, total_studies_height, newstudy_newreports_height ) if (previous == TRUE) { total_ma_y <- PRISMA_get_pos_( diagram_start_y, default_box_spacing, total_studies_height, total_ma_height, negative_offset = TRUE ) } else { total_ma_y <- PRISMA_get_pos_( newstudy_newreports_y, default_box_spacing, newstudy_newreports_height, total_ma_height, negative_offset = TRUE ) } assessed_y <- PRISMA_get_pos_( newstudy_newreports_y, default_box_spacing * 2, newstudy_newreports_height, assessed_height ) sought_y <- PRISMA_get_pos_( assessed_y, default_box_spacing, assessed_height, sought_height ) screened_y <- PRISMA_get_pos_( sought_y, default_box_spacing, sought_height, screened_height ) identified_y <- PRISMA_get_pos_( screened_y, default_box_spacing * 2, screened_height, identified_height ) top_box_y <- PRISMA_get_pos_( identified_y, default_box_spacing, identified_height, section_label_length ) screening_y <- mean( c( (screened_y + (screened_height / 2)), (assessed_y - (dbr_excluded_height / 2)) ) ) included_y <- if (total_studies_height > 0 && total_ma_height > 0) { mean(c(diagram_start_y, newstudy_newreports_y, total_ma_y)) } else if (total_studies_height > 0) { mean(c(diagram_start_y, newstudy_newreports_y)) } else if (total_ma_height > 0) { mean(c(newstudy_newreports_y, total_ma_y)) } else { newstudy_newreports_y } if (side_boxes == TRUE) { sidebox <- paste0( "node [ shape = box, fontsize = ", fontsize, ", fontname = ", font, ", color = ", title_colour, " ] identification [ color = LightSteelBlue2, label = ' ', style = 'filled,rounded', pos = '", diagram_start_x, ",", identified_y, "!', width = ", section_label_length, ", height = ", identification_box_height, ", tooltip = '", tooltips["identification"], "' ]; screening [ color = LightSteelBlue2, label = ' ', style = 'filled,rounded', pos = '", diagram_start_x, ",", screening_y, "!', width = ", section_label_length, ", height = ", screening_box_height, ", tooltip = '", tooltips["screening"], "' ]; included [ color = LightSteelBlue2, label = ' ', style = 'filled,rounded', pos = '", diagram_start_x, ",", included_y, "!', width = ", section_label_length, ", height = ", included_box_height, ", tooltip = '", tooltips["included"], "' ];\n" ) } else { sidebox <- "" } if (previous == TRUE) { A <- paste0( #nolint "A [ label = '', pos = '", prev_box_x, ",", diagram_start_y + 0, "!', tooltip = '' ]" ) Aedge <- paste0( #nolint "subgraph cluster0 { edge [ color = White, arrowhead = none, arrowtail = none ] 1->2; edge [ color = ", arrow_colour, ", arrowhead = none, arrowtail = ", arrow_tail, " ] 2->A; edge [ color = ", arrow_colour, ", arrowhead = ", arrow_head, ", arrowtail = none, constraint = FALSE ] A->19; }" ) bottomedge <- paste0( "edge [ color = '", arrow_colour, "', arrowhead = '", arrow_head, "', arrowtail = '", arrow_tail, "' ] 12->19;\n" ) previous_nodes <- paste0( "node [ shape = box, fontsize = ", fontsize, ", fontname = ", font, ", color = ", greybox_colour, " ] 1 [ label = '", previous_text, "', style = 'rounded,filled', width = ", default_box_width, ", height = ", section_label_length, ", pos = '", prev_box_x, ",", top_box_y, "!', tooltip = '", tooltips["prevstud"], "' ] node [ shape = box, fontname = ", font, ", color = ", greybox_colour, " ] 2 [ label = '", prev_study_label, "', style = 'filled', width = ", default_box_width, ", height = ", prev_study_height, ", fixed = 'true', pos = '", prev_box_x, ",", identified_y, "!', tooltip = '", tooltips["previous_studies"], "' ]" ) finalnode <- paste0( "node [ shape = box, fontname = ", font, ", color = ", greybox_colour, " ] 19 [ label = '", total_studies_label, "', style = 'filled', width = ", default_box_width, ", height = ", total_studies_height, ", fixedsize = 'true', pos = '", PRISMA_get_pos_( diagram_start_x, prev_study_offset + default_box_spacing, prev_study_width, default_box_width ), ",", diagram_start_y, "!', tooltip = '", tooltips["total_studies"], "' ]" ) prev_rank1 <- "{rank = same; A; 19}" prevnode1 <- "1; " prevnode2 <- "2; " } if (other == TRUE) { # positions other_box_x <- PRISMA_get_pos_( dbr_removed_x, default_box_spacing, default_box_width, default_box_width ) other_removed_x <- PRISMA_get_pos_( other_box_x, default_box_spacing, default_box_width, default_box_width ) B <- paste0( #nolint "B [ label = '', pos = '", other_box_x, ",", newstudy_newreports_y, "!', tooltip = '' ]" ) cluster2 <- paste0( "subgraph cluster2 { edge [ color = White, arrowhead = none, arrowtail = none ] 13->14; edge [ color = ", arrow_colour, ", arrowhead = ", arrow_head, ", arrowtail = ", arrow_tail, "] 14->15; 15->16; 15->17; 17->18; edge [ color = ", arrow_colour, ", arrowhead = none, arrowtail = ", arrow_tail, "] 17->B; edge [ color = ", arrow_colour, ", arrowhead = ", arrow_head, ", arrowtail = none, constraint = FALSE ] B->12; }" ) othernodes <- paste0( "node [ shape = box, fontname = ", font, ", color = ", greybox_colour, "] 13 [ label = '", other_text, "', style = 'rounded,filled', width = ", top_box_width, ", height = ", section_label_length, ", pos = '", mean(c(other_box_x, other_removed_x)), ",", top_box_y, "!', tooltip = '", tooltips["othstud"], "' ] node [ shape = box, fontname = ", font, ", color = ", greybox_colour, "] 14 [ label = '", other_identified_label, "', style = 'filled', width = ", default_box_width, ", height = ", other_identified_height, ", pos = '", other_box_x, ",", identified_y, "!', tooltip = '", tooltips["website_results"], "' ] node [ shape = box, fontname = ", font, ", color = ", greybox_colour, "] 15 [ label = '", other_sought_reports_label, "', style = 'filled', width = ", default_box_width, ", height = ", other_sought_reports_height, ", pos = '", other_box_x, ",", sought_y, "!', tooltip = '", tooltips["other_sought_reports"], "' ] node [ shape = box, fontname = ", font, ", color = ", greybox_colour, "] 16 [ label = '", other_notretrieved_label, "', style = 'filled', width = ", default_box_width, ", height = ", other_notretrieved_height, ", pos = '", other_removed_x, ",", sought_y, "!', tooltip = '", tooltips["other_notretrieved_reports"], "' ] node [ shape = box, fontname = ", font, ", color = ", greybox_colour, " ] 17 [ label = '", other_assessed_label, "', style = 'filled', width = ", default_box_width, ", height = ", other_assessed_height, ", pos = '", other_box_x, ",", assessed_y, "!', tooltip = '", tooltips["other_assessed"], "' ] node [ shape = box, fontname = ", font, ", color = ", greybox_colour, "] 18 [ label = '", other_excluded_label, "', style = 'filled', width = ", default_box_width, ", height = ", other_excluded_height, ", pos = '", other_removed_x, ",", assessed_y, "!', tooltip = '", tooltips["other_excluded"], "' ]\n" ) extraedges <- "16->18;" othernode13 <- "; 13" othernode14 <- "; 14" othernode1516 <- "; 15; 16" othernode1718 <- "; 17; 18" othernodeB <- "; B" #nolint } else { B <- "" #nolint cluster2 <- "" othernodes <- "" extraedges <- "" othernode13 <- "" othernode14 <- "" othernode1516 <- "" othernode1718 <- "" othernodeB <- "" #nolint } if (meta_analysis == TRUE) { metanode <- paste0("node [ shape = box, fontname = ", font, ", color = ", main_colour, ", fillcolor = '', style = solid ] 23 [ label = '", total_ma_label, "', width = ", default_box_width, ", height = ", total_ma_height, ", pos = '", dbr_box_x, ",", total_ma_y, "!', tooltip = '", tooltips["total_studies_ma"], "' ]") if (previous == TRUE) { bottomedge <- paste0( bottomedge, "edge [ color = '", arrow_colour, "', arrowhead = '", arrow_head, "', arrowtail = '", arrow_tail, "' ] 19->23;\n" ) } else { bottomedge <- paste0( bottomedge, "edge [ color = '", arrow_colour, "', arrowhead = '", arrow_head, "', arrowtail = '", arrow_tail, "' ] 12->23;\n" ) } } x <- DiagrammeR::grViz( paste0( "digraph TD { graph[ splines = ortho, layout = neato, tooltip = 'Click the boxes for further information', outputorder = edgesfirst, ]", sidebox, previous_nodes, "node [ shape = box, fontsize = ", fontsize, ", fontname = ", font, ", color = ", title_colour, "] 3 [ label = '", newstud_text, "', style = 'rounded,filled', width = ", top_box_width, ", height = ", section_label_length, ", pos = '", mean(c(dbr_box_x, dbr_removed_x)), ",", top_box_y, "!', tooltip = '", tooltips["newstud"], "' ] node [ shape = box, fontname = ", font, ", color = ", main_colour, "] 4 [ label = '", dbr_identified_label, "', width = ", default_box_width, ", height = ", dbr_identified_height, ", pos = '", dbr_box_x, ",", identified_y, "!', tooltip = '", tooltips["database_results"], "' ] node [ shape = box, fontname = ", font, ", color = ", main_colour, "] 5 [ label = '", dbr_notscreened_label, "', width = ", default_box_width, ", height = ", min_box_height, ", pos = '", dbr_removed_x, ",", identified_y, "!', tooltip = '", tooltips["duplicates"], "' ] node [ shape = box, fontname = ", font, ", color = ", main_colour, " ] 6 [ label = '", dbr_screened_label, "', width = ", default_box_width, ", height = ", dbr_screened_height, ", pos = '", dbr_box_x, ",", screened_y, "!', tooltip = '", tooltips["records_screened"], "' ] node [ shape = box, fontname = ", font, ", color = ", main_colour, " ] 7 [ label = '", dbr_screened_excluded_label, "', width = ", default_box_width, ", height = ", min_box_height, ", pos = '", dbr_removed_x, ",", screened_y, "!', tooltip = '", tooltips["records_excluded"], "' ] node [ shape = box, fontname = ", font, ", color = ", main_colour, "] 8 [ label = '", dbr_sought_label, "', width = ", default_box_width, ", height = ", dbr_sought_height, ", pos = '", dbr_box_x, ",", sought_y, "!', tooltip = '", tooltips["dbr_sought_reports"], "' ] node [ shape = box, fontname = ", font, ", color = ", main_colour, "] 9 [ label = '", dbr_notretrieved_label, "', width = ", default_box_width, ", height = ", min_box_height, ", pos = '", dbr_removed_x, ",", sought_y, "!', tooltip = '", tooltips["dbr_notretrieved_reports"], "' ] node [ shape = box, fontname = ", font, ", color = ", main_colour, "] 10 [ label = '", dbr_assessed_label, "', width = ", default_box_width, ", height = ", dbr_assessed_height, ", pos = '", dbr_box_x, ",", assessed_y, "!', tooltip = '", tooltips["dbr_assessed"], "' ] node [ shape = box, fontname = ", font, ", color = ", main_colour, ", fillcolor = White, style = filled ] 11 [ label = '", dbr_excluded_label, "', width = ", default_box_width, ", height = ", min_box_height, ", pos = '", dbr_removed_x, ",", assessed_y, "!', tooltip = '", tooltips["dbr_excluded"], "' ] node [ shape = box, fontname = ", font, ", color = ", main_colour, ", fillcolor = '', style = solid ] 12 [ label = '", newstudy_newreports_label, "', width = ", default_box_width, ", height = ", newstudy_newreports_height, ", pos = '", dbr_box_x, ",", newstudy_newreports_y, "!', tooltip = '", tooltips["new_studies"], "' ]", othernodes, finalnode, metanode, "node [ shape = square, width = 0, color=White ]\n", A, "\n", B, "\n", Aedge, "node [ shape = square, width = 0, style=invis ] C [ label = '', width = ", default_box_width, ", height = ", min_box_height, ", pos = '", dbr_removed_x, ",", assessed_y, "!', tooltip = '' ] subgraph cluster1 { edge [ style = invis ] 3->4; 3->5; edge [ color = ", arrow_colour, ", arrowhead = ", arrow_head, ", arrowtail = ", arrow_tail, ", style = filled ] 4->5; 4->6; 6->7; 6->8; 8->9; 8->10; 10->C; 10->12; edge [ style = invis ] 5->7; 7->9; 9->11; ", extraedges, " }", cluster2, "\n", bottomedge, "\n\n", prev_rank1, "\n", "{ rank = same; ", prevnode1, "3", othernode13, "} { rank = same; ", prevnode2, "4; 5", othernode14, "} { rank = same; 6; 7 } { rank = same; 8; 9", othernode1516, "} { rank = same; 10; 11", othernode1718, "} { rank = same; 12", othernodeB, "} }" ) ) if (side_boxes == TRUE) { x <- PRISMA_insert_js_( x, identification_text = identification_text, screening_text = screening_text, included_text = included_text ) } if (interactive == TRUE) { x <- PRISMA_interactive_( x, urls, previous = previous, other = other ) } return(x) } #' Read in PRISMA flow diagram data #' #' @description Read in a template CSV containing data for the flow diagram #' @param data File to read in. #' @return A list of objects needed to plot the flow diagram #' @examples #' csvFile <- system.file("extdata", "PRISMA.csv", package = "PRISMA2020") #' data <- read.csv(csvFile); #' data <- PRISMA_data(data); #' @export PRISMA_data <- function(data) { #nolint # Ensure data is a df, not a tibble; # tibbles do not return vectors using df[, 1]. data <- as.data.frame(data) #Set parameters previous_studies <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "previous_studies", data[, 1] ), ]$n ) ) previous_reports <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "previous_reports", data[, 1] ), ]$n ) ) register_results <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "register_results", data[, 1] ), ]$n ) ) database_results <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "database_results", data[, 1] ), ]$n ) ) database_specific_results <- PRISMA_parse_reasons_(data[ #nolint grep( "database_specific_results", data[, 1] ), ]$n ) register_specific_results <- PRISMA_parse_reasons_(data[ #nolint grep( "register_specific_results", data[, 1] ), ]$n ) website_results <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "website_results", data[, 1] ), ]$n ) ) organisation_results <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "organisation_results", data[, 1] ), ]$n ) ) citations_results <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "citations_results", data[, 1] ), ]$n ) ) duplicates <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "duplicates", data[, 1] ), ]$n ) ) excluded_automatic <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "excluded_automatic", data[, 1] ), ]$n ) ) excluded_other <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "excluded_other", data[, 1] ), ]$n ) ) records_screened <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "records_screened", data[, 1] ), ]$n ) ) records_excluded <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "records_excluded", data[, 1] ), ]$n ) ) dbr_sought_reports <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "dbr_sought_reports", data[, 1] ), ]$n ) ) dbr_notretrieved_reports <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "dbr_notretrieved_reports", data[, 1] ), ]$n ) ) other_sought_reports <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "other_sought_reports", data[, 1] ), ]$n ) ) other_notretrieved_reports <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "other_notretrieved_reports", data[, 1] ), ]$n ) ) dbr_assessed <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "dbr_assessed", data[, 1] ), ]$n ) ) dbr_excluded <- PRISMA_parse_reasons_(data[ #nolint grep( "dbr_excluded", data[, 1] ), ]$n ) other_assessed <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "other_assessed", data[, 1] ), ]$n ) ) other_excluded <- PRISMA_parse_reasons_(data[ #nolint grep( "other_excluded", data[, 1] ), ]$n ) new_studies <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "new_studies", data[, 1] ), ]$n ) ) new_reports <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "new_reports", data[, 1] ), ]$n ) ) total_studies <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "total_studies$", data[, 1] ), ]$n ) ) total_reports <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "total_reports$", data[, 1] ), ]$n ) ) total_studies_ma <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "total_studies_ma", data[, 1] ), ]$n ) ) total_reports_ma <- scales::comma( PRISMA_format_number_( #nolint data[ grep( "total_reports_ma", data[, 1] ), ]$n ) ) tooltips <- list() for (i in seq_len(nrow(data))) { if (!is.na(data[i, ]$tooltips)) { if (is.na(data[i, ]$data)) { name <- data[i, ]$box } else { name <- data[i, ]$data } tooltips[[name]] <- data[i, ]$tooltips } } urls <- data.frame( box = data[!duplicated(data$box), ]$box, url = data[!duplicated(data$box), ]$url ) #set text - if text >33 characters, previous_text <- data[grep("prevstud", data[, 3]), ]$boxtext newstud_text <- data[grep("newstud", data[, 3]), ]$boxtext other_text <- data[grep("othstud", data[, 3]), ]$boxtext previous_studies_text <- data[grep("previous_studies", data[, 1]), ]$boxtext previous_reports_text <- data[grep("previous_reports", data[, 1]), ]$boxtext register_results_text <- data[grep("register_results", data[, 1]), ]$boxtext database_results_text <- data[grep("database_results", data[, 1]), ]$boxtext website_results_text <- data[grep("website_results", data[, 1]), ]$boxtext organisation_results_text <- data[ grep( "organisation_results", data[, 1] ), ]$boxtext citations_results_text <- data[grep("citations_results", data[, 1]), ]$boxtext duplicates_text <- data[grep("duplicates", data[, 1]), ]$boxtext excluded_automatic_text <- data[ grep( "excluded_automatic", data[, 1] ), ]$boxtext excluded_other_text <- data[grep("excluded_other", data[, 1]), ]$boxtext records_screened_text <- data[grep("records_screened", data[, 1]), ]$boxtext records_excluded_text <- data[grep("records_excluded", data[, 1]), ]$boxtext dbr_sought_reports_text <- data[ grep( "dbr_sought_reports", data[, 1] ), ]$boxtext dbr_notretrieved_reports_text <- data[ grep( "dbr_notretrieved_reports", data[, 1] ), ]$boxtext other_sought_reports_text <- data[ grep( "other_sought_reports", data[, 1] ), ]$boxtext other_notretrieved_reports_text <- data[ #nolint grep( "other_notretrieved_reports", data[, 1] ), ]$boxtext dbr_assessed_text <- data[grep("dbr_assessed", data[, 1]), ]$boxtext dbr_excluded_text <- data[grep("dbr_excluded", data[, 1]), ]$boxtext other_assessed_text <- data[grep("other_assessed", data[, 1]), ]$boxtext other_excluded_text <- data[grep("other_excluded", data[, 1]), ]$boxtext new_studies_text <- data[grep("new_studies", data[, 1]), ]$boxtext new_reports_text <- data[grep("new_reports", data[, 1]), ]$boxtext total_studies_text <- data[grep("total_studies$", data[, 1]), ]$boxtext total_reports_text <- data[grep("total_reports$", data[, 1]), ]$boxtext identification_text <- data[grep("identification", data[, 1]), ]$boxtext screening_text <- data[grep("screening", data[, 1]), ]$boxtext included_text <- data[grep("included", data[, 1]), ]$boxtext total_studies_ma_text <- data[grep("total_studies_ma", data[, 1]), ]$boxtext total_reports_ma_text <- data[grep("total_reports_ma", data[, 1]), ]$boxtext x <- list( previous_studies = previous_studies, previous_reports = previous_reports, register_results = register_results, database_results = database_results, database_specific_results = database_specific_results, register_specific_results = register_specific_results, website_results = website_results, organisation_results = organisation_results, citations_results = citations_results, duplicates = duplicates, excluded_automatic = excluded_automatic, excluded_other = excluded_other, records_screened = records_screened, records_excluded = records_excluded, dbr_sought_reports = dbr_sought_reports, dbr_notretrieved_reports = dbr_notretrieved_reports, other_sought_reports = other_sought_reports, other_notretrieved_reports = other_notretrieved_reports, dbr_assessed = dbr_assessed, dbr_excluded = dbr_excluded, other_assessed = other_assessed, other_excluded = other_excluded, new_studies = new_studies, new_reports = new_reports, total_studies = total_studies, total_reports = total_reports, total_studies_ma = total_studies_ma, total_reports_ma = total_reports_ma, previous_text = previous_text, newstud_text = newstud_text, other_text = other_text, previous_studies_text = previous_studies_text, previous_reports_text = previous_reports_text, register_results_text = register_results_text, database_results_text = database_results_text, website_results_text = website_results_text, organisation_results_text = organisation_results_text, citations_results_text = citations_results_text, duplicates_text = duplicates_text, excluded_automatic_text = excluded_automatic_text, excluded_other_text = excluded_other_text, records_screened_text = records_screened_text, records_excluded_text = records_excluded_text, dbr_sought_reports_text = dbr_sought_reports_text, dbr_notretrieved_reports_text = dbr_notretrieved_reports_text, other_sought_reports_text = other_sought_reports_text, other_notretrieved_reports_text = other_notretrieved_reports_text, dbr_assessed_text = dbr_assessed_text, dbr_excluded_text = dbr_excluded_text, other_assessed_text = other_assessed_text, other_excluded_text = other_excluded_text, new_studies_text = new_studies_text, new_reports_text = new_reports_text, total_studies_text = total_studies_text, total_reports_text = total_reports_text, total_studies_ma_text = total_studies_ma_text, total_reports_ma_text = total_reports_ma_text, identification_text = identification_text, screening_text = screening_text, included_text = included_text, tooltips = tooltips, urls = urls ) return(x) } #' Save PRISMA2020 flow diagram #' @description Save the output from [PRISMA_flowdiagram()] to the #' working directory. #' @param plotobj A plot produced using [PRISMA_flowdiagram()]. #' @param filename The filename to save (including extension) #' @param filetype The filetype to save the plot in, supports: #' HTML, ZIP, PDF, PNG, SVG, PS and WEBP #' (if NA, the filetype will be calculated out based on the file extension) #' HTML files maintain hyperlinks and tooltips #' The ZIP option creates an archive containing the HTML file, #' alongside supporting javascript and css files in an adjacent folder, #' instead of embedded base64 within the HTML file #' @param overwrite if TRUE, will overwrite an existing file #' @param width passed as the width argument to #' [rsvg::rsvg()] and similar functions #' @param height passed as the height argument to #' [rsvg::rsvg()] and similar functions #' @param css passed as the css argument to #' [rsvg::rsvg()] and similar functions #' @return the absolute filename of the saved diagram plot. #' @examples #' csvFile <- system.file("extdata", "PRISMA.csv", package = "PRISMA2020") #' data <- read.csv(csvFile); #' data <- PRISMA_data(data); #' plot <- PRISMA_flowdiagram(data, #' fontsize = 12, #' interactive = TRUE, #' previous = FALSE, #' other = TRUE); #' PRISMA_save(plot, filename = tempfile(), filetype="html"); #' @export PRISMA_save <- function( #nolint plotobj, filename = "PRISMA2020_flowdiagram.html", filetype = NA, overwrite = FALSE, width = NULL, height = NULL, css = NULL ) { if (!file.exists(filename) || overwrite == TRUE) { format_real <- PRISMA_calc_filetype_(filename, filetype) #nolint switch( format_real, "HTML" = { tmp_html <- tempfile( pattern = "PRISMA2020_", tmpdir = tempdir(), fileext = ".html" ) htmlwidgets::saveWidget( plotobj, file = tmp_html, title = "PRISMA2020 Flowdiagram" ) if (!(file.copy(tmp_html, filename, overwrite = TRUE))) { stop("Error saving HTML") } file.remove(tmp_html) }, "ZIP" = { curr_wd <- getwd() tmp_dir <- tempdir() setwd(tmp_dir) tmp_zipfile <- tempfile( pattern = "PRISMA2020_", tmpdir = tempdir(), fileext = ".zip" ) tmp_html <- paste0( tools::file_path_sans_ext(basename(filename)), ".html" ) tmp_libdir <- paste0(tmp_html, "_files") htmlwidgets::saveWidget( plotobj, file = tmp_html, libdir = tmp_libdir, selfcontained = FALSE, title = "PRISMA2020 Flowdiagram" ) zip::zip(zipfile = tmp_zipfile, files = c(tmp_html, tmp_libdir)) setwd(curr_wd) if (!(file.copy(paste0(tmp_zipfile), filename, overwrite = TRUE))) { stop("Error saving ZIP File") } }, "PDF" = { tmp_svg <- PRISMA_gen_tmp_svg_(plotobj) #nolint rsvg::rsvg_pdf( tmp_svg, filename, width = width, height = height, css = css ) file.remove(tmp_svg) }, "PNG" = { tmp_svg <- PRISMA_gen_tmp_svg_(plotobj) #nolint rsvg::rsvg_png( tmp_svg, filename, width = width, height = height, css = css ) file.remove(tmp_svg) }, "SVG" = { tmp_svg <- PRISMA_gen_tmp_svg_(plotobj) #nolint if (!(file.copy(tmp_svg, filename, overwrite = TRUE))) { stop("Error saving SVG") } file.remove(tmp_svg) }, "PS" = { tmp_svg <- PRISMA_gen_tmp_svg_(plotobj) #nolint rsvg::rsvg_ps( tmp_svg, filename, width = width, height = height, css = css ) file.remove(tmp_svg) }, "WEBP" = { tmp_svg <- PRISMA_gen_tmp_svg_(plotobj) #nolint rsvg::rsvg_webp( tmp_svg, filename, width = width, height = height, css = css ) file.remove(tmp_svg) }, stop("Please choose one of the supported file types") ) return(tools::file_path_as_absolute(filename)) } else { stop("File exists, please set overwite = TRUE to overwrite") } } #' Defunct function - replaced by "PRISMA_interactive_" #' @description Defunct function - replaced by "PRISMA_interactive_" #' @seealso [PRISMA_interactive_()] #' @param plot A plot object from [PRISMA_flowdiagram()]. #' @param urls A dataframe consisting of two columns: nodes and urls. The first #' column should contain 19 rows for the nodes from node1 to node19. The second #' column should contain a corresponding URL for each node. #' @param previous Logical argument (TRUE or FALSE) (supplied through #' [PRISMA_flowdiagram()]) specifying whether previous studies were sought. #' @param other Logical argument (TRUE or FALSE) (supplied through #' [PRISMA_flowdiagram()]) specifying whether other studies were sought. sr_flow_interactive <- function(plot, urls, previous, other) { .Defunct("PRISMA_interactive_") } #' Defunct function - replaced by "PRISMA_data" #' @description Defunct function - replaced by "PRISMA_data" #' @seealso [PRISMA_data()] #' @param data File to read in. read_PRISMAdata <- function(data){ #nolint .Defunct("PRISMA_data") } ================================================ FILE: R/globals.R ================================================ utils::globalVariables(c("previous_studies", "previous_reports", "register_results", "database_results", "website_results", "organisation_results", "citations_results", "duplicates", "excluded_automatic", "excluded_other", "records_screened", "records_excluded", "dbr_sought_reports", "dbr_notretrieved_reports", "other_sought_reports", "other_notretrieved_reports", "dbr_assessed", "dbr_excluded", "other_assessed", "other_excluded", "new_studies", "new_reports", "total_studies", "total_reports", "total_studies_ma", "total_reports_ma", "previous_text", "newstud_text", "other_text", "previous_studies_text", "previous_reports_text", "register_results_text", "database_results_text", "website_results_text", "organisation_results_text", "citations_results_text", "duplicates_text", "excluded_automatic_text", "excluded_other_text", "records_screened_text", "records_excluded_text", "dbr_sought_reports_text", "dbr_notretrieved_reports_text", "other_sought_reports_text", "other_notretrieved_reports_text", "dbr_assessed_text", "dbr_excluded_text", "other_assessed_text", "other_excluded_text", "new_studies_text", "new_reports_text", "total_studies_text", "total_reports_text", "tooltips", "urls", "identification_text", "included_text", "screening_text", "total_studies_ma_text", "total_reports_ma_text")) ================================================ FILE: R/utils.R ================================================ # Utility functions for PRISMA_flowdiagram #' Calculate the correct height of a box from a list (e.g. of exclusion reasons) #' @description Get the correct height for a box #' @param n the number of rows of text in the label #' @param offset the offset height (e.g. 3.5) #' @param min the minimum number of rows before adjusting #' @return the height of the box #' @keywords internal PRISMA_get_height_ <- function (n, offset, min = 2) { #nolint lines <- n + 1 if (lines > min) { height <- offset + (lines * 0.25) - (min * 0.25) } else { height <- offset } return(height) } #' Calculate the correct position of a node #' @description Get the correct position for a node #' @param first_box_location the location of the first node #' @param offset the offset from the first node #' @param length_orig the width/height of the original node #' @param length_new the width/height of the new node #' @param negative_offset is the offset negative (defaults to false) #' @return the position of the node #' @keywords internal PRISMA_get_pos_ <- function (first_box_location, offset, length_orig, length_new, negative_offset = FALSE) { #nolint if (negative_offset == FALSE) { pos <- first_box_location + offset + (length_orig / 2) + (length_new / 2) } else { pos <- first_box_location - offset - (length_orig / 2) - (length_new / 2) } return(pos) } #' Generate / insert JS for labels #' @description Generate the javascript method to insert the side labels #' @param plot the plot object (without side labels) #' @param identification_text the text to use as the "identification" label #' @param screening_text the text to use as the "screening" label #' @param included_text the text to use as the "identification" label #' @return the plot object (with JS to generate side labels) #' @keywords internal PRISMA_insert_js_ <- function ( #nolint plot, identification_text, screening_text, included_text ) { # This JS loops through each node, and # locates the relevent tag containing the label # The blank label is replaced with the relevent descriptive label # This is done in the loop as positioning due # to rotation otherwise being difficult # we rotate the text and adjust the position to ensure that # the now rotated text is displayed correctly javascript <- htmltools::HTML(paste0(' const nodeMap = new Map([["node1","', identification_text, '"], ["node2","', screening_text, '"], ["node3","', included_text, '"]]); for (const [node, label] of nodeMap) { var theDiv = document.getElementById(node); var theText = theDiv.querySelector("text"); var attrX = theText.getAttribute("x"); var attrY = theText.getAttribute("y"); theText.setAttribute("y",parseFloat(attrX)) theText.setAttribute("x",parseFloat(attrY)*-1) theText.setAttribute("style","transform: rotate(-90deg);") theText.setAttribute("dominant-baseline", "middle") theText.innerHTML = label; } ')) plot <- htmlwidgets::appendContent( plot, htmlwidgets::onStaticRenderComplete(javascript) ) return(plot) } #' Add the hyperlink to the given node #' #' @description Generate the javascript method to insert the side labels #' @param node the relevent node #' @param url the URL the node should link to #' @return An interactive flow diagram plot. #' @keywords internal PRISMA_add_hyperlink_ <- function( #nolint node, url ) { t <- paste0( #nolint "const ", node, ' = document.getElementById("', node, '"); var link', node, ' = "" + ', node, '.innerHTML + "";', "\n", node, ".innerHTML = link", node, ";" ) } #' Plot interactive flow diagram for systematic reviews #' @description Converts a PRISMA systematic review flow diagram into an #' interactive HTML plot, for embedding links from each box. #' @seealso [PRISMA_interactive_()] #' @param plot A plot object from [PRISMA_flowdiagram()]. #' @param urls A dataframe consisting of two columns: nodes and urls. The first #' column should contain 19 rows for the nodes from node1 to node19. The second #' column should contain a corresponding URL for each node. #' @param previous Logical argument (TRUE or FALSE) (supplied through #' [PRISMA_flowdiagram()]) specifying whether previous studies were sought. #' @param other Logical argument (TRUE or FALSE) (supplied through #' [PRISMA_flowdiagram()]) specifying whether other studies were sought. #' @return An interactive flow diagram plot. #' @keywords internal PRISMA_interactive_ <- function( #nolint plot, urls, previous, other ) { if (previous == TRUE && other == TRUE) { link <- data.frame( boxname = c( "identification", "screening", "included", "prevstud", "box1", "newstud", "box2", "box3", "box4", "box5", "box6", "box7", "box8", "box9", "box10", "othstud", "box11", "box12", "box13", "box14", "box15", "box16", "A", "B" ), node = paste0("node", seq(1, 24)) ) target <- c( "node1", "node2", "node3", "node4", "node5", "node23", "node6", "node7", "node8", "node9", "node10", "node11", "node12", "node13", "node14", "node15", "node22", "node16", "node17", "node18", "node19", "node20", "node21", "node24" ) } else if (previous == FALSE && other == TRUE) { link <- data.frame( boxname = c( "identification", "screening", "included", "newstud", "box2", "box3", "box4", "box5", "box6", "box7", "box8", "box9", "box10", "othstud", "box11", "box12", "box13", "box14", "box15", "B" ), node = paste0("node", seq(1, 20)) ) target <- c( "node1", "node2", "node3", "node4", "node5", "node6", "node7", "node8", "node9", "node10", "node11", "node12", "node13", "node14", "node15", "node16", "node17", "node18", "node19", "node20" ) } else if (previous == TRUE && other == FALSE) { link <- data.frame( boxname = c( "identification", "screening", "included", "prevstud", "box1", "newstud", "box2", "box3", "box4", "box5", "box6", "box7", "box8", "box9", "box10", "box16", "A" ), node = paste0("node", seq(1, 17)) ) target <- c( "node1", "node2", "node3", "node4", "node5", "node6", "node7", "node8", "node9", "node10", "node11", "node12", "node13", "node14", "node15", "node16", "node17" ) } else { link <- data.frame( boxname = c( "identification", "screening", "included", "newstud", "box2", "box3", "box4", "box5", "box6", "box7", "box8", "box9", "box10" ), node = paste0("node", seq(1, 13)) ) target <- c( "node1", "node2", "node3", "node4", "node5", "node6", "node7", "node8", "node9", "node10", "node11", "node12", "node13" ) } link <- merge(link, urls, by.x = "boxname", by.y = "box", all.x = TRUE) link <- link[match(target, link$node), ] node <- link$node url <- link$url #the following code adds the location link for the new window javascript <- htmltools::HTML( paste( mapply( PRISMA_add_hyperlink_, node, url ), collapse = "\n" ) ) htmlwidgets::prependContent( plot, htmlwidgets::onStaticRenderComplete(javascript) ) } #' Calculate the correct filetime #' #' @description Work out the correct filetype to save the file as #' @param fn The filename (including extension) #' @param ft The filetype (which can be NA or NULL) #' @return the filetype taken from the filename, or overriden by the ft param #' @keywords internal PRISMA_calc_filetype_ <- function(fn, ft) { #nolint # if the filetype is set, return that, otherwise # calculate the filetype from the extension (HTM becomes HTML) if (!is.na(ft) && !is.null(ft)) { the_ft <- toupper(ft) } else { the_ft <- toupper(tools::file_ext(fn)) if (the_ft == "HTM") { the_ft <- "HTML" } } return(the_ft) } #' Format numbers with commas into numbers #' #' @description Turn strings containing numbers +/- commas into numbers #' @param x the number to format #' @return the number with commas removed #' @keywords internal PRISMA_format_number_ <- function(x) { #nolint if (is.character(x)) { x <- gsub(",", "", x) x <- gsub("[^0-9.]", "", x) } return(as.numeric(x)) } #' Parse an exclusion reason into a data frame #' #' @description Parse an exclusion reason string and returns a dataframe #' containing reasons and number #' @param reasons the string to parse #' @return a dataframe containing reasons and number applicable #' @keywords internal #' PRISMA_parse_reasons_ <- function(reasons) { #nolint reasons_out <- NA if (grepl("[^0-9,]", as.character(reasons))) { reasons_out <- data.frame( reason = gsub( ",.*$", "", unlist( strsplit( as.character(reasons), split = "(;)( )?" ) ) ), n = scales::comma( PRISMA_format_number_( #nolint gsub( ".*?,([ 0-9,]*)|.*()", "\\1", unlist( strsplit( as.character(reasons), split = "(;)( )?" ) ) ) ) ) ) } else { reasons_out <- data.frame( reason = "", n = scales::comma( PRISMA_format_number_(as.character(reasons)) ) ) } return(reasons_out) } #' Formats multiple exclusion reasons properly for printing #' #' @description Parse an exclusion reason dataframe from #' [PRISMA2020::PRISMA_parse_reasons_()] and returns a properly formatted string #' @param df the dataframe to parse #' @return a string ready for printing #' @keywords internal #' PRISMA_format_reasons_ <- function(df) { #nolint out_string <- paste0( ":", paste( paste( "\n", df[, 1], " (n = ", df[, 2], ")", sep = "" ), collapse = "" ) ) return(out_string) } #' Generate a temporary SVG from a plot object #' #' @description Generate and save a temporary SVG from a plot object #' @param obj the plot object #' @return the full path to the saved SVG #' @keywords internal PRISMA_gen_tmp_svg_ <- function(obj) { #nolint # generate temporary filenames tmpfilehtml <- tempfile( pattern = "PRISMA2020_", tmpdir = tempdir(), fileext = ".html" ) tmpfilesvg <- tempfile( pattern = "PRISMA2020_", tmpdir = tempdir(), fileext = ".svg" ) # save the widget as HTML and read it into a variable htmlwidgets::saveWidget(obj, file = tmpfilehtml) htmldata <- xml2::read_html(tmpfilehtml) # extract our labelling javascript using xml_find_first and xpath # it finds the first script element follwing the grViz class # this looks to be quite fragile if we change our injected JS js <- xml2::xml_text( xml2::xml_find_first( htmldata, '//div[contains(@class, "grViz")]//following-sibling::script' ) ) # use DiagrammeRsvg to export an SVG from the htmlwidgets code # this uses the V8 engine in the background so takes time # then read the SVG's XML into a variable svg <- DiagrammeRsvg::export_svg(obj) svg <- xml2::read_xml(svg) # we need to extract the node names and the label values from our JS # so find the appropriate part of the code # (again, sensitive to script changes) # we then extract the node names and labels and insert them into the SVG # in a similar manner to the original JS code jsnode <- stringr::str_split( stringr::str_remove_all( stringr::str_match( js, "const nodeMap = new Map\\(\\[(.*)\\]\\);" )[1, 2], "\\[|\"|]" ), ",\\s", simplify = TRUE ) len <- length(jsnode) for (i in 1:len) { matsp <- stringr::str_split_fixed(jsnode[i], ",", 2) namespace <- xml2::xml_ns(svg) xml_text_node <- xml2::xml_find_first( svg, paste0('//d1:g[@id="', matsp[, 1], '"]//d1:text'), namespace ) attr_x <- xml2::xml_attr(xml_text_node, "x") attr_y <- xml2::xml_attr(xml_text_node, "y") xml2::xml_attr(xml_text_node, "x") <- as.double(attr_y) * -1 xml2::xml_attr(xml_text_node, "y") <- as.double(attr_x) + 2 # libRSVG does not support css transforms # so we need to use the native SVG transform attribute xml2::xml_attr(xml_text_node, "transform") <- "rotate(-90)" xml2::xml_text(xml_text_node) <- matsp[, 2] } xml2::write_xml(svg, file = tmpfilesvg) return(tmpfilesvg) } ================================================ FILE: README.md ================================================ [![Check & Deploy](https://github.com/prisma-flowdiagram/PRISMA2020/actions/workflows/check-and-deploy.yml/badge.svg)](https://github.com/prisma-flowdiagram/PRISMA2020/actions/workflows/check-and-deploy.yml) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/prisma-flowdiagram/PRISMA2020) ![GitHub Repo stars](https://img.shields.io/github/stars/prisma-flowdiagram/PRISMA2020?style=social) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.4287834.svg)](https://doi.org/10.5281/zenodo.4287834) [![DOI](https://zenodo.org/badge/DOI/10.1002/cl2.1230.svg)](https://doi.org/10.1002/cl2.1230) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/O5O4JQJ5W) # PRISMA2020 Flow Diagram You can use this package to produce a flow diagram that conforms to the PRISMA 2020 standards using the `PRISMA_flowdiagram()` function. The data can be manually entered into the function, or loaded up using the template CSV file provided in 'INST/EXTDATA/'. The function, (if 'interactive = TRUE') produces an interactive HTML ouput with each box linking to a specific page (e.g. of search results or methods details), and hover-over tooltips for further information.

The 'Previous' and 'Other' study arms of the flowchart can be toggled on and off and removed or added to the diagram by specifying this in the function inputs. A static version is produced otherwise. See the interactive template here.
Visit the web-based Shiny app for a point-and-click user interface here. --- ## Docker Installation You can quickly install the PRISMA2020 package and run the included example shinyapp using [Docker](https://docs.docker.com/engine/install/). ```bash docker build . -t prisma-shiny:1 docker run -it --rm -p 3838:3838 prisma-shiny:1 ``` Then visit http://localhost:3838/app in your web browser. To stop the app, press `Ctrl+C` in the terminal. --- Please cite as:
Haddaway, N. R., Page, M. J., Pritchard, C. C., & McGuinness, L. A. (2022). PRISMA2020: An R package and Shiny app for producing PRISMA 2020-compliant flow diagrams, with interactivity for optimised digital transparency and Open Synthesis. Campbell Systematic Reviews, 18, e1230. https://doi.org/10.1002/cl2.1230
Citation in .ris format (right click 'Save Link As') ================================================ FILE: code-of-conduct.md ================================================ # Contributor Code of Conduct As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion. Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue, emailing the project lead , Neal Haddaway (), or one of the core developers. This Code of Conduct is adapted from the Contributor Covenant (http://contributor-covenant.org), version 1.0.0, available at http://contributor-covenant.org/version/1/0/0/ ================================================ FILE: contributing.md ================================================ # Contributing to PRISMA2020 The goal of this guide is to help you get up and contributing to PRISMA2020 as quickly as possible. Please note that PRISMA2020 is released with a [Contributor Code of Conduct](code-of-conduct.md). By contributing to this project, you agree to abide by its terms. ### Fixing typos Small typos or grammatical errors in documentation may be edited directly using the GitHub web interface, so long as the changes are made in the _source_ file. * DO: edit a roxygen comment in a `.R` file below `R/`. * DO NOT: edit an `.Rd` file below `man/`. Similarly, typos/errors in the supporting documents (e.g. these contributing guidelines, code-of-conduct.md) may be edited directly using the GitHub web interface. The exception to the above is when typos/errors occur in the README.md document. To correct these: * Fork the respository to your personal GitHub account * Edit the README.Rmd document to fix the error * Knit the README.Rmd document to produce the corrected README.md * Commit changes and push both corrected documents to your forked respository * Issue a pull request (see the section on [Pull Requests](#pull-requests), below). ### Filing an issue When filing an issue, the most important thing is to include a minimal reproducible example so that we can quickly verify the problem, and then figure out how to fix it. See "[Writing a good reproducible example](https://reprex.tidyverse.org/articles/reprex-dos-and-donts.html)". 1. **Packages** should be loaded at the top of the script, so it's easy to see which ones the example needs. 1. Spend a little bit of time ensuring that your **code** is easy for others to read: * make sure you've used spaces and your variable names are concise, but informative * use comments generously to indicate where your problem lies * do your best to remove everything that is not related to the problem. You can check you have actually made a reproducible example by starting up a fresh R session and pasting your script in. ### Pull Requests #### Getting Started * Make sure you have a [GitHub account](https://github.com/signup/free). * Familiarise yourself with Git and Github, using the [resources](#additional-resources) at the end of this page. #### Prerequisites Before you make a substantial pull request, you should always file an issue and make sure someone from the team agrees that it’s a problem. If you’ve found a bug, create an associated issue and illustrate the bug with a minimal [reproducible example](https://reprex.tidyverse.org/articles/reprex-dos-and-donts.html). #### Pull request process * We recommend that you create a Git branch for each pull request (PR). * Look at the Travis and AppVeyor build status before and after making changes. The `README` should contain badges for any continuous integration services used by the package. * New code should follow the tidyverse [style guide](http://style.tidyverse.org). You can use the [styler](https://CRAN.R-project.org/package=styler) package to apply these styles, but please don't restyle code that has nothing to do with your PR. * We use [roxygen2](https://cran.r-project.org/package=roxygen2), with [Markdown syntax](https://cran.r-project.org/web/packages/roxygen2/vignettes/markdown.html), for documentation. * We use [testthat](https://cran.r-project.org/package=testthat). Contributions with test cases included are easier to accept. ### Additional Resources * The [Git and Github](http://r-pkgs.had.co.nz/git.html) section of the __R Packages__ book by Hadley Wickham * [Happy Git and GitHub for the useR](https://happygitwithr.com/) * General GitHub [documentation](https://help.github.com/) * GitHub pull request [documentation]](https://help.github.com/articles/creating-a-pull-request/) ================================================ FILE: inst/CITATION ================================================ bibentry( header = "To cite PRISMA2020 in publications, please use:", bibtype = "Article", title = "PRISMA2020: An R package and Shiny app for producing PRISMA 2020-compliant flow diagrams, with interactivity for optimised digital transparency and Open Synthesis", author = "Neal R Haddaway, Matthew J Page, Chris C Pritchard and Luke A McGuinness", year = "2022", doi = "10.1002/cl2.1230", journal = "Campbell Systematic Reviews", number = "2", volume = "18", pages = "e1230", ) ================================================ FILE: inst/extdata/PRISMA.csv ================================================ data,node,box,description,boxtext,tooltips,url,n NA,node4,prevstud,Grey title box; Previous studies,Previous studies,Grey title box; Previous studies,prevstud.html,0 previous_studies,node5,box1,Studies included in previous version of review,Studies included in previous version of review,Studies included in previous version of review,previous_studies.html,0 previous_reports,NA,box1,Reports of studies included in previous version of review,Reports of studies included in previous version of review,NA,previous_reports.html,0 NA,node6,newstud,Yellow title box; Identification of new studies via databases and registers,Identification of new studies via databases and registers,Yellow title box; Identification of new studies via databases and registers,newstud.html,0 database_results,node7,box2,Records identified from: Databases,Databases,Records identified from: Databases and Registers,database_results.html,0 database_specific_results,NA,box2,Records identified from: specific databases,Specific Databases,NA,database_results.html,"Database 1, xxx; Database 2, xxx; Database 3, xxx" register_results,NA,box2,Records identified from: Registers,Registers,NA,NA,0 register_specific_results,NA,box2,Records identified from: specific registers,Specific Registers,NA,database_results.html,"Register 1, xxx; Register 2, xxx; Register 3, xxx" NA,node16,othstud,Grey title box; Identification of new studies via other methods,Identification of new studies via other methods,Grey title box; Identification of new studies via other methods,othstud.html,0 website_results,node17,box11,Records identified from: Websites,Websites,"Records identified from: Websites, Organisations and Citation Searching",website_results.html,0 organisation_results,,box11,Records identified from: Organisations,Organisations,NA,NA,0 citations_results,NA,box11,Records identified from: Citation searching,Citation searching,NA,NA,0 duplicates,node8,box3,Duplicate records,Duplicate records,Duplicate records,duplicates.html,0 excluded_automatic,NA,box3,Records marked as ineligible by automation tools,Records marked as ineligible by automation tools,NA,NA,0 excluded_other,NA,box3,Records removed for other reasons,Records removed for other reasons,NA,NA,0 records_screened,node9,box4,Records screened (databases and registers),Records screened,Records screened (databases and registers),records_screened.html,0 records_excluded,node10,box5,Records excluded (databases and registers),Records excluded,Records excluded (databases and registers),records_excluded.html,0 dbr_sought_reports,node11,box6,Reports sought for retrieval (databases and registers),Reports sought for retrieval,Reports sought for retrieval (databases and registers),dbr_sought_reports.html,0 dbr_notretrieved_reports,node12,box7,Reports not retrieved (databases and registers),Reports not retrieved,Reports not retrieved (databases and registers),dbr_notretrieved_reports.html,0 other_sought_reports,node18,box12,Reports sought for retrieval (other),Reports sought for retrieval,Reports sought for retrieval (other),other_sought_reports.html,0 other_notretrieved_reports,node19,box13,Reports not retrieved (other),Reports not retrieved,Reports not retrieved (other),other_notretrieved_reports.html,0 dbr_assessed,node13,box8,Reports assessed for eligibility (databases and registers),Reports assessed for eligibility,Reports assessed for eligibility (databases and registers),dbr_assessed.html,0 dbr_excluded,node14,box9,"Reports excluded (databases and registers): [separate reasons and numbers using ; e.g. Reason1, xxx; Reason2, xxx; Reason3, xxx]",Reports excluded,Reports excluded (databases and registers),dbrexcludedrecords.html,"Reason1, xxx; Reason2, xxx; Reason3, xxx" other_assessed,node20,box14,Reports assessed for eligibility (other),Reports assessed for eligibility,Reports assessed for eligibility (other),other_assessed.html,0 other_excluded,node21,box15,"Reports excluded (other): [separate reasons and numbers using ; e.g. Reason1, xxx; Reason2, xxx; Reason3, xxx]",Reports excluded,Reports excluded (other),other_excluded.html,"Reason1, xxx; Reason2, xxx; Reason3, xxx" new_studies,node15,box10,New studies included in review,New studies included in review,New studies included in review,new_studies.html,0 new_reports,NA,box10,Reports of new included studies,Reports of new included studies,NA,NA,0 total_studies,node22,box16,Total studies included in review,Total studies included in review,Total studies included in review,total_studies.html,0 total_reports,NA,box16,Reports of total included studies,Reports of total included studies,NA,NA,0 identification,node1,identification,Blue identification box,Identification,Blue identification box,identification.html,0 screening,node2,screening,Blue screening box,Screening,Blue screening box,screening.html,0 included,node3,included,Blue included box,Included,Blue included box,included.html,0 total_studies_ma,node23,box17,Total studies included in meta-analysis,Total studies included in meta-analysis,Total studies included in meta-analysis,total_studies_meta_analysis.html,0 total_reports_ma,NA,box17,Reports of total included studies in meta-analysis,Reports of total included studies in meta-analysis,NA,NA,0 ================================================ FILE: inst/extdata/Template.html ================================================
================================================ FILE: inst/extdata/citation.ris ================================================ TY - JOUR T1 - PRISMA2020: An R package and Shiny app for producing PRISMA 2020-compliant flow diagrams, with interactivity for optimised digital transparency and Open Synthesis AU - Haddaway, Neal R. AU - Page, Matthew J. AU - Pritchard, Chris C. AU - McGuinness, Luke A. Y1 - 2022/06/01 PY - 2022 DA - 2022/06/01 N1 - https://doi.org/10.1002/cl2.1230 DO - https://doi.org/10.1002/cl2.1230 T2 - Campbell Systematic Reviews JF - Campbell Systematic Reviews JO - Campbell Systematic Reviews JA - Campbell Systematic Reviews SP - e1230 VL - 18 IS - 2 PB - John Wiley & Sons, Ltd SN - 1891-1803 M3 - https://doi.org/10.1002/cl2.1230 UR - https://doi.org/10.1002/cl2.1230 Y2 - 2022/05/06 N2 - Abstract Background Reporting standards, such as PRISMA aim to ensure that the methods and results of systematic reviews are described in sufficient detail to allow full transparency. Flow diagrams in evidence syntheses allow the reader to rapidly understand the core procedures used in a review and examine the attrition of irrelevant records throughout the review process. Recent research suggests that use of flow diagrams in systematic reviews is poor and of low quality and called for standardised templates to facilitate better reporting in flow diagrams. The increasing options for interactivity provided by the Internet gives us an opportunity to support easy-to-use evidence synthesis tools, and here we report on the development of a tool for the production of PRISMA 2020-compliant systematic review flow diagrams. Methods and Findings We developed a free-to-use, Open Source R package and web-based Shiny app to allow users to design PRISMA flow diagrams for their own systematic reviews. Our tool allows users to produce standardised visualisations that transparently document the methods and results of a systematic review process in a variety of formats. In addition, we provide the opportunity to produce interactive, web-based flow diagrams (exported as HTML files), that allow readers to click on boxes of the diagram and navigate to further details on methods, results or data files. We provide an interactive example here; https://prisma-flowdiagram.github.io/. Conclusions We have developed a user-friendly tool for producing PRISMA 2020-compliant flow diagrams for users with coding experience and, importantly, for users without prior experience in coding by making use of Shiny (https://estech.shinyapps.io/prisma_flowdiagram/). This free-to-use tool will make it easier to produce clear and PRISMA 2020-compliant systematic review flow diagrams. Significantly, users can also produce interactive flow diagrams for the first time, allowing readers of their reviews to smoothly and swiftly explore and navigate to further details of the methods and results of a review. We believe this tool will increase use of PRISMA flow diagrams, improve the compliance and quality of flow diagrams, and facilitate strong science communication of the methods and results of systematic reviews by making use of interactivity. We encourage the systematic review community to make use of the tool, and provide feedback to streamline and improve their usability and efficiency. ER - ================================================ FILE: inst/shiny-examples/PRISMA_flowdiagram/app.R ================================================ library(shiny) library(shinyjs) library(rsvg) library(DT) #nolint library(rio) library(devtools) library(PRISMA2020) #nolint template <- read.csv("www/PRISMA.csv", stringsAsFactors = FALSE) #nolint the_options <- c( "Not Included", "Included", "Not Included", "Not Included", "Not Included" ) names(the_options) <- c( "previous", "other", "dbDetail", "regDetail", "metaAnalysis" ) # Define UI for application that draws a histogram ui <- tagList( #nolint tags$head( tags$script( src = "labels.js" ), tags$style( type = "text/css",#nolint "body {padding-top: 70px;}", #nolint "body {padding-bottom: 50px;}" #nolint ), tags$link( rel = "shortcut icon", #nolint href = "favicon.ico" #nolint ), # the below enables us to utilise analytics when pushing to shinyapps.io. # if self hosting you can insert your own analytics code here # it is your responsibility to ensure compliance with regulations such as # the EU GDPR. we use a self-hosted version of umami, # configured not to store any cookies or personally identifiable data. # We also respect the "do-not-track" header. analytics <- if (Sys.getenv("PRISMA_ANALYTICS") == TRUE) { #nolint tags$script( src = "https://umami.christopherpritchard.co.uk/umami.js", # nolint "async", "defer", "data-website-id" = "72f80a48-0dea-4914-9619-465de3df82a4", # nolint "data-do-not-track" = "true", # nolint "data-host-url" = "https://umami.christopherpritchard.co.uk", # nolint "data-domains" = "estech.shinyapps.io" # nolint ) }, kofi_load <- if (Sys.getenv("KOFI_DONATE") == TRUE) { #nolint tags$script( src='https://storage.ko-fi.com/cdn/scripts/overlay-widget.js' # nolint ) }, kofi_show <- if (Sys.getenv("KOFI_DONATE") == TRUE) { #nolint tags$script( src = "kofi.js" ) } ), navbarPage( "PRISMA Flow Diagram", position = "fixed-top", # Tab 1 ---- tabPanel("Home", fluidRow( column(10, offset = 1, h4("To get started, click \"Create flow diagram\" above, or read the instructions below for more information."), br(), "Systematic reviews should be described in a high degree of methodological detail. ", tags$a( href = "http://prisma-statement.org/", "The PRISMA Statement" ), "calls for a high level of reporting detail in systematic reviews and meta-analyses. An integral part of the methodological description of a review is a flow diagram.", br(), br(), "This tool allows you to produce a flow diagram for your own review that conforms to ", tags$a( href = "https://journals.plos.org/plosmedicine/article?id=10.1371/journal.pmed.1003583", # nolint "the PRISMA2020 Statement." ), "You can provide the numbers in the data entry section of the 'Create flow diagram' tab. These numbers will be initialised to any values provided in the URL query string. For example, if you provide the URL path: '?website_results=100&organisation_results=200', this will initialise the website results to 100 and the organisation results to 200. The name of the query string parameter should match the name of the 'data' column in the template file below. The additional arguments \"previous\", \"other\", \"dbDetail\", and \"regDetail\" can be used to set the initial main options for further customisation. Alternatively, you can use the template file below to specify any values, and to change somé of the labels within the diagram.", br(), br(), "This tool also allows you to download an interactive HTML version of the plot, alongside several other common formats.", br(), br(), "We also provide an R package:", tags$a( href = "https://github.com/prisma-flowdiagram/PRISMA2020", "PRISMA2020 flow diagram R package on Github." ), br(), br(), "Please let us know if you have any feedback or if you encounter an error by creating an", tags$a( href = "https://github.com/prisma-flowdiagram/PRISMA2020/issues", "issue on GitHub" ), br(), br(), tags$a( href = "PRISMA.csv", "Download the template CSV file", download = NA, target = "_blank" ), br(), br(), "Upload your edited file here:", br(), fileInput( "data_upload", "Choose CSV File", multiple = FALSE, accept = c( "text/csv", "text/comma-separated-values", "text/plain", ".csv" ) ), hr(), "Please cite as:", br(), "Haddaway, N. R., Page, M. J., Pritchard, C. C., & McGuinness, L. A. (2022). PRISMA2020: An R package and Shiny app for producing PRISMA 2020-compliant flow diagrams, with interactivity for optimised digital transparency and Open Synthesis Campbell Systematic Reviews, 18, e1230.", tags$a( href = "https://doi.org/10.1002/cl2.1230", "https://doi.org/10.1002/cl2.1230" ), br(), tags$a( href = "Haddaway_et_al_2022.ris", "Download citation (.ris)", download = NA, target = "_blank" ) ) ), fluidRow( column( 10, offset = 1, br(), "Credits:", br(), "Neal R Haddaway (creator, author)", br(), "Luke A McGuinness (coder, author)", br(), "Chris C Pritchard (coder, author)", br(), "Matthew J Page (advisor)", br(), "Jack Wasey (advisor)", br(), br(), tags$a( href = "https://github.com/prisma-flowdiagram/PRISMA2020", tags$img( height = 40, width = 40, src = "https://pngimg.com/uploads/github/github_PNG40.png" ) ), "Created November 2020, Updated June 2022" ) ), kofi_load, kofi_show ), # Tab 2 ---- tabPanel( "Create flow diagram", shinyjs::useShinyjs(), sidebarLayout( sidebarPanel( style = "overflow-y:scroll; max-height: 900px; position:relative;", tags$head( tags$style( HTML( ".shiny-split-layout > div { overflow: visible; }" ) ) ), div( id = "options", uiOutput("options") ), hr(), actionButton( "reset", "Click to reset" ), hr(), div( id = "inputs", uiOutput("selection") ), hr(), h3("Download"), downloadButton( "PRISMAflowdiagramPDF", "PDF" ), downloadButton( "PRISMAflowdiagramPNG", "PNG" ), downloadButton( "PRISMAflowdiagramSVG", "SVG" ), downloadButton( "PRISMAflowdiagramHTML", "Interactive HTML" ), downloadButton( "PRISMAflowdiagramZIP", "Interactive HTML (ZIP)" ) ), mainPanel( DiagrammeR::grVizOutput( outputId = "plot1", width = "100%", height = "700px" ) ) ) ), # the below analytics information should be updated to reflect # analytics that you use. It is your responsibility to ensure # compliance with regulations such as the EU GDPR # this section will not be shown unless the PRISMA_ANALYTICS # environment variable is set at runtime anaytics_info <- if (Sys.getenv("PRISMA_ANALYTICS") == TRUE) { tabPanel( "Privacy & Impact", tags$script( "async", "src" = "https://badge.dimensions.ai/badge.js", "charset" = "utf-8" ), tags$script( "type" = "text/javascript", "src" = "https://d1bxh8uas1mnw7.cloudfront.net/assets/embed.js" ), fluidRow( column( width = 10, offset = 1, "We use", tags$a( href = "https://umami.is", "Umami" ), "analytics to identify how our website is used and accessed. We do not collect any personally identifiable data, nor do we use cookies or local browser storage. All data collected for this purpose is anonymised. We also respect the 'do-not-track' header that can be set within your browser preferences.", br(), br(), "The site's usage can be viewed", tags$a( href = "https://umami.christopherpritchard.co.uk/share/DaPFWd0Q/Prisma%20Flow%20Diagram", #nolint "on the public dashboard." ), br(), br(), "RStudio collects data in line with their", tags$a( href = "https://www.rstudio.com/legal/privacy-policy/", "Privacy Policy" ), "for the neccesary functioning of their cloud products, including our hosting provider,", tags$a( href = "https://shinyapps.io", "shinyapps.io" ), br(), br(), hr(), "Our", tags$a( href = "https://doi.org/10.1002/cl2.1230", "article" ), "metrics are:", br() ), ), fluidRow( column(1, offset = 1, tags$div( "class" = "__dimensions_badge_embed__", "data-doi" = "10.1002/cl2.1230", "data-legend" = "hover-right", "data-style" = "small_circle", "width" = "64" ) ), column(1, offset = 0, tags$div( "class" = "altmetric-embed", "data-badge-type" = "donut", "data-doi" = "10.1002/cl2.1230", "width" = "64" ) ), ), fluidRow( column(10, offset = 1, br(), "We also published a", tags$a( href = "https://doi.org/10.1002/cl2.1230", "preprint." ), "Our metrics for the preprint are:", br(), br() ) ), fluidRow( column(1, offset = 1, tags$div( "class" = "__dimensions_badge_embed__", "data-doi" = "10.1101/2021.07.14.21260492", "data-legend" = "hover-right", "data-style" = "small_circle", "width" = "64" ) ), column(1, offset = 0, tags$div( "class" = "altmetric-embed", "data-badge-type" = "donut", "data-doi" = "10.1101/2021.07.14.21260492", "width" = "64" ) ), ) ) } ) ) # Define server logic required to draw a histogram server <- function(input, output, session) { #nolint # Define reactive values rv <- shiny::reactiveValues() # Data Handling ---- # Use template data to populate editable table observe({ if (is.null(input$data_upload)) { # Override default template with query string parameters if present query <- parseQueryString(session$clientData$url_search) if (length(query) > 0) { if ("previous" %in% names(query)) { if (query$previous == 1) { the_options["previous"] <- "Included" } else if (query$previous == 0) { the_options["previous"] <- "Not Included" } } if ("other" %in% names(query)) { if (query$other == 1) { the_options["other"] <- "Included" } else if (query$other == 0) { the_options["other"] <- "Not Included" } } if ("dbDetail" %in% names(query)) { if (query$dbDetail == 1) { the_options["dbDetail"] <- "Included" } else if (query$dbDetail == 0) { the_options["dbDetail"] <- "Not Included" } } if ("regDetail" %in% names(query)) { if (query$regDetail == 1) { the_options["regDetail"] <- "Included" } else if (query$regDetail == 0) { the_options["regDetail"] <- "Not Included" } } if ("metaAnalysis" %in% names(query)) { if (query$metaAnalysis == 1) { the_options["metaAnalysis"] <- "Included" } else if (query$metaAnalysis == 0) { the_options["metaAnalysis"] <- "Not Included" } } for (i in seq_len(nrow(template))) { if (!is.null(query[[template[i, "data"]]])) { template[i, "n"] <- query[[template[i, "data"]]] } } } # Create inital value that is passed to UI rv$data_initial <- template rv$opts_initial <- the_options # Create version that is edited and passed to graphing function rv$data <- template rv$opts <- the_options } else { # Create inital value that is passed to UI rv$data_initial <- read.csv(input$data_upload$datapath) rv$opts_initial <- the_options # Create version that is edited and passed to graphing function rv$data <- read.csv(input$data_upload$datapath) rv$opts <- the_options } }) # Reset to upload button observeEvent( input$reset, { if (is.null(input$data_upload)) { # Create version that is edited and passed to graphing function rv$data <- template } else { # Create version that is edited and passed to graphing function rv$data <- read.csv(input$data_upload$datapath) } shinyjs::reset("inputs") shinyjs::reset("options") } ) # Reset to blank button observeEvent( input$reset_data_upload, { shinyjs::reset("data_upload") } ) # Set up default options output$options <- renderUI({ tagList( h3("Main options"), splitLayout( selectInput( "previous", "Previous studies", choices = c( "Not Included", "Included" ), selected = rv$opts_initial["previous"] ), selectInput( "other", "Other searches for studies", choices = c( "Not Included", "Included" ), selected = rv$opts_initial["other"] ) ), splitLayout( selectInput( "dbDetail", "Individual databases", choices = c( "Not Included", "Included" ), selected = rv$opts_initial["dbDetail"] ), selectInput( "regDetail", "Individual registers", choices = c( "Not Included", "Included" ), selected = rv$opts_initial["regDetail"] ) ), selectInput( "metaAnalysis", "Meta analysis", choices = c( "Not Included", "Included" ), selected = rv$opts_initial["metaAnalysis"] ) ) }) # Set up default values in data entry boxes output$selection <- renderUI({ tagList( h3("Identification"), conditionalPanel( condition = "input.previous == 'Included'", splitLayout( textInput( "previous_studies", label = "Previous studies", value = rv$data_initial[ which(rv$data_initial$data == "previous_studies"), "n" ] ), textInput( "previous_reports", label = "Previous reports", value = rv$data_initial[ which(rv$data_initial$data == "previous_reports"), "n" ] ) ) ), splitLayout( textInput( "database_results", label = "Databases", value = rv$data_initial[ which(rv$data_initial$data == "database_results"), "n" ] ), textInput( "register_results", label = "Registers", value = rv$data_initial[ which(rv$data_initial$data == "register_results"), "n" ] ) ), conditionalPanel( condition = " input.dbDetail == 'Included' || input.regDetail == 'Included' ", splitLayout( textInput( "database_specific_results", label = "Specific Database Results", value = rv$data_initial[ which(rv$data_initial$data == "database_specific_results"), "n" ] ), textInput( "register_specific_results", label = "Specific Register Results", value = rv$data_initial[ which(rv$data_initial$data == "register_specific_results"), "n" ] ) ) ), conditionalPanel( condition = "input.other == 'Included'", splitLayout( textInput( "website_results", label = "Websites", value = rv$data_initial[ which(rv$data_initial$data == "website_results"), "n" ] ), textInput( "organisation_results", label = "Organisations", value = rv$data_initial[ which(rv$data_initial$data == "organisation_results"), "n" ] ) ), textInput( "citations_results", label = "Citations", value = rv$data_initial[ which(rv$data_initial$data == "citations_results"), "n" ] ) ), textInput( "duplicates", label = "Duplicates removed", value = rv$data_initial[ which(rv$data_initial$data == "duplicates"), "n" ] ), splitLayout( textInput( "excluded_automatic", label = "Automatically excluded", value = rv$data_initial[ which(rv$data_initial$data == "excluded_automatic"), "n" ] ), textInput( "excluded_other", label = "Other exclusions", value = rv$data_initial[ which(rv$data_initial$data == "excluded_other"), "n" ] ) ), h3("Screening"), splitLayout( textInput( "records_screened", label = "Records screened", value = rv$data_initial[ which(rv$data_initial$data == "records_screened"), "n" ] ), textInput( "records_excluded", label = "Records excluded", value = rv$data_initial[ which(rv$data_initial$data == "records_excluded"), "n" ] ) ), splitLayout( textInput( "dbr_sought_reports", label = "Reports sought", value = rv$data_initial[ which(rv$data_initial$data == "dbr_sought_reports"), "n" ] ), textInput( "dbr_notretrieved_reports", label = "Reports not retrieved", value = rv$data_initial[ which(rv$data_initial$data == "dbr_notretrieved_reports"), "n" ] ) ), conditionalPanel( condition = "input.other == 'Included'", splitLayout( textInput( "other_sought_reports", label = "Other reports sought", value = rv$data_initial[ which(rv$data_initial$data == "other_sought_reports"), "n" ] ), textInput( "other_notretrieved_reports", label = "Other reports not retrieved", value = rv$data_initial[ which(rv$data_initial$data == "other_notretrieved_reports"), "n" ] ) ) ), splitLayout( textInput( "dbr_assessed", label = "Reports assessed", value = rv$data_initial[ which(rv$data_initial$data == "dbr_assessed"), "n" ] ), textInput( "dbr_excluded", label = "Reports excluded", value = rv$data_initial[ which(rv$data_initial$data == "dbr_excluded"), "n" ] ) ), conditionalPanel( condition = "input.other == 'Included'", splitLayout( textInput( "other_assessed", label = "Other reports assessed", value = rv$data_initial[ which(rv$data_initial$data == "other_assessed"), "n" ] ), textInput( "other_excluded", label = "Other reports excluded", value = rv$data_initial[ which(rv$data_initial$data == "other_excluded"), "n" ] ) ) ), h3("Included"), splitLayout( textInput( "new_studies", label = "New studies", value = rv$data_initial[ which(rv$data_initial$data == "new_studies"), "n" ] ), textInput( "new_reports", label = "New reports", value = rv$data_initial[ which(rv$data_initial$data == "new_reports"), "n" ] ) ), conditionalPanel( condition = "input.metaAnalysis == 'Included'", splitLayout( textInput( "total_studies_ma", label = "Total studies (MA)", value = rv$data_initial[ which(rv$data_initial$data == "total_studies_ma"), "n" ] ), textInput( "total_reports_ma", label = "Total Reports (MA)", value = rv$data_initial[ which(rv$data_initial$data == "total_reports_ma"), "n" ] ) ) ), conditionalPanel( condition = "input.previous == 'Included'", splitLayout( textInput( "total_studies", label = "Total studies", value = rv$data_initial[ which(rv$data_initial$data == "total_studies"), "n" ] ), textInput( "total_reports", label = "Total reports", value = rv$data_initial[ which(rv$data_initial$data == "total_reports"), "n" ] ) ) ) ) }) # Text box observeEvent(input$previous_studies, { rv$data[ which(rv$data$data == "previous_studies"), "n" ] <- input$previous_studies }) observeEvent(input$previous_reports, { rv$data[ which(rv$data$data == "previous_reports"), "n" ] <- input$previous_reports }) observeEvent(input$register_results, { rv$data[ which(rv$data$data == "register_results"), "n" ] <- input$register_results }) observeEvent(input$database_results, { rv$data[ which(rv$data$data == "database_results"), "n" ] <- input$database_results }) observeEvent(input$database_specific_results, { rv$data[ which(rv$data$data == "database_specific_results"), "n" ] <- input$database_specific_results }) observeEvent(input$register_specific_results, { rv$data[ which(rv$data$data == "register_specific_results"), "n" ] <- input$register_specific_results }) observeEvent(input$website_results, { rv$data[ which(rv$data$data == "website_results"), "n" ] <- input$website_results }) observeEvent(input$organisation_results, { rv$data[ which(rv$data$data == "organisation_results"), "n" ] <- input$organisation_results }) observeEvent(input$citations_results, { rv$data[ which(rv$data$data == "citations_results"), "n" ] <- input$citations_results }) observeEvent(input$duplicates, { rv$data[ which(rv$data$data == "duplicates"), "n" ] <- input$duplicates }) observeEvent(input$excluded_automatic, { rv$data[ which(rv$data$data == "excluded_automatic"), "n" ] <- input$excluded_automatic }) observeEvent(input$excluded_other, { rv$data[ which(rv$data$data == "excluded_other"), "n" ] <- input$excluded_other }) observeEvent(input$records_screened, { rv$data[ which(rv$data$data == "records_screened"), "n" ] <- input$records_screened }) observeEvent(input$records_excluded, { rv$data[ which(rv$data$data == "records_excluded"), "n" ] <- input$records_excluded }) observeEvent(input$dbr_sought_reports, { rv$data[ which(rv$data$data == "dbr_sought_reports"), "n" ] <- input$dbr_sought_reports }) observeEvent(input$dbr_notretrieved_reports, { rv$data[ which(rv$data$data == "dbr_notretrieved_reports"), "n" ] <- input$dbr_notretrieved_reports }) observeEvent(input$other_sought_reports, { rv$data[ which(rv$data$data == "other_sought_reports"), "n" ] <- input$other_sought_reports }) observeEvent(input$other_notretrieved_reports, { rv$data[ which(rv$data$data == "other_notretrieved_reports"), "n" ] <- input$other_notretrieved_reports }) observeEvent(input$dbr_assessed, { rv$data[ which(rv$data$data == "dbr_assessed"), "n" ] <- input$dbr_assessed }) observeEvent(input$dbr_excluded, { rv$data[ which(rv$data$data == "dbr_excluded"), "n" ] <- input$dbr_excluded }) observeEvent(input$other_assessed, { rv$data[ which(rv$data$data == "other_assessed"), "n" ] <- input$other_assessed }) observeEvent(input$other_excluded, { rv$data[ which(rv$data$data == "other_excluded"), "n" ] <- input$other_excluded }) observeEvent(input$new_studies, { rv$data[ which(rv$data$data == "new_studies"), "n" ] <- input$new_studies }) observeEvent(input$new_reports, { rv$data[ which(rv$data$data == "new_reports"), "n" ] <- input$new_reports }) observeEvent(input$total_studies, { rv$data[ which(rv$data$data == "total_studies"), "n" ] <- input$total_studies }) observeEvent(input$total_reports, { rv$data[ which(rv$data$data == "total_reports"), "n" ] <- input$total_reports }) observeEvent(input$total_studies_ma, { rv$data[ which(rv$data$data == "total_studies_ma"), "n" ] <- input$total_studies_ma }) observeEvent(input$total_reports_ma, { rv$data[ which(rv$data$data == "total_reports_ma"), "n" ] <- input$total_reports_ma }) observeEvent(input$previous, { rv$opts["previous"] <- input$previous }) observeEvent(input$other, { rv$opts["other"] <- input$other }) observeEvent(input$dbDetail, { rv$opts["dbDetail"] <- input$dbDetail }) observeEvent(input$regDetail, { rv$opts["regDetail"] <- input$regDetail }) observeEvent(input$metaAnalysis, { rv$opts["metaAnalysis"] <- input$metaAnalysis }) # Define table proxy proxy <- DT::dataTableProxy("mytable") # Update reactive dataset on cell edit observeEvent( input$mytable_cell_edit, { info <- input$mytable_cell_edit # Define edited row i <- info$row # Define edited column (column index offset by 4, because you are hiding # the rownames column and the first 3 columns of the data) j <- info$col + 4L # Define value of edit v <- info$value # Pass edited value to appropriate cell of data stored in rv$data rv$data[i, j] <- shiny::coerceValue(v, rv$data[i, j]) # Replace data in table with updated data stored in rv$data replaceData( proxy, rv$data, resetPaging = FALSE, rownames = FALSE) # important }) # Define thank you modal thank_you_modal <- modalDialog( easyClose = TRUE, title = "Thank You", "Thank you for using the PRISMA Flow Diagram tool. Your flow diagram is being downloaded.", hr(), "Please remember to cite the tool as: ", br(), "Haddaway, N. R., Page, M. J., Pritchard, C. C., & McGuinness, L. A. (2022). PRISMA2020: An R package and Shiny app for producing PRISMA 2020-compliant flow diagrams, with interactivity for optimised digital transparency and Open Synthesis Campbell Systematic Reviews, 18, e1230.", tags$a( href = "https://doi.org/10.1002/cl2.1230", "https://doi.org/10.1002/cl2.1230" ), br(), tags$a( href = "Haddaway_et_al_2022.ris", "Download citation (.ris)", download = NA, target = "_blank" ) ) # Reactive plot ---- # Create plot plot <- reactive({ data <- PRISMA2020::PRISMA_data(rv$data) if (rv$opts["previous"] == "Included") { include_previous <- TRUE } else { include_previous <- FALSE } if (rv$opts["other"] == "Included") { include_other <- TRUE } else { include_other <- FALSE } if (rv$opts["dbDetail"] == "Included") { detail_databases <- TRUE } else { detail_databases <- FALSE } if (rv$opts["regDetail"] == "Included") { detail_registers <- TRUE } else { detail_registers <- FALSE } if (rv$opts["metaAnalysis"] == "Included"){ meta_analysis <- TRUE } else { meta_analysis <- FALSE } shinyjs::runjs( paste0( 'const nodeMap = new Map([["node1","', rv$data[which(rv$data$data == "identification"), "boxtext"], '"], ["node2","', rv$data[which(rv$data$data == "screening"), "boxtext"], '"], ["node3","', rv$data[which(rv$data$data == "included"), "boxtext"], '"]])', "\n", "createLabels(nodeMap)" ) ) plot <- PRISMA2020::PRISMA_flowdiagram( data, fontsize = 12, font = "Helvetica", interactive = TRUE, previous = include_previous, other = include_other, meta_analysis = meta_analysis, side_boxes = TRUE, detail_databases = detail_databases, detail_registers = detail_registers ) }) # Display plot output$plot1 <- DiagrammeR::renderDiagrammeR({ plot <- plot() }) # Handle downloads ---- output$PRISMAflowdiagramPDF <- downloadHandler( #nolint filename = "prisma.pdf", content = function(file) { showModal( thank_you_modal ) PRISMA2020::PRISMA_save(plot(), filename = file, filetype = "PDF") } ) output$PRISMAflowdiagramPNG <- downloadHandler( #nolint filename = "prisma.png", content = function(file) { showModal( thank_you_modal ) PRISMA2020::PRISMA_save(plot(), filename = file, filetype = "PNG") } ) output$PRISMAflowdiagramSVG <- downloadHandler( #nolint filename = "prisma.svg", content = function(file) { showModal( thank_you_modal ) PRISMA2020::PRISMA_save(plot(), filename = file, filetype = "SVG") } ) output$PRISMAflowdiagramHTML <- downloadHandler( #nolint filename = "prisma.html", content = function(file) { showModal( thank_you_modal ) PRISMA2020::PRISMA_save(plot(), filename = file, filetype = "html") } ) output$PRISMAflowdiagramZIP <- downloadHandler( #nolint filename = "prisma.zip", content = function(file) { showModal( thank_you_modal ) PRISMA2020::PRISMA_save(plot(), filename = file, filetype = "zip") } ) } # Run the application shinyApp(ui = ui, server = server) ================================================ FILE: inst/shiny-examples/PRISMA_flowdiagram/rsconnect/shinyapps.io/estech/prisma_flowdiagram.dcf ================================================ name: prisma_flowdiagram title: username: account: estech server: shinyapps.io hostUrl: https://api.shinyapps.io/v1 appId: 3345280 bundleId: 5842784 url: https://estech.shinyapps.io/prisma_flowdiagram/ when: 1651068649.29361 lastSyncTime: 1651068649.29363 ================================================ FILE: inst/shiny-examples/PRISMA_flowdiagram/www/Haddaway_et_al_2022.ris ================================================ TY - JOUR T1 - PRISMA2020: An R package and Shiny app for producing PRISMA 2020-compliant flow diagrams, with interactivity for optimised digital transparency and Open Synthesis AU - Haddaway, Neal R. AU - Page, Matthew J. AU - Pritchard, Chris C. AU - McGuinness, Luke A. Y1 - 2022/06/01 PY - 2022 DA - 2022/06/01 N1 - https://doi.org/10.1002/cl2.1230 DO - https://doi.org/10.1002/cl2.1230 T2 - Campbell Systematic Reviews JF - Campbell Systematic Reviews JO - Campbell Systematic Reviews JA - Campbell Systematic Reviews SP - e1230 VL - 18 IS - 2 PB - John Wiley & Sons, Ltd SN - 1891-1803 M3 - https://doi.org/10.1002/cl2.1230 UR - https://doi.org/10.1002/cl2.1230 Y2 - 2022/05/06 N2 - Abstract Background Reporting standards, such as PRISMA aim to ensure that the methods and results of systematic reviews are described in sufficient detail to allow full transparency. Flow diagrams in evidence syntheses allow the reader to rapidly understand the core procedures used in a review and examine the attrition of irrelevant records throughout the review process. Recent research suggests that use of flow diagrams in systematic reviews is poor and of low quality and called for standardised templates to facilitate better reporting in flow diagrams. The increasing options for interactivity provided by the Internet gives us an opportunity to support easy-to-use evidence synthesis tools, and here we report on the development of a tool for the production of PRISMA 2020-compliant systematic review flow diagrams. Methods and Findings We developed a free-to-use, Open Source R package and web-based Shiny app to allow users to design PRISMA flow diagrams for their own systematic reviews. Our tool allows users to produce standardised visualisations that transparently document the methods and results of a systematic review process in a variety of formats. In addition, we provide the opportunity to produce interactive, web-based flow diagrams (exported as HTML files), that allow readers to click on boxes of the diagram and navigate to further details on methods, results or data files. We provide an interactive example here; https://prisma-flowdiagram.github.io/. Conclusions We have developed a user-friendly tool for producing PRISMA 2020-compliant flow diagrams for users with coding experience and, importantly, for users without prior experience in coding by making use of Shiny (https://estech.shinyapps.io/prisma_flowdiagram/). This free-to-use tool will make it easier to produce clear and PRISMA 2020-compliant systematic review flow diagrams. Significantly, users can also produce interactive flow diagrams for the first time, allowing readers of their reviews to smoothly and swiftly explore and navigate to further details of the methods and results of a review. We believe this tool will increase use of PRISMA flow diagrams, improve the compliance and quality of flow diagrams, and facilitate strong science communication of the methods and results of systematic reviews by making use of interactivity. We encourage the systematic review community to make use of the tool, and provide feedback to streamline and improve their usability and efficiency. ER - ================================================ FILE: inst/shiny-examples/PRISMA_flowdiagram/www/PRISMA.csv ================================================ data,node,box,description,boxtext,tooltips,url,n NA,node4,prevstud,Grey title box; Previous studies,Previous studies,Grey title box; Previous studies,prevstud.html,0 previous_studies,node5,box1,Studies included in previous version of review,Studies included in previous version of review,Studies included in previous version of review,previous_studies.html,0 previous_reports,NA,box1,Reports of studies included in previous version of review,Reports of studies included in previous version of review,NA,previous_reports.html,0 NA,node6,newstud,Yellow title box; Identification of new studies via databases and registers,Identification of new studies via databases and registers,Yellow title box; Identification of new studies via databases and registers,newstud.html,0 database_results,node7,box2,Records identified from: Databases,Databases,Records identified from: Databases and Registers,database_results.html,0 database_specific_results,NA,box2,Records identified from: specific databases,Specific Databases,NA,database_results.html,"Database 1, xxx; Database 2, xxx; Database 3, xxx" register_results,NA,box2,Records identified from: Registers,Registers,NA,NA,0 register_specific_results,NA,box2,Records identified from: specific registers,Specific Registers,NA,database_results.html,"Register 1, xxx; Register 2, xxx; Register 3, xxx" NA,node16,othstud,Grey title box; Identification of new studies via other methods,Identification of new studies via other methods,Grey title box; Identification of new studies via other methods,othstud.html,0 website_results,node17,box11,Records identified from: Websites,Websites,"Records identified from: Websites, Organisations and Citation Searching",website_results.html,0 organisation_results,,box11,Records identified from: Organisations,Organisations,NA,NA,0 citations_results,NA,box11,Records identified from: Citation searching,Citation searching,NA,NA,0 duplicates,node8,box3,Duplicate records,Duplicate records,Duplicate records,duplicates.html,0 excluded_automatic,NA,box3,Records marked as ineligible by automation tools,Records marked as ineligible by automation tools,NA,NA,0 excluded_other,NA,box3,Records removed for other reasons,Records removed for other reasons,NA,NA,0 records_screened,node9,box4,Records screened (databases and registers),Records screened,Records screened (databases and registers),records_screened.html,0 records_excluded,node10,box5,Records excluded (databases and registers),Records excluded,Records excluded (databases and registers),records_excluded.html,0 dbr_sought_reports,node11,box6,Reports sought for retrieval (databases and registers),Reports sought for retrieval,Reports sought for retrieval (databases and registers),dbr_sought_reports.html,0 dbr_notretrieved_reports,node12,box7,Reports not retrieved (databases and registers),Reports not retrieved,Reports not retrieved (databases and registers),dbr_notretrieved_reports.html,0 other_sought_reports,node18,box12,Reports sought for retrieval (other),Reports sought for retrieval,Reports sought for retrieval (other),other_sought_reports.html,0 other_notretrieved_reports,node19,box13,Reports not retrieved (other),Reports not retrieved,Reports not retrieved (other),other_notretrieved_reports.html,0 dbr_assessed,node13,box8,Reports assessed for eligibility (databases and registers),Reports assessed for eligibility,Reports assessed for eligibility (databases and registers),dbr_assessed.html,0 dbr_excluded,node14,box9,"Reports excluded (databases and registers): [separate reasons and numbers using ; e.g. Reason1, xxx; Reason2, xxx; Reason3, xxx]",Reports excluded,Reports excluded (databases and registers),dbrexcludedrecords.html,"Reason1, xxx; Reason2, xxx; Reason3, xxx" other_assessed,node20,box14,Reports assessed for eligibility (other),Reports assessed for eligibility,Reports assessed for eligibility (other),other_assessed.html,0 other_excluded,node21,box15,"Reports excluded (other): [separate reasons and numbers using ; e.g. Reason1, xxx; Reason2, xxx; Reason3, xxx]",Reports excluded,Reports excluded (other),other_excluded.html,"Reason1, xxx; Reason2, xxx; Reason3, xxx" new_studies,node15,box10,New studies included in review,New studies included in review,New studies included in review,new_studies.html,0 new_reports,NA,box10,Reports of new included studies,Reports of new included studies,NA,NA,0 total_studies,node22,box16,Total studies included in review,Total studies included in review,Total studies included in review,total_studies.html,0 total_reports,NA,box16,Reports of total included studies,Reports of total included studies,NA,NA,0 identification,node1,identification,Blue identification box,Identification,Blue identification box,identification.html,0 screening,node2,screening,Blue screening box,Screening,Blue screening box,screening.html,0 included,node3,included,Blue included box,Included,Blue included box,included.html,0 total_studies_ma,node23,box17,Total studies included in meta-analysis,Total studies included in meta-analysis,Total studies included in meta-analysis,total_studies_meta_analysis.html,0 total_reports_ma,NA,box17,Reports of total included studies in meta-analysis,Reports of total included studies in meta-analysis,NA,NA,0 ================================================ FILE: inst/shiny-examples/PRISMA_flowdiagram/www/kofi.js ================================================ /** * Ko-fi button */ kofiWidgetOverlay.draw( 'chriscpritchard', { 'type': 'floating-chat', 'floating-chat.donateButton.text': 'Support Us', 'floating-chat.donateButton.background-color': '#5bc0de', 'floating-chat.donateButton.text-color': '#323842' } ); ================================================ FILE: inst/shiny-examples/PRISMA_flowdiagram/www/labels.js ================================================ /** * @param {HTMLElement} node the node to label * @param {string} label the label for the node */ function renderLabel(node, label) { var theText = node.querySelector("text"); var attrX = theText.getAttribute("x"); var attrY = theText.getAttribute("y"); theText.setAttribute("y",parseFloat(attrX)) theText.setAttribute("x",parseFloat(attrY)*-1) theText.setAttribute("style","transform: rotate(-90deg);") theText.setAttribute("dominant-baseline", "middle") theText.innerHTML = label; } // https://stackoverflow.com/questions/38881301/observe-mutations-on-a-target-node-that-doesnt-exist-yet/38882022 /** * @param {string} id the ID of the node to wait for * @param {HTMLElement} parent the parent of the node * @param {boolean} recursive whether or not to recurse * @param {function(HTMLElement, string)} done the callback function to execute when the node appears */ function waitForAddedNode(id, parent, recursive, done) { new MutationObserver((mutations, observer) => { var el = document.getElementById(id) if (el) { observer.disconnect() done(el) } }).observe(parent || document, { subtree: !!recursive || !parent, childList: true }) } /** * @param {Map } nodeMap a map of nodes and their labels * @return {MutationObserver} a mutation observer to label the node with the label whenever it changes */ function createLabels(nodeMap) { for (const [node, label] of nodeMap) { waitForAddedNode(node, document.getElementById("plot1"), true, (node) => { renderLabel(node, label) }) } } ================================================ FILE: man/PRISMA_add_hyperlink_.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{PRISMA_add_hyperlink_} \alias{PRISMA_add_hyperlink_} \title{Add the hyperlink to the given node} \usage{ PRISMA_add_hyperlink_(node, url) } \arguments{ \item{node}{the relevent node} \item{url}{the URL the node should link to} } \value{ An interactive flow diagram plot. } \description{ Generate the javascript method to insert the side labels } \keyword{internal} ================================================ FILE: man/PRISMA_calc_filetype_.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{PRISMA_calc_filetype_} \alias{PRISMA_calc_filetype_} \title{Calculate the correct filetime} \usage{ PRISMA_calc_filetype_(fn, ft) } \arguments{ \item{fn}{The filename (including extension)} \item{ft}{The filetype (which can be NA or NULL)} } \value{ the filetype taken from the filename, or overriden by the ft param } \description{ Work out the correct filetype to save the file as } \keyword{internal} ================================================ FILE: man/PRISMA_data.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/PRISMA_flowdiagram.R \name{PRISMA_data} \alias{PRISMA_data} \title{Read in PRISMA flow diagram data} \usage{ PRISMA_data(data) } \arguments{ \item{data}{File to read in.} } \value{ A list of objects needed to plot the flow diagram } \description{ Read in a template CSV containing data for the flow diagram } \examples{ csvFile <- system.file("extdata", "PRISMA.csv", package = "PRISMA2020") data <- read.csv(csvFile); data <- PRISMA_data(data); } ================================================ FILE: man/PRISMA_flowdiagram.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/PRISMA_flowdiagram.R \name{PRISMA_flowdiagram} \alias{PRISMA_flowdiagram} \title{Plot interactive flow diagrams for systematic reviews} \usage{ PRISMA_flowdiagram( data, interactive = FALSE, previous = TRUE, other = TRUE, detail_databases = FALSE, detail_registers = FALSE, meta_analysis = FALSE, fontsize = 7, font = "Helvetica", title_colour = "Goldenrod1", greybox_colour = "Gainsboro", main_colour = "Black", arrow_colour = "Black", arrow_head = "normal", arrow_tail = "none", side_boxes = TRUE ) } \arguments{ \item{data}{List of data inputs including numbers of studies, box text, tooltips, and urls for hyperlinks. Data inputted via the \code{\link[=PRISMA_data]{PRISMA_data()}} function. If inputting individually, see the necessary parameters listed in the \code{\link[=PRISMA_data]{PRISMA_data()}}) function and combine them in a list using \code{data <- list()}.} \item{interactive}{Logical argument TRUE or FALSE whether to plot interactivity (tooltips and hyperlinked boxes).} \item{previous}{Logical argument (TRUE or FALSE) specifying whether previous studies were sought.} \item{other}{Logical argument (TRUE or FALSE) specifying whether other studies were sought.} \item{detail_databases}{Logical argument (TRUE or FALSE) specifying whether to list specific databases.} \item{detail_registers}{Logical argument (TRUE or FALSE) specifying whether to list specific registers.} \item{meta_analysis}{Logical argument (TRUE or FALSE) specifying whether to display a box for meta-analysis} \item{fontsize}{The font size for text in each box. The default is '12'.} \item{font}{The font for text in each box. The default is 'Helvetica'.} \item{title_colour}{The colour for the upper middle title box (new studies). The default is 'Goldenrod1'. See 'DiagrammeR' colour scheme. \url{http://rich-iannone.github.io/DiagrammeR/graphviz_and_mermaid.html#colors}.} \item{greybox_colour}{The colour for the left and right column boxes. The default is 'Gainsboro'. See 'DiagrammeR' colour scheme \url{http://rich-iannone.github.io/DiagrammeR/graphviz_and_mermaid.html#colors}.} \item{main_colour}{The colour for the main box borders. The default is 'Black'. See 'DiagrammeR' colour scheme \url{http://rich-iannone.github.io/DiagrammeR/graphviz_and_mermaid.html#colors}.} \item{arrow_colour}{The colour for the connecting lines. The default is 'Black'. See 'DiagrammeR' colour scheme \url{http://rich-iannone.github.io/DiagrammeR/graphviz_and_mermaid.html#colors}.} \item{arrow_head}{The head shape for the line connectors. The default is 'normal'. See DiagrammeR arrow shape specification \url{http://rich-iannone.github.io/DiagrammeR/graphviz_and_mermaid.html#arrow-shapes}. #nolint} \item{arrow_tail}{The tail shape for the line connectors. The default is 'none'. See DiagrammeR arrow shape specification \url{http://rich-iannone.github.io/DiagrammeR/graphviz_and_mermaid.html#arrow-shapes}. #nolint} \item{side_boxes}{Whether or not to include the blue label boxes along the side} } \value{ A flow diagram plot. } \description{ Produces a PRISMA2020 style flow diagram for systematic reviews, with the option to add interactivity through tooltips (mouseover popups) and hyperlink URLs to each box. Data can be imported from the standard CSV template provided. } \examples{ csvFile <- system.file("extdata", "PRISMA.csv", package = "PRISMA2020") data <- read.csv(csvFile); data <- PRISMA_data(data); plot <- PRISMA_flowdiagram(data, fontsize = 12, interactive = TRUE, previous = FALSE, other = TRUE); plot } ================================================ FILE: man/PRISMA_format_number_.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{PRISMA_format_number_} \alias{PRISMA_format_number_} \title{Format numbers with commas into numbers} \usage{ PRISMA_format_number_(x) } \arguments{ \item{x}{the number to format} } \value{ the number with commas removed } \description{ Turn strings containing numbers +/- commas into numbers } \keyword{internal} ================================================ FILE: man/PRISMA_format_reasons_.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{PRISMA_format_reasons_} \alias{PRISMA_format_reasons_} \title{Formats multiple exclusion reasons properly for printing} \usage{ PRISMA_format_reasons_(df) } \arguments{ \item{df}{the dataframe to parse} } \value{ a string ready for printing } \description{ Parse an exclusion reason dataframe from \code{\link[=PRISMA_parse_reasons_]{PRISMA_parse_reasons_()}} and returns a properly formatted string } \keyword{internal} ================================================ FILE: man/PRISMA_gen_tmp_svg_.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{PRISMA_gen_tmp_svg_} \alias{PRISMA_gen_tmp_svg_} \title{Generate a temporary SVG from a plot object} \usage{ PRISMA_gen_tmp_svg_(obj) } \arguments{ \item{obj}{the plot object} } \value{ the full path to the saved SVG } \description{ Generate and save a temporary SVG from a plot object } \keyword{internal} ================================================ FILE: man/PRISMA_get_height_.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{PRISMA_get_height_} \alias{PRISMA_get_height_} \title{Calculate the correct height of a box from a list (e.g. of exclusion reasons)} \usage{ PRISMA_get_height_(n, offset, min = 2) } \arguments{ \item{n}{the number of rows of text in the label} \item{offset}{the offset height (e.g. 3.5)} \item{min}{the minimum number of rows before adjusting} } \value{ the height of the box } \description{ Get the correct height for a box } \keyword{internal} ================================================ FILE: man/PRISMA_get_pos_.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{PRISMA_get_pos_} \alias{PRISMA_get_pos_} \title{Calculate the correct position of a node} \usage{ PRISMA_get_pos_( first_box_location, offset, length_orig, length_new, negative_offset = FALSE ) } \arguments{ \item{first_box_location}{the location of the first node} \item{offset}{the offset from the first node} \item{length_orig}{the width/height of the original node} \item{length_new}{the width/height of the new node} \item{negative_offset}{is the offset negative (defaults to false)} } \value{ the position of the node } \description{ Get the correct position for a node } \keyword{internal} ================================================ FILE: man/PRISMA_insert_js_.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{PRISMA_insert_js_} \alias{PRISMA_insert_js_} \title{Generate / insert JS for labels} \usage{ PRISMA_insert_js_(plot, identification_text, screening_text, included_text) } \arguments{ \item{plot}{the plot object (without side labels)} \item{identification_text}{the text to use as the "identification" label} \item{screening_text}{the text to use as the "screening" label} \item{included_text}{the text to use as the "identification" label} } \value{ the plot object (with JS to generate side labels) } \description{ Generate the javascript method to insert the side labels } \keyword{internal} ================================================ FILE: man/PRISMA_interactive_.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{PRISMA_interactive_} \alias{PRISMA_interactive_} \title{Plot interactive flow diagram for systematic reviews} \usage{ PRISMA_interactive_(plot, urls, previous, other) } \arguments{ \item{plot}{A plot object from \code{\link[=PRISMA_flowdiagram]{PRISMA_flowdiagram()}}.} \item{urls}{A dataframe consisting of two columns: nodes and urls. The first column should contain 19 rows for the nodes from node1 to node19. The second column should contain a corresponding URL for each node.} \item{previous}{Logical argument (TRUE or FALSE) (supplied through \code{\link[=PRISMA_flowdiagram]{PRISMA_flowdiagram()}}) specifying whether previous studies were sought.} \item{other}{Logical argument (TRUE or FALSE) (supplied through \code{\link[=PRISMA_flowdiagram]{PRISMA_flowdiagram()}}) specifying whether other studies were sought.} } \value{ An interactive flow diagram plot. } \description{ Converts a PRISMA systematic review flow diagram into an interactive HTML plot, for embedding links from each box. } \seealso{ \code{\link[=PRISMA_interactive_]{PRISMA_interactive_()}} } \keyword{internal} ================================================ FILE: man/PRISMA_parse_reasons_.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{PRISMA_parse_reasons_} \alias{PRISMA_parse_reasons_} \title{Parse an exclusion reason into a data frame} \usage{ PRISMA_parse_reasons_(reasons) } \arguments{ \item{reasons}{the string to parse} } \value{ a dataframe containing reasons and number applicable } \description{ Parse an exclusion reason string and returns a dataframe containing reasons and number } \keyword{internal} ================================================ FILE: man/PRISMA_save.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/PRISMA_flowdiagram.R \name{PRISMA_save} \alias{PRISMA_save} \title{Save PRISMA2020 flow diagram} \usage{ PRISMA_save( plotobj, filename = "PRISMA2020_flowdiagram.html", filetype = NA, overwrite = FALSE, width = NULL, height = NULL, css = NULL ) } \arguments{ \item{plotobj}{A plot produced using \code{\link[=PRISMA_flowdiagram]{PRISMA_flowdiagram()}}.} \item{filename}{The filename to save (including extension)} \item{filetype}{The filetype to save the plot in, supports: HTML, ZIP, PDF, PNG, SVG, PS and WEBP (if NA, the filetype will be calculated out based on the file extension) HTML files maintain hyperlinks and tooltips The ZIP option creates an archive containing the HTML file, alongside supporting javascript and css files in an adjacent folder, instead of embedded base64 within the HTML file} \item{overwrite}{if TRUE, will overwrite an existing file} \item{width}{passed as the width argument to \code{\link[rsvg:rsvg]{rsvg::rsvg()}} and similar functions} \item{height}{passed as the height argument to \code{\link[rsvg:rsvg]{rsvg::rsvg()}} and similar functions} \item{css}{passed as the css argument to \code{\link[rsvg:rsvg]{rsvg::rsvg()}} and similar functions} } \value{ the absolute filename of the saved diagram plot. } \description{ Save the output from \code{\link[=PRISMA_flowdiagram]{PRISMA_flowdiagram()}} to the working directory. } \examples{ csvFile <- system.file("extdata", "PRISMA.csv", package = "PRISMA2020") data <- read.csv(csvFile); data <- PRISMA_data(data); plot <- PRISMA_flowdiagram(data, fontsize = 12, interactive = TRUE, previous = FALSE, other = TRUE); PRISMA_save(plot, filename = tempfile(), filetype="html"); } ================================================ FILE: man/read_PRISMAdata.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/PRISMA_flowdiagram.R \name{read_PRISMAdata} \alias{read_PRISMAdata} \title{Defunct function - replaced by "PRISMA_data"} \usage{ read_PRISMAdata(data) } \arguments{ \item{data}{File to read in.} } \description{ Defunct function - replaced by "PRISMA_data" } \seealso{ \code{\link[=PRISMA_data]{PRISMA_data()}} } ================================================ FILE: man/sr_flow_interactive.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/PRISMA_flowdiagram.R \name{sr_flow_interactive} \alias{sr_flow_interactive} \title{Defunct function - replaced by "PRISMA_interactive_"} \usage{ sr_flow_interactive(plot, urls, previous, other) } \arguments{ \item{plot}{A plot object from \code{\link[=PRISMA_flowdiagram]{PRISMA_flowdiagram()}}.} \item{urls}{A dataframe consisting of two columns: nodes and urls. The first column should contain 19 rows for the nodes from node1 to node19. The second column should contain a corresponding URL for each node.} \item{previous}{Logical argument (TRUE or FALSE) (supplied through \code{\link[=PRISMA_flowdiagram]{PRISMA_flowdiagram()}}) specifying whether previous studies were sought.} \item{other}{Logical argument (TRUE or FALSE) (supplied through \code{\link[=PRISMA_flowdiagram]{PRISMA_flowdiagram()}}) specifying whether other studies were sought.} } \description{ Defunct function - replaced by "PRISMA_interactive_" } \seealso{ \code{\link[=PRISMA_interactive_]{PRISMA_interactive_()}} } ================================================ FILE: shiny-server.conf ================================================ run_as shiny; # Define a server that listens on port 3838 server { listen 3838; # Define a location at the base URL location / { # Host the directory of Shiny Apps stored in this directory site_dir /srv/shiny-server/prisma; # Log all Shiny output to files in this directory log_dir /var/log/shiny-server; # Disable directory listing directory_index off; } }