├── .github
├── .gitignore
└── workflows
│ ├── check-standard.yaml
│ └── test-coverage.yaml
├── .gitignore
├── .project
├── README.md
├── editbl.gif
├── editbl
├── DESCRIPTION
├── NAMESPACE
├── R
│ ├── demoApp.R
│ ├── devApp.R
│ ├── eDT.R
│ ├── eDT_app.R
│ ├── foreignTbl.R
│ ├── selectInputDT.R
│ ├── shinyInput.R
│ ├── tbl.R
│ ├── tbl_dbi.R
│ ├── tbl_df.R
│ ├── tbl_dtplyr_step.R
│ └── utils.R
├── README.md
├── inst
│ ├── NEWS
│ └── extdata
│ │ ├── artists.xlsx
│ │ └── chinook.sqlite
├── man
│ ├── addButtons.Rd
│ ├── beginTransaction.Rd
│ ├── canXXXRowTemplate.Rd
│ ├── castForDisplay.Rd
│ ├── castFromTbl.Rd
│ ├── castToFactor.Rd
│ ├── castToSQLSupportedType.Rd
│ ├── castToTbl.Rd
│ ├── castToTemplate.Rd
│ ├── checkForeignTbls.Rd
│ ├── coalesce.Rd
│ ├── coerceColumns.Rd
│ ├── coerceValue.Rd
│ ├── commitTransaction.Rd
│ ├── connectDB.Rd
│ ├── createButtons.Rd
│ ├── createCloneButtonHTML.Rd
│ ├── createCloneButtonHTML_shiny.Rd
│ ├── createDeleteButtonHTML.Rd
│ ├── createDeleteButtonHTML_shiny.Rd
│ ├── createEditButtonHTML.Rd
│ ├── createEditButtonHTML_shiny.Rd
│ ├── customButton.Rd
│ ├── demoServer_DB.Rd
│ ├── demoServer_custom.Rd
│ ├── demoServer_mtcars.Rd
│ ├── demoUI_DB.Rd
│ ├── demoUI_custom.Rd
│ ├── demoUI_mtcars.Rd
│ ├── devServer.Rd
│ ├── devUI.Rd
│ ├── disableDoubleClickButtonCss.Rd
│ ├── eDT.Rd
│ ├── eDTOutput.Rd
│ ├── eDT_app.Rd
│ ├── eDT_app_server.Rd
│ ├── eDT_app_ui.Rd
│ ├── e_rows_insert.Rd
│ ├── e_rows_insert.default.Rd
│ ├── e_rows_insert.dtplyr_step.Rd
│ ├── e_rows_insert.tbl_dbi.Rd
│ ├── e_rows_update.Rd
│ ├── e_rows_update.data.frame.Rd
│ ├── e_rows_update.default.Rd
│ ├── e_rows_update.dtplyr_step.Rd
│ ├── e_rows_update.tbl_dbi.Rd
│ ├── evalCanCloneRow.Rd
│ ├── evalCanDeleteRow.Rd
│ ├── evalCanEditRow.Rd
│ ├── fillDeductedColumns.Rd
│ ├── fixInteger64.Rd
│ ├── foreignTbl.Rd
│ ├── getColumnTypeSums.Rd
│ ├── getNonNaturalKeyCols.Rd
│ ├── get_db_table_name.Rd
│ ├── initData.Rd
│ ├── inputServer.Rd
│ ├── inputServer.default.Rd
│ ├── inputUI.Rd
│ ├── inputUI.default.Rd
│ ├── joinForeignTbl.Rd
│ ├── overwriteDefaults.Rd
│ ├── rollbackTransaction.Rd
│ ├── rowInsert.Rd
│ ├── rowUpdate.Rd
│ ├── rows_delete.dtplyr_step.Rd
│ ├── runDemoApp.Rd
│ ├── runDemoApp_DB.Rd
│ ├── runDemoApp_custom.Rd
│ ├── runDemoApp_mtcars.Rd
│ ├── runDevApp.Rd
│ ├── selectInputDT_Server.Rd
│ ├── selectInputDT_UI.Rd
│ ├── shinyInput.Rd
│ ├── standardizeArgument_colnames.Rd
│ ├── standardizeArgument_editable.Rd
│ └── whereSQL.Rd
├── tests
│ ├── testthat.R
│ └── testthat
│ │ ├── test-demoApp.R
│ │ ├── test-devApp.R
│ │ ├── test-eDT.R
│ │ ├── test-foreignTbl.R
│ │ ├── test-selectInputDT.R
│ │ ├── test-shinyInput.R
│ │ ├── test-tbl.R
│ │ ├── test-tbl_dbi.R
│ │ ├── test-tbl_df.R
│ │ ├── test-tbl_dtplyr_step.R
│ │ └── test-utils.R
└── vignettes
│ ├── howto_relational_db.rmd
│ ├── howto_relational_db_dm.rmd
│ ├── howto_row_level_access.rmd
│ ├── howto_switch_from_DT.rmd
│ └── screenshots
│ ├── howto_relational_db_1.png
│ ├── howto_relational_db_2.png
│ ├── howto_relational_db_3.png
│ ├── howto_relational_db_dm_1.png
│ ├── howto_row_level_access_1.png
│ ├── howto_row_level_access_2.png
│ ├── howto_switch_from_DT_1.png
│ └── howto_switch_from_DT_2.png
├── editbl_logo.drawio
├── editbl_logo.png
└── editbl_logo.svg
/.github/.gitignore:
--------------------------------------------------------------------------------
1 | *.html
2 |
--------------------------------------------------------------------------------
/.github/workflows/check-standard.yaml:
--------------------------------------------------------------------------------
1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3 | on:
4 | push:
5 | branches: [main, master]
6 | pull_request:
7 | branches: [main, master]
8 |
9 | name: R-CMD-check
10 |
11 | jobs:
12 | R-CMD-check:
13 | runs-on: ${{ matrix.config.os }}
14 |
15 | name: ${{ matrix.config.os }} (${{ matrix.config.r }})
16 |
17 | strategy:
18 | fail-fast: false
19 | matrix:
20 | config:
21 | - {os: macos-latest, r: 'release'}
22 | - {os: windows-latest, r: 'release'}
23 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'}
24 | - {os: ubuntu-latest, r: 'release'}
25 | - {os: ubuntu-latest, r: 'oldrel-1'}
26 |
27 | env:
28 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
29 | R_KEEP_PKG_SOURCE: yes
30 |
31 | steps:
32 | - uses: actions/checkout@v3
33 |
34 | - uses: r-lib/actions/setup-pandoc@v2
35 |
36 | - uses: r-lib/actions/setup-r@v2
37 | with:
38 | r-version: ${{ matrix.config.r }}
39 | http-user-agent: ${{ matrix.config.http-user-agent }}
40 | use-public-rspm: true
41 |
42 | - uses: r-lib/actions/setup-r-dependencies@v2
43 | with:
44 | working-directory: "./editbl"
45 | extra-packages: any::rcmdcheck
46 | needs: check
47 |
48 | - uses: r-lib/actions/check-r-package@v2
49 | with:
50 | working-directory: "./editbl"
51 | upload-snapshots: true
52 |
--------------------------------------------------------------------------------
/.github/workflows/test-coverage.yaml:
--------------------------------------------------------------------------------
1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3 | on:
4 | push:
5 | branches: [main, master]
6 | pull_request:
7 | branches: [main, master]
8 |
9 | name: test-coverage
10 |
11 | jobs:
12 | test-coverage:
13 | runs-on: ubuntu-latest
14 | env:
15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
16 |
17 | steps:
18 | - uses: actions/checkout@v3
19 |
20 | - uses: r-lib/actions/setup-r@v2
21 | with:
22 | use-public-rspm: true
23 |
24 | - uses: r-lib/actions/setup-r-dependencies@v2
25 | with:
26 | working-directory: "./editbl"
27 | extra-packages: any::covr
28 | needs: coverage
29 |
30 | - name: Test coverage
31 | run: |
32 | covr::codecov(
33 | path = "./editbl",
34 | quiet = FALSE,
35 | clean = FALSE,
36 | install_path = file.path(Sys.getenv("RUNNER_TEMP"), "package")
37 | )
38 | shell: Rscript {0}
39 |
40 | - name: Show testthat output
41 | if: always()
42 | run: |
43 | ## --------------------------------------------------------------------
44 | find ${{ runner.temp }}/package -name 'testthat.Rout*' -exec cat '{}' \; || true
45 | shell: bash
46 |
47 | - name: Upload test results
48 | if: failure()
49 | uses: actions/upload-artifact@v4
50 | with:
51 | name: coverage-test-failures
52 | path: ${{ runner.temp }}/package
53 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /editbl.Rcheck/
2 | editbl_*.tar.gz
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | editbl
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.statet.r.resourceProjects.RBuilder
10 |
11 |
12 |
13 |
14 | org.eclipse.statet.docmlet.resourceProjects.WikitextBuilder
15 |
16 |
17 |
18 |
19 |
20 | org.eclipse.statet.ide.resourceProjects.Statet
21 | org.eclipse.statet.r.resourceProjects.R
22 | org.eclipse.statet.docmlet.resourceProjects.Wikitext
23 |
24 |
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | editbl/README.md
--------------------------------------------------------------------------------
/editbl.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openanalytics/editbl/603a4e2702f7183b4ce8447bc8b274f6c8f71942/editbl.gif
--------------------------------------------------------------------------------
/editbl/DESCRIPTION:
--------------------------------------------------------------------------------
1 | Package: editbl
2 | Type: Package
3 | Version: 1.3.0
4 | Date: 2025-04-23
5 | Title: 'DT' Extension for CRUD (Create, Read, Update, Delete) Applications in 'shiny'
6 | Authors@R: c(person("Jasper", "Schelfhout", "", "jasper.schelfhout@openanalytics.eu",
7 | role = c("aut", "cre")),
8 | person("Maxim", "Nazarov", "", "maxim.nazarov@openanalytics.eu",
9 | role = c("rev")),
10 | person("Daan", "Seynaeve", "", "daan.seynaeve@openanalytics.eu",
11 | role = c("rev")),
12 | person("Lennart", "Tuijnder", "", "lennart.tuijnder@openanalytics.eu",
13 | role = c("rev")),
14 | person("Saar", "Junius", "", "saar.junius@openanalytics.eu",
15 | role = c("aut")))
16 | Maintainer: Jasper Schelfhout
17 | Description: The core of this package is a function eDT() which enhances DT::datatable() such that it can be used to interactively modify data in 'shiny'. By the use of generic 'dplyr' methods it supports many types of data storage, with relational databases ('dbplyr') being the main use case.
18 | License: GPL-3
19 | Copyright: Open Analytics NV, 2023
20 | Imports:
21 | shiny,
22 | shinyjs,
23 | DT,
24 | tibble,
25 | dplyr,
26 | rlang,
27 | uuid,
28 | fontawesome (>= 0.4.0)
29 | Suggests:
30 | testthat,
31 | dtplyr,
32 | data.table,
33 | vctrs,
34 | RSQLite,
35 | dbplyr,
36 | glue,
37 | DBI,
38 | bit64,
39 | knitr,
40 | dm
41 | URL: https://github.com/openanalytics/editbl
42 | BugReports: https://github.com/openanalytics/editbl/issues
43 | VignetteBuilder: knitr
44 | Encoding: UTF-8
45 | Roxygen: list(markdown = TRUE)
46 | RoxygenNote: 7.3.2
47 |
--------------------------------------------------------------------------------
/editbl/NAMESPACE:
--------------------------------------------------------------------------------
1 | # Generated by roxygen2: do not edit by hand
2 |
3 | S3method(e_rows_insert,default)
4 | S3method(e_rows_insert,dtplyr_step)
5 | S3method(e_rows_insert,tbl_dbi)
6 | S3method(e_rows_update,data.frame)
7 | S3method(e_rows_update,default)
8 | S3method(e_rows_update,dtplyr_step)
9 | S3method(e_rows_update,tbl_dbi)
10 | S3method(inputServer,default)
11 | S3method(inputUI,default)
12 | S3method(rows_delete,dtplyr_step)
13 | export(connectDB)
14 | export(customButton)
15 | export(eDT)
16 | export(eDTOutput)
17 | export(e_rows_insert)
18 | export(e_rows_update)
19 | export(foreignTbl)
20 | export(inputServer)
21 | export(inputUI)
22 | export(runDemoApp)
23 | export(selectInputDT_Server)
24 | export(selectInputDT_UI)
25 | import(fontawesome)
26 | importFrom(DT,DTOutput)
27 | importFrom(DT,coerceValue)
28 | importFrom(DT,dataTableProxy)
29 | importFrom(DT,datatable)
30 | importFrom(DT,formatStyle)
31 | importFrom(DT,hideCols)
32 | importFrom(DT,renderDT)
33 | importFrom(DT,renderDataTable)
34 | importFrom(DT,styleEqual)
35 | importFrom(dplyr,"%>%")
36 | importFrom(dplyr,across)
37 | importFrom(dplyr,all_of)
38 | importFrom(dplyr,anti_join)
39 | importFrom(dplyr,as_tibble)
40 | importFrom(dplyr,collect)
41 | importFrom(dplyr,count)
42 | importFrom(dplyr,distinct)
43 | importFrom(dplyr,everything)
44 | importFrom(dplyr,filter)
45 | importFrom(dplyr,if_any)
46 | importFrom(dplyr,inner_join)
47 | importFrom(dplyr,is.tbl)
48 | importFrom(dplyr,left_join)
49 | importFrom(dplyr,pull)
50 | importFrom(dplyr,relocate)
51 | importFrom(dplyr,rows_delete)
52 | importFrom(dplyr,rows_insert)
53 | importFrom(dplyr,rows_update)
54 | importFrom(dplyr,rowwise)
55 | importFrom(dplyr,select)
56 | importFrom(dplyr,tbl)
57 | importFrom(dplyr,tbl_vars)
58 | importFrom(dplyr,tibble)
59 | importFrom(dplyr,type_sum)
60 | importFrom(dplyr,union)
61 | importFrom(rlang,":=")
62 | importFrom(shiny,HTML)
63 | importFrom(shiny,NS)
64 | importFrom(shiny,actionButton)
65 | importFrom(shiny,dateInput)
66 | importFrom(shiny,div)
67 | importFrom(shiny,fluidPage)
68 | importFrom(shiny,freezeReactiveValue)
69 | importFrom(shiny,icon)
70 | importFrom(shiny,is.reactive)
71 | importFrom(shiny,isTruthy)
72 | importFrom(shiny,isolate)
73 | importFrom(shiny,modalButton)
74 | importFrom(shiny,modalDialog)
75 | importFrom(shiny,moduleServer)
76 | importFrom(shiny,numericInput)
77 | importFrom(shiny,observe)
78 | importFrom(shiny,observeEvent)
79 | importFrom(shiny,reactive)
80 | importFrom(shiny,reactiveValues)
81 | importFrom(shiny,renderPrint)
82 | importFrom(shiny,renderUI)
83 | importFrom(shiny,req)
84 | importFrom(shiny,selectInput)
85 | importFrom(shiny,shinyApp)
86 | importFrom(shiny,showModal)
87 | importFrom(shiny,showNotification)
88 | importFrom(shiny,stopApp)
89 | importFrom(shiny,tagList)
90 | importFrom(shiny,tags)
91 | importFrom(shiny,textInput)
92 | importFrom(shiny,uiOutput)
93 | importFrom(shiny,verbatimTextOutput)
94 | importFrom(shinyjs,disable)
95 | importFrom(shinyjs,disabled)
96 | importFrom(shinyjs,enable)
97 | importFrom(shinyjs,hidden)
98 | importFrom(shinyjs,useShinyjs)
99 | importFrom(tibble,as_tibble)
100 | importFrom(utils,head)
101 | importFrom(utils,packageName)
102 | importFrom(utils,str)
103 | importFrom(utils,tail)
104 | importFrom(uuid,UUIDgenerate)
105 |
--------------------------------------------------------------------------------
/editbl/R/demoApp.R:
--------------------------------------------------------------------------------
1 | #' Run a demo app
2 | #' @param app demoApp to run. Options: database / mtcars / custom
3 | #'
4 | #' @details These apps are for illustrative purposes.
5 | #'
6 | #' @param ... arguments passed onto the demoApp
7 | #' @examples
8 | #' ## Only run this example in interactive R sessions
9 | #' if(interactive()){
10 | #'
11 | #' # Database
12 | #' tmpFile <- tempfile(fileext = ".sqlite")
13 | #' file.copy(system.file("extdata", "chinook.sqlite", package = 'editbl'), tmpFile)
14 | #'
15 | #' conn <- connectDB(dbname = tmpFile)
16 | #'
17 | #' runDemoApp(app = "database", conn = conn)
18 | #' DBI::dbDisconnect(conn)
19 | #'
20 | #' unlink(tmpFile)
21 | #'
22 | #' # mtcars
23 | #' runDemoApp(app = "mtcars")
24 | #'
25 | #' # Any tibble of your liking
26 | #' runDemoApp(app = "custom", dplyr::tibble(iris))
27 | #' }
28 | #' @inherit shiny::shinyApp return
29 | #' @export
30 | runDemoApp <- function(app = "database", ...){
31 | fn <- switch(app,
32 | "database" = runDemoApp_DB,
33 | "mtcars" = runDemoApp_mtcars,
34 | "custom" = runDemoApp_custom,
35 | stop("demoApp not found")
36 | )
37 | do.call(fn, list(...))
38 | }
39 |
40 | #' Run a demo app
41 | #' @importFrom shiny shinyApp
42 | #' @inherit shiny::shinyApp return
43 | runDemoApp_DB <- function(){
44 | tmpFile <- tempfile(fileext = ".sqlite")
45 | file.copy(system.file("extdata", "chinook.sqlite", package = 'editbl'), tmpFile)
46 | conn <- editbl::connectDB(dbname = tmpFile)
47 |
48 | ui <- demoUI_DB(id = "app", conn = conn)
49 | server <- function(input, output, session) {
50 | demoServer_DB(id = "app", conn = conn)
51 | }
52 | shinyApp(ui, server)
53 | }
54 |
55 | #' Run a demo app
56 | #' @importFrom shiny shinyApp
57 | #' @inherit shiny::shinyApp return
58 | runDemoApp_mtcars <- function(){
59 | ui <- demoUI_mtcars(id = "app")
60 | server <- function(input, output, session) {
61 | demoServer_mtcars(id = "app")
62 | }
63 | shinyApp(ui, server)
64 | }
65 |
66 | #' Run a custom demo app
67 | #' @param x `tbl`
68 | #' @importFrom shiny shinyApp
69 | #' @inherit shiny::shinyApp return
70 | runDemoApp_custom <- function(x){
71 | ui <- demoUI_custom(id = "app")
72 | server <- function(input, output, session) {
73 | demoServer_custom(id = "app", x= x)
74 | }
75 | shinyApp(ui, server)
76 | }
77 |
78 | #' UI of the DB demo app
79 | #' @param id `character(1)`
80 | #' @param conn database connection object as given by \code{\link[DBI]{dbConnect}}.
81 | #' @importFrom shiny NS tagList selectInput verbatimTextOutput
82 | #' @return HTML
83 | #'
84 | #' @author Jasper Schelfhout
85 | demoUI_DB <- function(id, conn) {
86 | ns <- NS(id)
87 | tagList(
88 | selectInput(
89 | inputId = ns("table"),
90 | label = "table",
91 | choices = DBI::dbListTables(conn)),
92 | eDTOutput(id = ns("editbl"))
93 | )
94 | }
95 |
96 | #' Server of the DB demo app
97 | #' @param id `character(1)`
98 | #' @param conn database connection object as given by \code{\link[DBI]{dbConnect}}.
99 | #' @importFrom shiny reactive moduleServer renderPrint
100 | #' @importFrom dplyr tbl
101 | #' @return NULL, just executes the module server.
102 | #' @author Jasper Schelfhout
103 | demoServer_DB <- function(id, conn) {
104 | moduleServer(
105 | id,
106 | function(input, output, session) {
107 | data <- reactive({
108 | dplyr::tbl(conn, input$table)
109 | })
110 |
111 | modifiedData <- eDT(
112 | id = "editbl",
113 | data = data,
114 | in_place = TRUE
115 | )
116 |
117 | invisible()
118 | }
119 | )
120 | }
121 |
122 | #' UI of the demo mtcars app
123 | #' @param id `character(1)`
124 | #' @importFrom shiny NS tagList
125 | #' @return HTML
126 | #'
127 | #' @author Jasper Schelfhout
128 | demoUI_mtcars <- function(id) {
129 | demoUI_custom(id)
130 | }
131 |
132 | #' Server of the mtcars demo app
133 | #' @param id `character(1)`
134 | #' @importFrom dplyr tibble
135 | #' @inherit demoServer_custom return
136 | #' @author Jasper Schelfhout
137 | demoServer_mtcars <- function(id) {
138 | demoServer_custom(id, dplyr::tibble(datasets::mtcars))
139 | }
140 |
141 | #' UI of the demo mtcars app
142 | #' @param id `character(1)`
143 | #' @importFrom shiny NS tagList
144 | #' @return HTML
145 | #'
146 | #' @author Jasper Schelfhout
147 | demoUI_custom <- function(id) {
148 | ns <- NS(id)
149 | tagList(
150 | eDTOutput(id = ns("editbl")),
151 | )
152 | }
153 |
154 | #' Server of the mtcars demo app
155 | #' @param id `character(1)`
156 | #' @param x `tbl`
157 | #' @return NULL, just executes the module server.
158 | #' @author Jasper Schelfhout
159 | demoServer_custom <- function(id, x) {
160 | moduleServer(
161 | id,
162 | function(input, output, session) {
163 | modifiedData <- eDT(
164 | id = "editbl",
165 | data = x,
166 | in_place = FALSE
167 | )$result
168 |
169 | invisible()
170 | }
171 | )
172 | }
173 |
--------------------------------------------------------------------------------
/editbl/R/devApp.R:
--------------------------------------------------------------------------------
1 | #' Run a development app
2 | #' @details This app prints some of the server objects and has a button to interactively browse the code.
3 | #' This is useful for debugging and experimenting with new features.
4 | #'
5 | #' @importFrom shiny shinyApp
6 | #' @inherit shiny::shinyApp return
7 | runDevApp <- function(){
8 | conn <- connectDB()
9 | ui <- devUI(id = "app", conn = conn)
10 | server <- function(input, output, session) {
11 | devServer(id = "app", conn = conn)
12 | }
13 | shinyApp(ui, server)
14 | }
15 |
16 | #' UI of the development app
17 | #' @param id `character(1)`
18 | #' @param conn database connection object as given by \code{\link[DBI]{dbConnect}}.
19 | #' @importFrom shiny NS tagList selectInput verbatimTextOutput actionButton
20 | #' @return HTML
21 | #'
22 | #' @author Jasper Schelfhout
23 | devUI <- function(id, conn) {
24 | ns <- NS(id)
25 | tagList(
26 | actionButton(ns("browse"), label = "browse parent"),
27 | selectInput(
28 | inputId = ns("table"),
29 | label = "table",
30 | choices = DBI::dbListTables(conn)),
31 | eDTOutput(id = ns("editbl")),
32 | verbatimTextOutput(ns("modifiedData"))
33 | )
34 | }
35 |
36 | #' Server of the development app
37 | #' @param id `character(1)`
38 | #' @param conn database connection object as given by \code{\link[DBI]{dbConnect}}.
39 | #' @importFrom shiny reactive moduleServer renderPrint
40 | #' @importFrom dplyr tbl
41 | #' @return NULL, just executes the module server.
42 | #' @author Jasper Schelfhout
43 | devServer <- function(id, conn) {
44 | moduleServer(
45 | id,
46 | function(input, output, session) {
47 | data <- reactive({
48 | dplyr::tbl(conn, input$table)
49 | })
50 |
51 | modifiedData <- eDT(
52 | id = "editbl",
53 | data = data,
54 | options = list(
55 | columnDefs = list(list(
56 | visible = TRUE,
57 | targets = "_all"))),
58 | in_place = TRUE,
59 | filter = "top"
60 | )$result
61 |
62 |
63 | observeEvent(input$browse,{
64 | browser()
65 | })
66 |
67 | output$modifiedData <- renderPrint({
68 | str(modifiedData())
69 | })
70 | invisible()
71 | }
72 | )
73 | }
--------------------------------------------------------------------------------
/editbl/R/eDT_app.R:
--------------------------------------------------------------------------------
1 | #' Open interactive app to explore and modify data
2 | #'
3 | #' @details When \code{\link{eDT}} is not used within the server of a shiny app, it will
4 | #' call this function to start up a shiny app itself. Just as `DT::datatable()` displays a table
5 | #' in the browser when called upon interactively.
6 | #'
7 | #' @param ... arguments past to \code{\link{eDT}}
8 | #' @importFrom shiny shinyApp
9 | #' @return data (or a modified version thereof) once you click 'close'
10 | eDT_app <- function(...){
11 | args <- list(...)
12 | ui <- eDT_app_ui(eDTId = args$id)
13 | server <- function(input, output, session) {
14 | do.call(eDT_app_server, args)
15 | }
16 | shiny::runApp(list(ui = ui, server = server))
17 | }
18 |
19 | #' UI of eDT_app
20 | #' @param moduleId `character(1)` id to connect with eDT_app_server
21 | #' @param eDTId `character(1)` id to connect \code{\link{eDTOutput}} to \code{\link{eDT}} within the module.
22 | #' @importFrom shiny NS tagList actionButton
23 | #' @return HTML
24 | #'
25 | #' @author Jasper Schelfhout
26 | eDT_app_ui <- function(moduleId = "nevergonnagiveyouup", eDTId = "nevergonnaletyoudown") {
27 | ns <- NS(moduleId)
28 | tagList(
29 | actionButton(inputId = ns("close"), "label" = "close"),
30 | eDTOutput(id = ns(eDTId)),
31 | )
32 | }
33 |
34 | #' Server of eDT_app
35 | #' @param moduleId `character(1)` id to connect with eDT_app_server
36 | #' @param ... arguments passed to \link{eDT}
37 | #' @importFrom shiny reactive moduleServer observeEvent stopApp reactiveValues
38 | #' @importFrom dplyr tbl
39 | #' @return moduleServer which on application stop returns version of x with made changes
40 | #'
41 | #' @author Jasper Schelfhout
42 | eDT_app_server <- function(moduleId = "nevergonnagiveyouup",...) {
43 | args <- list(...)
44 | moduleServer(
45 | moduleId,
46 | function(input, output, session) {
47 | modifiedData <- do.call(eDTServer, args)$result
48 |
49 | observeEvent(input$close, {
50 | shiny::stopApp(modifiedData())
51 | })
52 | }
53 | )
54 | }
55 |
--------------------------------------------------------------------------------
/editbl/R/foreignTbl.R:
--------------------------------------------------------------------------------
1 | #' Create a foreign tibble
2 | #'
3 | #' @details This is a tibble that can be passed onto \code{\link{eDT}} as a referenced table.
4 | #'
5 | #' It is the equivalent of a database table to which the `data` tbl of eDT has a foreign key.
6 | #'
7 | #' It will be merged with the tbl passed onto the `data` argument allowing to provide restrictions
8 | #' for certain columns.
9 | #'
10 | #' Note that row uniqueness for the columns used in `by` and `naturalKey` is assumed.
11 | #' This assumption will however not be checked since it is an expensive operation on big datasets.
12 | #' However, if violated, it might give errors or unexpected results during usage of the eDT module.
13 | #'
14 | #' @param x `tbl`. The referencing table.
15 | #' @param y `tbl`. The referenced table.
16 | #' @param by `character`. Column names to match on.
17 | #' Note that you should rename and/or typecast the columns in y should they not exactly match the columns in x.
18 | #' @param naturalKey `character`. The columns that form the natural key in y.
19 | #' These are the only ones that can actually get modified in `eDT` when changing cells in the table.
20 | #' Reasoning being that these columns should be sufficient to uniquely identify a row in the referenced table.
21 | #' All other columns will be automatically fetched and filled in.
22 | #'
23 | #' @param allowNew `logical`. Whether or not new values are allowed. If `TRUE`,
24 | #' the rows in the foreignTbl will only be used as suggestions, not restrictions.
25 | #'
26 | #' @examples
27 | #' a <- tibble::tibble(
28 | #' first_name = c("Albert","Donald","Mickey"),
29 | #' last_name_id = c(1,2,2)
30 | #' )
31 | #'
32 | #' b <- foreignTbl(
33 | #' a,
34 | #' tibble::tibble(
35 | #' last_name = c("Einstein", "Duck", "Mouse"),
36 | #' last_name_id = c(1,2,3)
37 | #' ),
38 | #' by = "last_name_id",
39 | #' naturalKey = "last_name"
40 | #')
41 | #'
42 | #' ## Only run this in interactive R sessions
43 | #' if(interactive()){
44 | #' eDT(a,
45 | #' foreignTbls = list(b),
46 | #' options = list(columnDefs = list(list(visible=FALSE, targets="last_name_id")))
47 | #' )
48 | #' }
49 | #'
50 | #'
51 | #' @return List with unmodified arguments. However, they have now been checked for validity.
52 | #' - y, see argument `y`.
53 | #' - by, see argument `by`.
54 | #' - naturalKey, see argument `naturalKey`.
55 | #' - allowNew, see argument `allowNew`
56 | #'
57 | #' @importFrom dplyr tbl_vars all_of select
58 | #'
59 | #' @author Jasper Schelfhout
60 | #' @export
61 | foreignTbl <- function(
62 | x,
63 | y,
64 | by = intersect(dplyr::tbl_vars(x), dplyr::tbl_vars(y)),
65 | naturalKey = dplyr::tbl_vars(y),
66 | allowNew = FALSE
67 | ){
68 | stopifnot(all(naturalKey %in% colnames(y)))
69 | stopifnot(is.logical(allowNew))
70 | if(is.null(names(by))){
71 | names(by) <- by
72 | }
73 |
74 | clashingNames <- intersect(
75 | setdiff(dplyr::tbl_vars(y), by),
76 | dplyr::tbl_vars(x)
77 | )
78 | if(length(clashingNames)){
79 | stop(sprintf("Name clashes on: %s. Please rename.", paste(clashingNames, collapse = ", ")))
80 | }
81 |
82 | x_types <- x %>% select(all_of(by)) %>% getColumnTypeSums %>% unlist()
83 | y_types <- y %>% select(all_of(by)) %>% getColumnTypeSums %>% unlist()
84 | typeMisMatches <- names(x_types[x_types != y_types])
85 | if(length(typeMisMatches)){
86 | stop(sprintf("%s is not of the same type: %s vs %s. Try casting with dplyr::mutate()",
87 | typeMisMatches,
88 | x_types[typeMisMatches],
89 | y_types[typeMisMatches]))
90 | }
91 |
92 | foreignTbl <- list(
93 | y = y,
94 | by = by,
95 | naturalKey = naturalKey,
96 | allowNew = allowNew
97 | )
98 |
99 | return(foreignTbl)
100 | }
101 |
102 | #' Merge a tbl with it a foreignTbl
103 | #'
104 | #' @details see also `dplyr` join functions, for example `dplyr::left_join`.
105 | #'
106 | #' @param tbl `tbl`
107 | #' @param foreignTbl `list` as created by \code{\link{foreignTbl}}
108 | #' @param keepNA `logical` keep rows from tbl with NA keys.
109 | #' @param by named `character`, columns to join on.
110 | #' @param copy `logical`, whether or not to copy the `foreignTbl` to the source of argument `tbl` for joining.
111 | #' @param type `character(1)`, type of joint to perform. Can be 'inner' or 'left'.
112 | #' @return `tbl`, containing both columns from argument `tbl` and argument `foreignTbl`.
113 | #'
114 | #' @importFrom dplyr left_join inner_join filter union
115 | #'
116 | #' @author Jasper Schelfhout
117 | joinForeignTbl <- function(
118 | tbl,
119 | foreignTbl,
120 | keepNA = TRUE,
121 | by = foreignTbl$by,
122 | copy = TRUE,
123 | type = c("inner", "left")[1]){
124 | .data <- NULL # for R CMD CHECK
125 | if(is.null(names(by))){
126 | names(by) <- by
127 | }
128 |
129 | if(type == "inner"){
130 | result <- dplyr::inner_join(
131 | x = tbl,
132 | y = foreignTbl$y,
133 | by = by,
134 | copy = copy)
135 | } else if(type == "left"){
136 | result <- dplyr::left_join(
137 | x = tbl,
138 | y = foreignTbl$y,
139 | by = by,
140 | copy = copy)
141 | } else {
142 | stop("only inner and left join supported")
143 | }
144 |
145 |
146 | if(keepNA && type == "inner"){
147 | naTbl <- tbl
148 | for(key in names(by)){
149 | naTbl <- dplyr::filter(naTbl, is.na(.data[[key]]))
150 | }
151 |
152 | naTbl <- dplyr::left_join(
153 | x = naTbl,
154 | y = foreignTbl$y,
155 | by = by,
156 | copy = copy
157 | )
158 | result <- dplyr::union(result, naTbl)
159 | }
160 |
161 | return(result)
162 | }
163 |
164 | #' Fill data columns based on foreignTbls
165 | #'
166 | #' @details
167 | #' When a combination of columns is not found in the foreignTbl,
168 | #' fill the deductedColumns with NA.
169 | #'
170 | #' @details on foreignTbls suggesting conflicting data,
171 | #' an arbitrary choice is made. It is best to afterwards check with
172 | #' checkForeignTbls to see if a valid result is obtained.
173 | #'
174 | #' @param tbl `tbl`
175 | #' @param foreignTbls list of foreign tbls as created by \code{\link{foreignTbl}}
176 | #' @return tbl
177 | #'
178 | #' @author Jasper Schelfhout
179 | fillDeductedColumns <- function(tbl, foreignTbls){
180 | columnOrder <- names(tbl)
181 | nrow <- nrow(tbl)
182 |
183 | # Clear columns that should be deducted
184 | for (foreignTbl in foreignTbls){
185 | autoFill <- setdiff(colnames(foreignTbl$y), foreignTbl$naturalKey)
186 | tbl[,autoFill] <- NULL
187 | }
188 |
189 | # Fill in columns
190 | for(foreignTbl in foreignTbls){
191 | tbl <- joinForeignTbl(tbl, foreignTbl, by = foreignTbl$naturalKey, type = "left")
192 | }
193 |
194 | newNrow <- nrow(tbl)
195 |
196 | if(nrow < newNrow){
197 | stop("One of the given foreign tbls has non unique keys.")
198 | }
199 |
200 | tbl[,columnOrder]
201 | }
202 |
203 | #' Check if all rows in tbl fufill `foreignTbl` constraints
204 | #' @param tbl `tbl`
205 | #' @param foreignTbls list of foreign tbls as created by \code{\link{foreignTbl}}
206 | #' @return `logical` stating if tbl fufills all constraints imposed by all foreign tbls.
207 | #' @importFrom dplyr count pull anti_join filter if_any all_of
208 | #'
209 | #' @author Jasper Schelfhout
210 | checkForeignTbls <- function(tbl, foreignTbls){
211 | n <- NULL # R CMD CHECK fix
212 |
213 |
214 | for(foreignTbl in foreignTbls){
215 | # match on combination on naturalKey and surrogateKey
216 | # E.g. check that the row actually exists in the foreignTbl
217 | if(foreignTbl$allowNew){
218 | next()
219 | }
220 | by <- unique(unname(c(foreignTbl$by, foreignTbl$naturalKey)))
221 |
222 | nonExisting <- dplyr::anti_join(tbl, foreignTbl$y,
223 | by = by,
224 | copy = TRUE) %>%
225 | dplyr::filter(if_any(all_of(by), ~ !is.na(.))) # Do not complain about empty rows, might be nice as a parameter.
226 |
227 | if(dplyr::pull(dplyr::count(nonExisting), n)){
228 | stop(sprintf("Invalid %s: %s",
229 | paste(foreignTbl$naturalKey, collapse = ", "),
230 | paste(as.character(nonExisting[1,foreignTbl$naturalKey]), collapse = ", ")
231 | ))
232 | }
233 | }
234 | TRUE
235 | }
236 |
237 | #' Cast all columns that exist in a foreignTbl to factor
238 | #'
239 | #' @details Can be used to fixate possible options when editing.
240 | #'
241 | #' @param data `data.frame`
242 | #' @param foreignTbls list of foreign tbls as created by \code{\link{foreignTbl}}
243 | #' @importFrom dplyr distinct select pull tbl_vars
244 | #' @return data.frame
245 | #'
246 | #' @author Jasper Schelfhout
247 | castToFactor <- function(data, foreignTbls){
248 | levels <- list()
249 | for(foreignTbl in foreignTbls){
250 | for(column in dplyr::tbl_vars(foreignTbl$y)){
251 | currentLevels <- levels[[column]]
252 | newRestrictions <- dplyr::pull(dplyr::distinct(dplyr::select(foreignTbl$y, column)))
253 | if(is.null(currentLevels)){
254 | levels[[column]] <- newRestrictions
255 | } else {
256 | levels[[column]] <- intersect(currentLevels, newRestrictions)
257 | }
258 | }
259 | }
260 |
261 | for(column in intersect(dplyr::tbl_vars(data), names(levels))){
262 | data[[column]] <- factor(data[[column]], levels = levels[[column]])
263 | }
264 | data
265 | }
266 |
267 | #' Get all columns that are not natural keys
268 | #' @param foreignTbls list of foreign tbls as created by \code{\link{foreignTbl}}
269 | #' @return `character`
270 | #'
271 | #' @author Jasper Schelfhout
272 | getNonNaturalKeyCols <- function(foreignTbls){
273 | result <- c()
274 | for(foreignTbl in foreignTbls){
275 | cols <- as.character(dplyr::tbl_vars(foreignTbl$y))
276 | naturalKey <- foreignTbl$naturalKey
277 | result <- unique(c(result, setdiff(cols, naturalKey)))
278 | }
279 | result
280 | }
281 |
--------------------------------------------------------------------------------
/editbl/R/selectInputDT.R:
--------------------------------------------------------------------------------
1 | #' UI part of a DT select input
2 | #' @param id `character(1)` same one as used in \code{\link{selectInputDT_Server}}
3 | #' @importFrom shiny NS uiOutput
4 | #' @return HTML
5 | #' @inherit selectInputDT_Server examples
6 | #' @export
7 | #' @author Jasper Schelfhout
8 | selectInputDT_UI <- function(id){
9 | ns <- NS(id)
10 | uiOutput(ns("DT_UI"))
11 | }
12 |
13 | #' Server part to use a \code{\link[DT]{datatable}} as select input
14 | #'
15 | #' @seealso `shiny::selectInput`. This function can be more convenient for selecting rows
16 | #' with multiple columns.
17 | #'
18 | #' @param id `character(1)` same one as used in \code{\link{selectInputDT_UI}}
19 | #' @param choices `data.frame`
20 | #' @param label `character(1)`
21 | #' @param selected `data.frame` with rows available in `choices`.
22 | #' @param multiple `logical`. Whether or not multiple row selection is allowed
23 | #'
24 | #' @examples
25 | #' ## Only run this example in interactive R sessions
26 | #' if(interactive()){
27 | #' ui <- selectInputDT_UI('id')
28 | #' data <- data.frame(id = 1:3, name = letters[1:3])
29 | #' server <- function(input,output, session){
30 | #' selected = selectInputDT_Server('id', choices = data, selected = data[1,] )
31 | #' observe({print(selected())})
32 | #' }
33 | #' shiny::shinyApp(ui, server)
34 | #'
35 | #' }
36 | #' @importFrom shiny is.reactive reactive renderUI moduleServer
37 | #' @importFrom DT renderDataTable datatable
38 | #' @return A selection of rows from the `data.frame` provided under choices.
39 | #' @export
40 | #' @author Jasper Schelfhout
41 | selectInputDT_Server <- function(id,
42 | label = "",
43 | choices,
44 | selected = NULL,
45 | multiple = FALSE){
46 | moduleServer(
47 | id,
48 | function(input,output,session){
49 | # Make arguments reactive
50 | # Need to be explicit about environement. Otherwhise they overwrite themselves.
51 | # This way users can pass on both reactive an non reactive arguments
52 | argEnv <- parent.frame(3)
53 |
54 | if(!shiny::is.reactive(label)){
55 | label <- shiny::reactive(label, env = argEnv)
56 | }
57 |
58 | if(!shiny::is.reactive(choices)){
59 | choices <- shiny::reactive(choices, env = argEnv)
60 | }
61 |
62 | if(!shiny::is.reactive(selected)){
63 | selected <- shiny::reactive(selected, env = argEnv)
64 | }
65 |
66 | if(!shiny::is.reactive(multiple)){
67 | multiple <- shiny::reactive(multiple, env = argEnv)
68 | }
69 |
70 | observe({
71 | if(!multiple() && nrow(selected()) > 1){
72 | stop("Can not have more than 1 row selected.")
73 | }
74 | })
75 |
76 | ns <- session$ns
77 |
78 | hasSelection <- reactive({
79 | if(is.null(selected())){
80 | FALSE
81 | } else if ({ all(is.na(selected()))}){
82 | FALSE
83 | } else {
84 | TRUE
85 | }
86 | })
87 |
88 | data_selection_first <- reactive({
89 | if(hasSelection()){
90 | dt <- unique(rbind(selected(), choices()))
91 | } else {
92 | dt <- choices()
93 | }
94 | dt
95 | })
96 |
97 | output$DT_UI <- renderUI({
98 | tagList(
99 | label(),
100 | DT::DTOutput(ns("DT"))
101 | )
102 | })
103 |
104 | mode <- reactive({
105 | if(multiple()){
106 | mode = "multiple"
107 | } else {
108 | mode = "single"
109 | }
110 | mode
111 | })
112 |
113 | rowNrs <- reactive({
114 | if (hasSelection()){
115 | rowNrs = seq_len(nrow(selected()))
116 | } else {
117 | c()
118 | }
119 | rowNrs
120 | })
121 |
122 | output$DT <- DT::renderDataTable({
123 | data <- data_selection_first()
124 | DT::datatable(
125 | data,
126 | rownames = TRUE,
127 | options = list(
128 | scrollX = TRUE,
129 | columnDefs = list(
130 | list(
131 | visible = FALSE,
132 | targets = c(0)) # hide row names https://github.com/rstudio/DT/issues/945
133 | )
134 | ),
135 | filter = "top",
136 | selection = list(
137 | mode = mode(),
138 | selected = rowNrs(),
139 | target = 'row'))
140 | })
141 |
142 | reactive({
143 | data_selection_first()[input$DT_rows_selected,]
144 | })
145 | })
146 | }
147 |
--------------------------------------------------------------------------------
/editbl/R/shinyInput.R:
--------------------------------------------------------------------------------
1 | #' An input UI for a `data.frame`
2 | #'
3 | #' @details A new method for this can be added if you wish to alter the default behavior of the pop-up modals in \code{\link{eDT}}.
4 | #'
5 | #' @param id `character(1)` module id
6 | #' @param ... arguments passed onto methods
7 | #' @inherit inputServer examples
8 | #'
9 | #' @return HTML. A set of input fields corresponding to the given row.
10 | #'
11 | #' @author Jasper Schelfhout
12 | #' @export
13 | inputUI <- function(id, ...){
14 | UseMethod("inputUI")
15 | }
16 |
17 | #' An input server for a `data.frame`
18 | #'
19 | #' @details A new method for this can be added if you wish to alter the default behavior of the pop-up modals in \code{\link{eDT}}.
20 | #'
21 | #' @param id `character(1)` module id
22 | #' @param data single row `data.frame`
23 | #' @param ... further arguments for methods
24 | #' @return modified version of data
25 | #'
26 | #' @examples
27 | #' if(interactive()){
28 | #' library(shiny)
29 | #' ui <- inputUI('id')
30 | #' server <- function(input,output,session){
31 | #' input <- inputServer("id", mtcars[1,])
32 | #' observe({print(input())})
33 | #' }
34 | #' shinyApp(ui, server)
35 | #' }
36 | #'
37 | #' @author Jasper Schelfhout
38 | #' @export
39 | inputServer <- function(id, data, ...){
40 | UseMethod("inputServer")
41 | }
42 |
43 | #' UI part for modal with input fields for editing
44 | #'
45 | #' @details The UI elements that have an id identical to a column name are used for updating the data.
46 | #'
47 | #' @param id character module id
48 | #' @param ... for compatibility with method
49 | #' @return HTML. A set of input fields corresponding to the given row.
50 | #'
51 | #' @author Jasper Schelfhout
52 | #' @export
53 | inputUI.default <- function(id, ...){
54 | ns <- NS(id)
55 | uiOutput(ns("inputUI"))
56 | }
57 |
58 | #' An input server for a `data.frame`
59 | #'
60 | #' @details Reads all inputs ids that are identical to column names of the data
61 | #' and updates the data.
62 | #'
63 | #' @param id `character(1)` module id
64 | #' @param data single row `data.frame`
65 | #' @param notEditable `character` columns that should not be edited
66 | #' @param colnames named `character`
67 | #' @param foreignTbls list of foreignTbls. See \code{\link{foreignTbl}}
68 | #' @param ... for compatibility with other methods
69 | #' @return reactive modified version of data
70 | #'
71 | #' @author Jasper Schelfhout
72 | #' @export
73 | inputServer.default <- function(id, data, colnames, notEditable, foreignTbls, ...){
74 | missingColnames <- missing(colnames)
75 | missingForeignTbls <- missing(foreignTbls)
76 | missingNotEditable <- missing(notEditable)
77 |
78 | moduleServer(
79 | id,
80 | function(input, output, session) {
81 | ns <- session$ns
82 | rv <- reactiveValues(inputDTData = list())
83 |
84 | # Make arguments reactive
85 | # Need to be explicit about environement. Otherwhise they overwrite themselves.
86 | # This way users can pass on both reactive an non reactive arguments
87 | argEnv <- parent.frame(3)
88 |
89 | if(!shiny::is.reactive(data)){
90 | data <- reactive(data, env = argEnv)
91 | }
92 |
93 | if(missingColnames){
94 | colnames <- reactive({
95 | colnames <- base::colnames(data())
96 | names(colnames) <- colnames
97 | colnames
98 | })
99 | }
100 | else if(!shiny::is.reactive(colnames)){
101 | colnames <- reactive(colnames, env = argEnv)
102 | }
103 |
104 | if(missingNotEditable){
105 | notEditable <- reactive({
106 | character(0)
107 | })
108 | }
109 | else if(!shiny::is.reactive(notEditable)){
110 | notEditable <- reactive(notEditable, env = argEnv)
111 | }
112 |
113 | if(missingForeignTbls){
114 | foreignTbls <- reactive({
115 | list()
116 | })
117 | }
118 | else if(!shiny::is.reactive(foreignTbls)){
119 | foreignTbls <- reactive(foreignTbls, env = argEnv)
120 | }
121 |
122 | inputDTs <- reactive({
123 | inputDTs <- lapply(foreignTbls(), function(x){
124 | if(length(x$naturalKey) > 1){
125 | id <- gsub("-", "_", force(uuid::UUIDgenerate()))
126 | list(
127 | id = id,
128 | choices = dplyr::collect(
129 | dplyr::select(
130 | x$y,
131 | dplyr::all_of(as.character(x$naturalKey))
132 | )
133 | ),
134 | selected = data()[,x$naturalKey]
135 | )
136 | }
137 | })
138 | if(length(inputDTs)){
139 | inputDTs[sapply(inputDTs, is.null)] <- NULL
140 | }
141 | inputDTs
142 | })
143 |
144 | observe({
145 | inputDTData <- list()
146 | for(inputDT in inputDTs()){
147 | inputDTData[[inputDT$id]] <- do.call(selectInputDT_Server, inputDT)
148 | }
149 | rv$inputDTData <- inputDTData
150 | })
151 |
152 | inputDTCols <- reactive({
153 | unique(unlist(lapply(inputDTs(), function(x){
154 | names(x$choices)
155 | })))
156 | })
157 |
158 | normalInputs <- reactive({
159 | uiData <- data()[,setdiff(base::colnames(data()), notEditable()), drop = FALSE]
160 | uiData <- castToFactor(uiData, foreignTbls())
161 | inputNormalCols <- setdiff(base::colnames(uiData), inputDTCols())
162 |
163 | inputs <- lapply(inputNormalCols, function(x){
164 | if(x %in% colnames()){
165 | label = names(colnames())[which(colnames() == x)]
166 | } else {
167 | label = x
168 | }
169 |
170 | shinyInput(
171 | x = uiData[[x]],
172 | inputId = ns(x),
173 | label = label,
174 | selected = uiData[,x]
175 | )
176 | })
177 | inputs
178 | })
179 |
180 | DTInputs <- reactive({
181 | lapply(inputDTs(), function(inputDT){
182 | selectInputDT_UI(id = ns(inputDT$id))
183 | })
184 | })
185 |
186 | output$inputUI <- renderUI({
187 | do.call(tagList,c(normalInputs(), DTInputs()))
188 | })
189 |
190 | newData <- reactive({
191 | data <- data()
192 |
193 | # Table inputs
194 | for(DTinput in rv$inputDTData){
195 | newData <- DTinput()
196 | data[,base::colnames(newData)] <- newData
197 | }
198 |
199 | # Normal inputs
200 | for(col in intersect(base::colnames(data), names(input))){
201 | data[,col] = coerceValue(input[[col]], data[,col])
202 | }
203 | data
204 | })
205 | newData
206 | }
207 | )
208 | }
209 |
210 | #' Get a shiny input for a column of a `tbl`
211 | #' @param x column
212 | #' @param inputId shiny input Id
213 | #' @param label `character(1)`
214 | #' @param selected object of class of x
215 | #' @importFrom shiny numericInput dateInput selectInput textInput
216 | #' @return shiny input
217 | #'
218 | #' @author Jasper Schelfhout
219 | shinyInput <- function(x, inputId, label, selected){
220 | if(inherits(x, "logical")){
221 | selectInput(inputId = inputId, label = label, choices = c(" " = NA, "TRUE" = TRUE, "FALSE" = FALSE), selected = selected)
222 | }
223 | else if(inherits(x, "numeric")){
224 | numericInput(inputId = inputId, label = label, value = selected)
225 | }
226 | else if(inherits(x, "Date")){
227 | dateInput(inputId = inputId, label = label, value = selected)
228 | }
229 | else if(inherits(x, "factor")){
230 | selectInput(inputId = inputId, label = label, choices = sort(levels(x)), selected = selected)
231 | } else {
232 | textInput(inputId = inputId, label = label, value = as.character(selected))
233 | }
234 | }
235 |
236 |
--------------------------------------------------------------------------------
/editbl/R/tbl.R:
--------------------------------------------------------------------------------
1 | #' @importFrom dplyr rows_delete
2 | NULL
3 |
4 | #' Insert rows into a tibble
5 | #' @details Mainly a wrapper around \code{\link[dplyr]{rows_insert}}.
6 | #' Allows for specific implementations should the behavior differ from what's needed by `editbl`.
7 | #' Reason for separate method is to avoid conflicts on package loading.
8 | #'
9 | #' @inheritParams dplyr::rows_insert
10 | #' @inherit dplyr::rows_insert return details
11 | #' @export
12 | e_rows_insert <- function(
13 | x,
14 | y,
15 | by = NULL,
16 | ...,
17 | conflict = c("error", "ignore"),
18 | copy = FALSE,
19 | in_place = FALSE){
20 | UseMethod("e_rows_insert")
21 | }
22 |
23 | #' @inherit e_rows_insert
24 | #' @export
25 | #' @importFrom dplyr rows_insert
26 | e_rows_insert.default <- function(
27 | x,
28 | y,
29 | by = NULL,
30 | ...,
31 | conflict = c("error", "ignore"),
32 | copy = FALSE,
33 | in_place = FALSE){
34 | dplyr::rows_insert(
35 | x = x,
36 | y = y,
37 | by = by,
38 | ... ,
39 | conflict = conflict,
40 | copy = copy,
41 | in_place = in_place)
42 | }
43 |
44 | #' Update rows of a tibble
45 | #' @details Mainly a wrapper around \code{\link[dplyr]{rows_update}}.
46 | #' Allows for specific implementations should the behavior differ from what's needed by `editbl`.
47 | #' Reason for separate method is to avoid conflicts on package loading.
48 | #' @param match named `list` consisting out of two equal length `data.frame`'s with columns defined in `by`.
49 | #' This allows for updates of columns defined in `by`.
50 | #' @inheritParams dplyr::rows_update
51 | #' @inherit dplyr::rows_update return details
52 | #' @export
53 | e_rows_update <- function(
54 | x,
55 | y,
56 | by = NULL,
57 | ...,
58 | match,
59 | unmatched = c("error", "ignore"),
60 | copy = FALSE,
61 | in_place = FALSE){
62 | UseMethod("e_rows_update")
63 | }
64 |
65 | #' @inherit e_rows_update
66 | #' @importFrom dplyr rows_update
67 | #' @export
68 | e_rows_update.default <- function(
69 | x,
70 | y,
71 | by = NULL,
72 | ...,
73 | match = match,
74 | unmatched = c("error", "ignore"),
75 | copy = FALSE,
76 | in_place = FALSE){
77 | dplyr::rows_update(
78 | x = x,
79 | y = y,
80 | by = by,
81 | ... ,
82 | match = match,
83 | unmatched = unmatched,
84 | copy = copy,
85 | in_place = in_place
86 | )
87 | }
88 |
89 | #' Start a transaction for a tibble
90 | #' @param tbl `tbl`
91 | #'
92 | #' @author Jasper Schelfhout
93 | beginTransaction <- function(tbl){
94 | if(inherits(tbl, "tbl_dbi")){
95 | DBI::dbBegin(tbl$src$con)
96 | }
97 | }
98 |
99 | #' Start a transaction for a tibble
100 | #' @param tbl `tbl`
101 | #'
102 | #' @author Jasper Schelfhout
103 | commitTransaction <- function(tbl){
104 | if(inherits(tbl, "tbl_dbi")){
105 | DBI::dbCommit(tbl$src$con)
106 | }
107 | }
108 |
109 | #' Start a transaction for a tibble
110 | #' @param tbl `tbl`
111 | #'
112 | #' @author Jasper Schelfhout
113 | rollbackTransaction <- function(tbl){
114 | if(inherits(tbl, "tbl_dbi")){
115 | DBI::dbRollback(tbl$src$con)
116 | }
117 | }
--------------------------------------------------------------------------------
/editbl/R/tbl_dbi.R:
--------------------------------------------------------------------------------
1 | #' rows_insert implementation for DBI backends.
2 | #'
3 | #' @examples
4 | #' library(dplyr)
5 | #'
6 | #' # Set up a test table
7 | #' conn <- DBI::dbConnect(RSQLite::SQLite(), ":memory:")
8 | #' artists_df <- data.frame(
9 | #' ArtistId = c(1,2),
10 | #' Name = c("AC/DC", "The Offspring")
11 | #' )
12 | #' DBI::dbWriteTable(conn, "Artist", artists_df)
13 | #'
14 | #' # Insert new row
15 | #' artists <- tbl(conn, "Artist")
16 | #' DBI::dbBegin(conn)
17 | #' e_rows_insert(artists,
18 | #' data.frame(ArtistId = 999, Name = "testArtist"),
19 | #' in_place = TRUE)
20 | #'
21 | #' DBI::dbRollback(conn)
22 | #' DBI::dbDisconnect(conn)
23 | #'
24 | #' @inheritParams e_rows_insert
25 | #' @inherit e_rows_insert return details
26 | #'
27 | #' @author Jasper Schelfhout
28 | #' @export
29 | e_rows_insert.tbl_dbi <- function(x, y, by = NULL, ..., copy = FALSE, in_place = FALSE){
30 | if(!in_place){
31 | stop("Can only edit in place")
32 | }
33 |
34 | if(copy){
35 | stop("copy TRUE not supported yet.")
36 | }
37 |
38 | if(is.null(by)){
39 | by <- colnames(x)[1]
40 | }
41 |
42 | if(in_place){
43 | lapply(seq_len(nrow(y)), function(i){
44 | rowInsert(
45 | conn = x$src$con,
46 | table = get_db_table_name(x),
47 | values = as.list(y[i,])
48 | )
49 | })
50 | return(invisible(x))
51 | }
52 | return(x)
53 | }
54 |
55 | #' rows_update implementation for DBI backends.
56 | #' @inheritParams e_rows_update
57 | #' @inherit e_rows_update return details
58 | #' @examples
59 | #' library(dplyr)
60 | #'
61 | #' # Set up a test table
62 | #' conn <- DBI::dbConnect(RSQLite::SQLite(), ":memory:")
63 | #' artists_df <- data.frame(
64 | #' ArtistId = c(1,2),
65 | #' Name = c("AC/DC", "The Offspring")
66 | #' )
67 | #' DBI::dbWriteTable(conn, "Artist", artists_df)
68 | #'
69 | #' # Update rows without changing the key.
70 | #' artists <- tbl(conn, "Artist")
71 | #' DBI::dbBegin(conn)
72 | #' y <- data.frame(ArtistId = 1, Name = "DC/AC")
73 | #' e_rows_update(
74 | #' x = artists,
75 | #' y = y,
76 | #' by = "ArtistId",
77 | #' in_place = TRUE)
78 | #' DBI::dbRollback(conn)
79 | #'
80 | #' # Update key values of rows.
81 | #' DBI::dbBegin(conn)
82 | #' y <- data.frame(ArtistId = 999, Name = "DC/AC")
83 | #' match <- list(
84 | #' x = data.frame("ArtistId" = 1),
85 | #' y = data.frame("ArtistId" = 999)
86 | #' )
87 | #' e_rows_update(
88 | #' x = artists,
89 | #' y = y,
90 | #' match = match,
91 | #' by = "ArtistId",
92 | #' in_place = TRUE)
93 | #' DBI::dbRollback(conn)
94 | #' DBI::dbDisconnect(conn)
95 | #'
96 | #' @author Jasper Schelfhout
97 | #' @export
98 | e_rows_update.tbl_dbi <- function(x, y, by = NULL, match = NULL,..., copy = FALSE, in_place = FALSE){
99 | if(!in_place){
100 | stop("Can only edit in place")
101 | }
102 |
103 | if(copy){
104 | stop("copy TRUE not supported yet.")
105 | }
106 |
107 | if(is.null(by)){
108 | by <- colnames(x)[1]
109 | }
110 |
111 | if(is.null(match)){
112 | match <- list(x = y[by], y = y[by])
113 | }
114 |
115 | lapply(seq_len(nrow(match$y)), function(i){
116 | yMatch <- match$y[i,, drop = FALSE]
117 | values <- merge(yMatch, y, by = by)
118 | xMatch <- match$x[i,, drop = FALSE]
119 |
120 | rowUpdate(
121 | conn = x$src$con,
122 | table = get_db_table_name(x),
123 | values = as.list(values),
124 | where = as.list(xMatch)
125 | )
126 | })
127 | return(invisible(x))
128 | }
129 |
130 | #' Get name of the tbl in the database
131 | #' @param x `tbl_dbi`
132 | #' @return SQL, the table name as used in the database
133 | get_db_table_name <- function(x){
134 | name <- coalesce(
135 | tryCatch({dbplyr::remote_table(x)}, error = function(e){NULL}), # Since dbplyr 2.4.0
136 | tryCatch({x$lazy_query$x$x}, error = function(e){NULL}), # tbl can apparently get more nested
137 | tryCatch({x$lazy_query$x}, error = function(e){NULL}), # normal place
138 | tryCatch({x$ops$x}, error = function(e){NULL}) # old tbl compatibility
139 | )
140 |
141 | if(is.null(name)){
142 | stop("Can not find in-database table name based on the tibble object.")
143 | }
144 |
145 | if(inherits(name,'dbplyr_table_ident')){
146 | attributes <- unclass(name)
147 | id <- list()
148 | if(!is.na(attributes$table)){
149 | id$table <- attributes$table
150 | }
151 | if(!is.na(attributes$schema)){
152 | id$schema <- attributes$schema
153 | }
154 | if(!is.na(attributes$catalog)){
155 | id$catalog <- attributes$catalog
156 | }
157 | name <- DBI::dbQuoteIdentifier(
158 | conn = x$src$con,
159 | x = do.call(DBI::Id, id)
160 | )
161 | } else {
162 | name <- DBI::SQL(name)
163 | }
164 |
165 | return(name)
166 | }
167 |
168 | #' Add a row to a table in the database.
169 | #'
170 | #' @param conn database connection object as given by \code{\link[DBI]{dbConnect}}.
171 | #' @param table character
172 | #' @param values named list, row to add. Names are database column names. Unspecified columns will get database defaults.
173 | #' @return integer number of affected rows.
174 | rowInsert <- function (
175 | conn,
176 | table,
177 | values){
178 |
179 | for(i in seq_along(values)){
180 | values[[i]] <- castToSQLSupportedType(values[[i]])
181 | }
182 |
183 | # Treat missing values as NULL and use database default.
184 | # This might need to be a flag in the function.
185 | # E.g. there is a difference between wanting to insert NA or let a database generate a default.
186 | values <- values[!is.na(values)]
187 |
188 | query <- glue::glue_sql(
189 | .con = conn,
190 | "INSERT INTO {`table`} ({`columns`*}) VALUES ({values*})",
191 | table = table,
192 | columns = names(values),
193 | values = values
194 | )
195 |
196 | DBI::dbExecute(
197 | conn,
198 | query)
199 | }
200 |
201 |
202 | #' Update rows in the database.
203 | #'
204 | #' @param conn database connection object as given by \code{\link[DBI]{dbConnect}}.
205 | #' @param table character
206 | #' @param values named list, values to be set. Names are database column names.
207 | #' @param where named list, values to filter on. Names are database column names. If NULL no filter is applied.
208 | #' @return integer number of affected rows.
209 | rowUpdate <- function (
210 | conn,
211 | table,
212 | values,
213 | where){
214 | values <- lapply(values, function(x){
215 | castToSQLSupportedType(x)
216 | })
217 |
218 | query <- glue::glue_sql(
219 | .con =conn,
220 | "UPDATE {`table`} SET ({`columns`*}) = ({values*})",
221 | table = table,
222 | columns = names(values),
223 | values = values
224 | )
225 |
226 | if(length(where)){
227 | whereSQL <- lapply(seq_along(where), function(i){
228 | whereSQL(
229 | conn = conn,
230 | table = table,
231 | column = names(where)[i],
232 | operator = "in",
233 | values = where[[i]]
234 | )
235 | })
236 | whereSQL <- paste(whereSQL, collapse = " AND ")
237 | query <- paste(query, "WHERE", whereSQL)
238 | }
239 |
240 | DBI::dbExecute(
241 | conn,
242 | query)
243 | }
244 |
245 | #' Cast the data type to something supported by SQL.
246 | #' @param x single value or vector of values
247 | #' @return x, possibly cast to different type
248 | #'
249 | #' @author Jasper Schelfhout
250 | castToSQLSupportedType <- function(x){
251 | if(!inherits(x, c("numeric", "character"))){
252 | x <- as.character(x)
253 | }
254 | x
255 | }
256 |
257 | #' Generate where sql
258 | #'
259 | #' @param conn database connection object as given by \code{\link[DBI]{dbConnect}}.
260 | #' @param table character table name (or alias used in query)
261 | #' @param column character column of table
262 | #' @param values character vector of values
263 | #' @param operator character
264 | #' @return character sql
265 | #'
266 | #' @author Jasper Schelfhout
267 | whereSQL <- function(
268 | conn,
269 | table,
270 | column,
271 | operator = 'in',
272 | values = NULL){
273 |
274 | values <- castToSQLSupportedType(values)
275 |
276 | if(length(values) > 0){
277 |
278 | singleOperators <- c("like", "not like", "=", "!=", ">", "<", ">=", "<=", "<>")
279 | multiOperators <- c("in", "not in")
280 | if(length(values) == 1){
281 | operators <- c(singleOperators, multiOperators)
282 | } else {
283 | operators <- multiOperators
284 | }
285 |
286 | if(!operator %in% operators){
287 | stop(sprintf("Should use on of following operators: %s",
288 | paste(operators, collapse = ", ")))
289 | }
290 |
291 | sql <- sprintf("{`table`}.{`column`} %s ({values*})",
292 | operator)
293 | sql <- glue::glue_sql(
294 | .con = conn,
295 | sql,
296 | table = table,
297 | column = column,
298 | operator = operator,
299 | values = values
300 | )
301 | } else{
302 | sql = "TRUE"
303 | }
304 | sql
305 | }
306 |
--------------------------------------------------------------------------------
/editbl/R/tbl_df.R:
--------------------------------------------------------------------------------
1 | #' rows_update implementation for data.frame backends.
2 | #' @inheritParams e_rows_update
3 | #' @inherit e_rows_update return details
4 | #' @author Jasper Schelfhout
5 | #' @export
6 | e_rows_update.data.frame <- function(x, y, by = NULL, match = NULL,..., copy = FALSE, in_place = FALSE){
7 | if(in_place){
8 | stop("Can not edit in place")
9 | }
10 |
11 | if(is.null(by)){
12 | by <- colnames(x)[1]
13 | }
14 |
15 | if(is.null(match)){
16 | match <- list(x = y[by], y = y[by])
17 | }
18 |
19 | idx <- vctrs::vec_match(match$x, x[by])
20 |
21 | bad <- which(is.na(idx))
22 | if (length(bad)) {
23 | stop("Attempting to update missing rows.")
24 | }
25 |
26 | x[idx, names(y)] <- y
27 |
28 | x
29 | }
30 |
--------------------------------------------------------------------------------
/editbl/R/tbl_dtplyr_step.R:
--------------------------------------------------------------------------------
1 | #' rows_insert implementation for `data.table` backends.
2 | #'
3 | #' @inherit e_rows_insert
4 | #'
5 | #' @author Jasper Schelfhout
6 | #' @export
7 | e_rows_insert.dtplyr_step <- function(x, y, by = NULL, ..., copy = FALSE, in_place = FALSE){
8 | x_dt <- data.table::copy(data.table::as.data.table(x))
9 | y_dt <- data.table::as.data.table(y)
10 |
11 | if(is.null(by)){
12 | by <- colnames(x)[1]
13 | }
14 |
15 | if(in_place){
16 | stop("Adding rows by reference to a data.table is not supported yet.")
17 | } else {
18 | result <- dtplyr::lazy_dt(rbind(x_dt,y_dt))
19 | }
20 |
21 | return(result)
22 | }
23 |
24 | #' rows_delete implementation for data.table backends.
25 | #' @inheritParams dplyr::rows_delete
26 | #' @inherit dplyr::rows_delete return details
27 | #' @export
28 | #' @author Jasper Schelfhout
29 | rows_delete.dtplyr_step <- function(x, y, by = NULL, ..., unmatched, copy = FALSE, in_place = FALSE){
30 | x_dt <- data.table::copy(data.table::as.data.table(x))
31 | y_dt <- data.table::as.data.table(y)
32 |
33 | if(!nrow(y_dt)){
34 | return(x)
35 | }
36 |
37 | if(is.null(by)){
38 | by <- colnames(x_dt)[1]
39 | }
40 |
41 | if(in_place){
42 | stop("In place deletes for data.tables are not supported yet.
43 | See issue: https://github.com/Rdatatable/data.table/issues/635")
44 | }
45 |
46 | matches <- unlist(lapply(seq_len(nrow(x_dt)), function(i){
47 | nrow(merge(x_dt[i,], y_dt, by = by)) > 0
48 | }))
49 |
50 | x_dt <- x_dt[!matches,]
51 | result <- dtplyr::lazy_dt(x_dt)
52 |
53 | result
54 | }
55 |
56 |
57 |
58 | #' rows_update implementation for data.table backends.
59 | #' @inherit e_rows_update
60 | #' @author Jasper Schelfhout
61 | #' @export
62 | e_rows_update.dtplyr_step <- function(x, y, by = NULL, match = NULL,..., copy = FALSE, in_place = FALSE){
63 | args <- c(as.list(environment()), list(...))
64 |
65 | x_dt <- data.table::as.data.table(x)
66 | y_dt <- data.table::as.data.table(y)
67 |
68 | if(!nrow(y_dt)){
69 | return(x)
70 | }
71 |
72 | if(is.null(by)){
73 | by <- colnames(x_dt)[1]
74 | }
75 |
76 | if(is.null(args$match)){ # Be explicit about argument since otherwhise base::match will used.
77 | match <- list(x = y_dt[,by, with = FALSE, drop = FALSE],
78 | y = y_dt[,by, with = FALSE, drop = FALSE])
79 | }
80 |
81 | for (i in seq_len(nrow(match$y))){
82 | yMatch <- match$y[i,, drop = FALSE]
83 | values <- merge(yMatch, y, by = by)[,colnames(y_dt)]
84 | xMatch <- match$x[i,, drop = FALSE]
85 |
86 | xRows <- which(unlist(lapply(seq_len(nrow(x_dt)), function(i){
87 | nrow(merge(x_dt[i,], xMatch, by = by)) > 0
88 | })))
89 |
90 | x_dt[xRows,] <- values
91 | }
92 |
93 | result <- dtplyr::lazy_dt(x_dt)
94 | return(invisible(result))
95 | }
96 |
--------------------------------------------------------------------------------
/editbl/R/utils.R:
--------------------------------------------------------------------------------
1 | #' @importFrom tibble as_tibble
2 | #' @import fontawesome
3 | testImports <- NULL
4 |
5 | #' Cast columns to the type of the template
6 | #'
7 | #' @param template `data.frame`
8 | #' @param x `data.frame`
9 | #'
10 | #' @details only affects columns in both the template and x
11 | coerceColumns <- function(template, x){
12 | for(col in intersect(names(x), names(template))){
13 | x[[col]] <- coerceValue(x[[col]], template[[col]])
14 | }
15 | x
16 | }
17 |
18 | #' `DT::coerceValue` with better `POSIXct` support
19 | #'
20 | #' @details Will assume UTC in case no timezone is specified.
21 | #'
22 | #' @inheritParams DT::coerceValue
23 | #' @importFrom DT coerceValue
24 | #'
25 | #' @author Jasper Schelfhout
26 | coerceValue <- function(val,old){
27 |
28 | if (inherits(old, c('POSIXlt', 'POSIXct'))) {
29 |
30 | # Try a bunch of formats supported by ISO
31 | newVal <- strptime(val, '%Y-%m-%dT%H:%M:%SZ', tz = 'UTC')
32 | if(is.na(newVal)){
33 | newVal <- strptime(val, '%Y-%m-%d %H:%M:%SZ', tz = 'UTC')
34 | }
35 | if(is.na(newVal)){
36 | newVal <- strptime(val, '%Y-%m-%d %H:%M:%S', tz = 'UTC')
37 | }
38 | if(is.na(newVal)){
39 | newVal <- strptime(val, '%Y-%m-%dT%H:%M:%S', tz = 'UTC')
40 | }
41 |
42 | if (inherits(old, 'POSIXlt')) return(newVal)
43 | return(as.POSIXct(newVal))
44 | } else if(inherits(old, "logical")){
45 | newVal <- as.logical(val)
46 | } else {
47 | return(DT::coerceValue(val,old))
48 | }
49 | }
50 |
51 | #' Connect to a database.
52 | #'
53 | #' @details Connects by default to a test SQLite database originally obtained here:
54 | #' [chinook_git](https://github.com/lerocha/chinook-database/blob/master/ChinookDatabase/DataSources/Chinook_Sqlite.sqlite)
55 | #'
56 | #' @param dbname `character(0)`
57 | #' @param drv database driver
58 | #' @param ... arguments passed to `DBI::dbConnect`
59 | #' @importFrom utils packageName
60 | #' @examples
61 | #'
62 | #' conn <- connectDB()
63 | #' DBI::dbDisconnect(conn)
64 | #'
65 | #' @return database connection
66 | #' @export
67 | connectDB <- function(
68 | dbname = system.file("extdata", "chinook.sqlite", package = utils::packageName()),
69 | drv = RSQLite::SQLite(),
70 | ...
71 | ){
72 | DBI::dbConnect(
73 | dbname = dbname,
74 | drv = drv,
75 | ...
76 | )
77 | }
78 |
79 | #' Return first non `NULL` argument
80 | #' @param ... set of arguments
81 | #'
82 | #' @author Jasper Schelfhout
83 | coalesce <- function(...){
84 | args <- list(...)
85 | result <- NULL
86 | for(arg in args){
87 | if(!is.null(arg)){
88 | return(arg)
89 | }
90 | }
91 | return(result)
92 | }
93 |
94 | #' Cast columns in `data.frame` to editable types in datatable
95 | #' @param data `data.frame`
96 | #' @param cols `character` columns to perform casting on.
97 | #' @return `data.frame` with some columns cast to another type
98 | #'
99 | #' @author Jasper Schelfhout
100 | castForDisplay <- function(data, cols = colnames(data)){
101 | stopifnot(cols %in% colnames(data))
102 |
103 | for(col in cols){
104 | if(!inherits(data[[col]], c("integer", "character"))){
105 | data[[col]] <- as.character(data[[col]])
106 | }
107 | }
108 | data
109 | }
110 |
111 | #' Cast data to tbl
112 | #' @param data object
113 | #' @return tbl
114 | #'
115 | #' @importFrom dplyr as_tibble is.tbl
116 | #'
117 | #' @author Jasper Schelfhout
118 | castToTbl <- function(data){
119 | if(dplyr::is.tbl(data)){
120 | result <- data
121 | } else if(inherits(data, 'data.table')){
122 | result <- dtplyr::lazy_dt(data)
123 | } else {
124 | result <- dplyr::as_tibble(data)
125 | }
126 | result
127 | }
128 |
129 | #' Cast `tbl` or `data.frame` x to the types of the template
130 | #'
131 | #' @details If template is a `tbl` with database source, convert to an in-memory tibble with same data types instead.
132 | #' @details Rownames might differ or get lost.
133 | #'
134 | #' @param x `data.frame`, `tbl` or `data.table`
135 | #' @param template `data.frame`, `tbl` or `data.table`
136 | #' @return object containing data of x in the class and structure of the template.
137 | #'
138 | #' @author Jasper Schelfhout
139 | castToTemplate <- function(x, template){
140 | if(!all(base::colnames(x) == base::colnames(template)))
141 | stop("Template and casted tbl should have exactly the same colums")
142 |
143 | rowNames <- attr(x, 'row.names')
144 |
145 | templateRow <- dplyr::collect(dplyr::filter(template, dplyr::row_number()==1))
146 | # In case the template is empty, templateRow will only contain the format (without content)
147 | # Add an empty row to make sure that the format will be correctly inherited
148 | if (nrow(templateRow) == 0) {
149 | templateRow[1,] <- NA
150 | }
151 | result <- rbind(templateRow,x)[-1,]
152 |
153 | # Tbl doesn't properly support row names
154 | if(!inherits(template, 'tbl')){
155 | try({rownames(result) <- rowNames}, silent = TRUE)
156 | }
157 |
158 | result
159 | }
160 |
161 | #' Cast tbl to class of template
162 | #' @param tbl `tbl`
163 | #' @param template tabular object like `data.frame` or `data.table` or `tbl`.
164 | #' @return tbl cast to the type of template
165 | #' @importFrom dplyr is.tbl
166 | #'
167 | #' @author Jasper Schelfhout
168 | castFromTbl <- function(tbl, template){
169 | if(dplyr::is.tbl(template)){
170 | result <- tbl
171 | } else {
172 | result <- castToTemplate(tbl,template)
173 | }
174 | result
175 | }
176 |
177 | #' Standardize colnames argument to the format of named character vector
178 | #' @inheritParams eDT
179 | #' @importFrom dplyr tbl_vars
180 | #' @return named character vector
181 | #'
182 | #' @author Jasper Schelfhout
183 | standardizeArgument_colnames <- function(colnames, data){
184 | if(is.null(colnames)){
185 | result <- as.character(dplyr::tbl_vars(data))
186 | names(result) <- result
187 | } else if (is.numeric(colnames)) {
188 | result <- dplyr::tbl_vars(data)[colnames]
189 | names(result) = names(colnames)
190 | } else if (is.character(colnames)){
191 | if(!is.null(names(colnames))){
192 | result <- colnames
193 | } else {
194 | result <- dplyr::tbl_vars(data)[seq_len(length(colnames))]
195 | names(result) <- colnames
196 | }
197 | }
198 | result
199 | }
200 |
201 | #' Standardized editable argument to be in the form of a list
202 | #' @inheritParams eDT
203 | #' @return list of the form `list(target = foo, ...)`
204 | #'
205 | #' @author Jasper Schelfhout
206 | standardizeArgument_editable <- function(
207 | editable,
208 | data
209 | ){
210 | if(is.logical(editable)){
211 | if(editable){
212 | return(list(target = "cell"))
213 | } else {
214 | return(list(target = "cell", disable = list(columns = seq_len(ncol(data)))))
215 | }
216 | }
217 |
218 | if(is.character(editable)){
219 | return(list(target = editable))
220 | }
221 |
222 | if(is.list(editable)){
223 | return(editable)
224 | }
225 |
226 | stop("editable is not in a standard format.")
227 | }
228 |
229 | #' Replace instances of integer64 with actual NA values instead of weird default 9218868437227407266
230 | #'
231 | #' @details [github issue](https://github.com/Rdatatable/data.table/issues/4561)
232 | #'
233 | #' @param x `data.frame`
234 | #' @return x with `integer64` columns set to `bit64::as.integer64(NA)`
235 | #'
236 | #' @author Jasper Schelfhout
237 | fixInteger64 <- function(x){
238 | for(column in dplyr::tbl_vars(x)){
239 | if(inherits(x[[column]], "integer64")){
240 | x[[column]] <- rep(bit64::as.integer64(NA), nrow(x))
241 | }
242 | }
243 | x
244 | }
245 |
246 | #' Get types of columns in a tbl
247 | #' @param tbl `tbl`
248 | #' @return named list with types of the colums
249 | #'
250 | #' @importFrom dplyr type_sum collect
251 | #' @importFrom utils head
252 | #'
253 | #' @author Jasper Schelfhout
254 | getColumnTypeSums <- function(tbl){
255 | tbl %>%
256 | head %>%
257 | collect %>%
258 | lapply(dplyr::type_sum)
259 | }
260 |
261 | #' Generate a custom button for \code{\link{eDT}}
262 | #'
263 | #' @details
264 | #' Combines elements of `shiny::actionButton` and [datatable options](https://datatables.net/reference/option/)
265 | #'
266 | #' @param id `character(1)`, namespaced id
267 | #' @param label `character(1)`
268 | #' @param icon `shiny::icon`
269 | #' @param disabled `logical`. Whether or not the button should start in a disabled state.
270 | #' @return list to be used in `eDT(options = list(buttons = xxx))`
271 | #'
272 | #' @examples
273 | #' if(interactive()){
274 | #'
275 | #' ui <- eDTOutput("data")
276 | #' server <- function(input,output,session){
277 | #' b <- customButton('print', label = 'print')
278 | #' eDT_result <- eDT(id = "data", mtcars, options = list(buttons = list("save", b)))
279 | #' observeEvent(input$print,{
280 | #' print(eDT_result$state())
281 | #' })
282 | #' }
283 | #' shinyApp(ui,server)
284 | #' }
285 | #'
286 | #' @author Jasper Schelfhout
287 | #' @export
288 | customButton <- function(id, label, icon = "", disabled = FALSE){
289 | list(
290 | attr = list(
291 | id = id,
292 | class = "btn btn-default action-button shiny-bound-input",
293 | disabled = disabled
294 | ),
295 | extend = "",
296 | text = paste(as.character(icon), label, sep = " "),
297 | action = DT::JS(sprintf("function (e, dt, node, config ) {
298 | Shiny.setInputValue('%s', true, {priority: 'event'});
299 | }", id))
300 | )
301 | }
302 |
303 |
304 | #' Overwrite default settings with provided settings
305 | #' @param defaults named character vector
306 | #' @param settings named character vector
307 | #' @return named character vector
308 | #'
309 | #' @author Jasper Schelfhout
310 | overwriteDefaults <- function(defaults,settings){
311 |
312 | if(is.null(settings)){
313 | return(defaults)
314 | }
315 |
316 | result <- defaults
317 | for(entry in names(settings)){
318 | result[entry] <- settings[entry]
319 | }
320 | result
321 | }
322 |
--------------------------------------------------------------------------------
/editbl/README.md:
--------------------------------------------------------------------------------
1 | # {editbl}: DT extension for CRUD
2 |
3 | [](https://cran.r-project.org/package=editbl)
4 | [](https://github.com/openanalytics/editbl/actions/workflows/check-standard.yaml)
5 | [](https://app.codecov.io/gh/openanalytics/editbl)
6 | 
7 |
8 | `editbl` ('*edit [tibble](https://cran.r-project.org/package=tibble)*') allows you to modify tables in a spreadsheet-like fashion. Not just in-memory `data.frame` objects, but also
9 | data living in a database.
10 |
11 | ## Installation
12 |
13 | * From CRAN:
14 |
15 | ```
16 | install.packages('editbl')
17 | ```
18 |
19 | * Latest development version:
20 |
21 | ```
22 | remotes::install_github("https://github.com/openanalytics/editbl", ref = "main", subdir = "editbl")
23 | ```
24 |
25 | ## Get started
26 |
27 | Choose a dataset of your liking and use `eDT` to interactively explore and modify it!
28 |
29 | ```
30 | modifiedData <- editbl::eDT(mtcars)
31 | print(modifiedData)
32 | ```
33 |
34 |
35 | Run some demo apps
36 |
37 | ```
38 | editbl::runDemoApp()
39 | ```
40 | 
41 |
42 | More introductory examples can be found [here](https://github.com/openanalytics/editbl/blob/main/editbl/R/demoApp.R).
43 | Advanced examples can be found in the [vignettes](https://github.com/openanalytics/editbl/tree/main/editbl/vignettes).
44 |
45 |
46 | ## Features
47 |
48 | ### Overview of main features
49 |
50 | * Supporting multiple backends and in-place editing
51 | * Customizable (lightweight [DT](https://CRAN.R-project.org/package=DT) wrapper)
52 | * Easy integration in [shiny](https://cran.r-project.org/package=shiny) apps
53 | * Undo/redo button
54 | * Copy rows
55 | * Drag cells
56 | * No need to have all data in-memory
57 | * Tackles challenges such as enforcing foreign keys and hiding of surrogate keys
58 | * Transactional commits (currently for `tbl_dbi` class and non in-place editing)
59 | * Default values for new rows (UUID's, 'current date', 'inserted by', ...)
60 | * Possible to set row level security
61 |
62 | ### Constraints and normalized tables
63 |
64 | Sometimes you want to restrict certain columns of your table to only contain specific values.
65 | Many of these restrictions would be implemented at database level through the use of foreign keys to other tables.
66 |
67 | `editbl` allows you to specify similar rules through the use of `foreignTbls` as an argument to `eDT()`.
68 | Note that you can additionally hide surrogate keys by the use of `naturalKey` and `columnDefs` if you wish to.
69 |
70 | ```
71 | a <- tibble::tibble(
72 | first_name = c("Albert","Donald","Mickey"),
73 | last_name_id = c(1,2,2)
74 | )
75 |
76 | b <- foreignTbl(
77 | a,
78 | tibble::tibble(
79 | last_name = c("Einstein", "Duck", "Mouse"),
80 | last_name_id = c(1,2,3)
81 | ),
82 | by = "last_name_id",
83 | naturalKey = "last_name"
84 | )
85 |
86 | eDT(a,
87 | foreignTbls = list(b),
88 | options = list(columnDefs = list(list(visible=FALSE, targets="last_name_id")))
89 | )
90 | ```
91 |
92 | ### Support for different backends
93 |
94 | `dplyr` code is used for all needed data manipulations and it is recommended to pass on your data as a `tbl`.
95 | This allows editbl to support multiple backends through the usage of other packages like `dtplyr`, `dbplyr` etc.
96 |
97 | In case you pass on other tabular objects like `data.frame` or `data.table` the function will internally automatically
98 | cast back and forth to `tbl`. Small side effects may occur because of this (like loosing rownames), so it might be better
99 | to cast yourself to `tbl` explicitly first.
100 |
101 | ```
102 | # tibble support
103 | modifiedData <- editbl::eDT(tibble::as_tibble(mtcars))
104 |
105 | # data.frame support
106 | modifiedData <- editbl::eDT(mtcars)
107 |
108 | # data.table support
109 | modifiedData <- editbl::eDT(data.table::data.table(mtcars))
110 |
111 | # database support
112 | tmpFile <- tempfile(fileext = ".sqlite")
113 | file.copy(system.file("extdata", "chinook.sqlite", package = 'editbl'), tmpFile)
114 | conn <- editbl::connectDB(dbname = tmpFile)
115 | modifiedData <- editbl::eDT(dplyr::tbl(conn, "Artist"), in_place = TRUE)
116 | DBI::dbDisconnect(conn)
117 | unlink(tmpFile)
118 |
119 | ```
120 |
121 | Note that there are some custom methods in the package itself for `rows_update` / `rows_delete` / `rows_insert`. The goal
122 | would be to fully rely on `dplyr` once these functions are not experimental anymore and support all needed requirements.
123 | These functions also explain the high amount of 'suggested' packages, while the core functionality of `editbl` has few
124 | dependencies.
125 |
126 | ## Switching from DT
127 |
128 | Let's say you already use `DT::datatable()` to display your data, but want to switch to `editbl::eDT()` to be able to edit it. Would this be a lot of effort? No!
129 | In fact, `eDT()` accepts the exact same arguments. So it is almost as easy as replacing the functions and you are done.
130 | Should you run into problems take a look [here](https://github.com/openanalytics/editbl/blob/main/editbl/vignettes/howto_switch_from_DT.rmd) for some pointers to look out for.
131 |
132 | ## Notes
133 |
134 | * https://github.com/tidyverse/dtplyr/issues/260 might cause errors / warnings when using `eDT` with `dtplyr`. If possible convert to normal tibble first.
135 | * `editbl` assumes that **all rows in your table are unique**. This assumption is the key (ba dum tss) to allow for only having the data partially in memory.
136 | * `editbl` does not attempt to detect/give notifications on concurrent updates by other users to the same data, nor does it 'lock' the rows you are updating.
137 | It just sends its updates to the backend by matching on the keys of a row. If other users have in the meantime made conflicting adjustments,
138 | the changes you made might not be executed correctly or errors might be thrown.
139 |
140 | ## General future goals for this package
141 |
142 | * Full `dplyr` compatibility so support for different backends is easily facilitated. Now there are 2 methods (`e_rows_update`, `e_rows_insert`) that need to be implemented to support a new backend.
143 | * Full `DT` compatibility, including all extensions.
144 | * Better editing / display options for time values. E.g. control over timezone and format of display / storage + nicer input forms.
145 | * Any addition that supports the concept of editing data as flexible/easy as possible while respecting backend schema's and constraints.
146 |
147 | ## References
148 |
149 | ### Alternatives
150 |
151 | These are other popular CRUD packages in R.
152 | Depending on your needs, they might be better alternatives.
153 |
154 | [**DataEditR**](https://cran.r-project.org/package=DataEditR)
155 |
156 | * Rstudio plugin
157 | * Really flexible excel-like feeling
158 | * Can only edit in-memory tables. Harder to support databases etc.
159 |
160 | [**editData**](https://cran.r-project.org/package=editData)
161 |
162 | * Rstudio plugin
163 | * Nice features in terms of editing (pop-ups, more buttons,...)
164 | * Can only edit in-memory tables. Harder to support databases etc.
165 |
166 | [**Editor**](https://editor.datatables.net/)
167 |
168 | * Premium datatable extension allowing for editing data.
169 |
170 | [**DT-Editor**](https://github.com/jienagu/DT-Editor)
171 |
172 | * data.table focused
173 |
174 | [**DTedit**](https://github.com/jbryer/DTedit)
175 |
176 | * `DT` extension
177 | * Very customizable (own callbacks)
178 | * Few dependencies
179 |
180 | ### Additional links:
181 | [CRAN `DT`](https://cran.r-project.org/package=DT)
182 |
183 | [CRAN `tibble`](https://cran.r-project.org/package=tibble)
184 |
185 | [Blogpost spreadsheets vs robust backends](https://www.openanalytics.eu/blog/2023/02/12/spreadsheets-backends-love/)
186 |
187 | [Blogpost buttons in DT](https://thatdatatho.com/adding-action-buttons-in-rows-of-dt-data-table-in-r-shiny/)
188 |
189 | [Blogpost shiny vs excel](https://appsilon.com/forget-about-excel-use-r-shiny-packages-instead/)
190 |
191 | [Generic CRUD application](https://github.com/garrylachman/ElectroCRUD)
192 |
193 | [Example SQLite database](https://www.sqlitetutorial.net/sqlite-sample-database/)
194 |
--------------------------------------------------------------------------------
/editbl/inst/NEWS:
--------------------------------------------------------------------------------
1 | 1.3.0
2 | o FIX: castToTemplate removed first row of adapted data in case the template was empty.
3 | o FEAT: Add a clone button for each row.
4 | 1.2.0
5 | o FIX: State variable of eDT output does not contain deleted rows anymore.
6 | o FEAT: Allow to order the columns of the full table (including columns from foreignTbls). ([issue](https://github.com/openanalytics/editbl/issues/5))
7 | 1.1.0
8 | o Allow to block edits and deletes with row-specific logic.
9 | o More performance since buttons get generated on row basis instead of dataset basis.
10 | o Adjustable renamed hidden utility columns ('_editbl_identity', '_editbl_deleted', '_editbl_status', '_editbl_buttons'). Prevents name clashes.
11 | 1.0.5
12 | o Use screenshots in vignettes for speed increase and to solve CRAN checks.
13 | 1.0.4
14 | o Fixed bug when using non-English locale ([issue](https://github.com/openanalytics/editbl/issues/3))
15 | o Added vignettes + revised documentation
16 | o Prevent greyout when dragging value with increment ([issue](https://github.com/openanalytics/editbl/issues/4)).
17 | 1.0.3
18 | o FIX: inputUI argument was not working, now passing on the selected row.
19 | o FIX: Evaluate defaults argument for each new row
20 | o FIX: Use correct column types for in_place `rows_delete` on databases.
21 | o FIX: Row dragging during an active filter / search now modifies the correct cells.
22 | 1.0.2
23 | o Fix compatibility with dbplyr 2.4.0. Adjust method to retrieve table identifier.
24 | 1.0.1
25 | o Export `rows_insert.default()` and `rows_update.default()`
26 | 1.0.0
27 | o Use `dbplyr::rows_delete.tbl_dbi` instead of custom implementation. (https://github.com/openanalytics/editbl/issues/1)
28 | o BREAKING: rename custom `rows_insert` and `rows_update` functions to `e_rows_insert` and `e_rows_update`. This to avoid
29 | conflicts with other packages. The default for both functions will still be `dplyr::rows_insert` and `dplyr::rows_update`.
30 | (https://github.com/openanalytics/editbl/issues/1). Reason for not using the dplyr functions directly is because currently `rows_update`
31 | does not allow updating of the keys (see `match` argument). Secondly, `rows_insert.tbl_dbi` does not complain about inserting duplicated rows.
32 | o BREAKING: do not provide direct connection to in-package database in runDemoApp(). It's cleaner to work on a copy of the file.
33 | o Support for `is.atomic(NULL) = FALSE` (https://stat.ethz.ch/pipermail/r-devel/2023-September/082892.html).
34 | 0.9.6
35 | o Adjustments in examples and documenation to meet CRAN standards
36 | 0.9.3
37 | o Fixes in documentation to get CRAN standard
38 | 0.9.2
39 | o Documentation / test adaptations to get CRAN standard.
40 | o Exporting `selectInputDT` module since it might be convenient for users of the package.
41 | 0.9.0
42 | o Hide rownames for selectInutDT in a cleaner way
43 | o Disable button column clicking id-specific instead of page-wide css
44 | o Add tests
45 | o More documentation
46 | o Remove breaking change warning introduced at 0.8.3
47 | 0.8.5
48 | o More explicit code instead of string evaluation
49 | o Fix reactivity in case of identical edits
50 | o Check classes of default values for columns
51 | 0.8.3
52 | o BREAKING: eDT returns list instead of reactive. Adjust to eDT()$result for old behavior.
53 | o eDT now returns the state of the unsaved data as eDT()$state
54 | o Add pagination selector by default. Is closer to native DT behavior
55 | o Bugfix: dragging rows used to only work on the first page, now works for all pages.
56 | 0.7.4
57 | o Make buttons part of datatable buttons extension. Allows for easy configuration and nicer display.
58 | 0.7.3
59 | o 'defaults' argument evaluated in calling environment for each now row. Allows for example id generation.
60 | o Support for editable TRUE / FALSE
61 | 0.7.2
62 | o Add 'defaults' argument to eDT
63 | 0.7.1
64 | o Bugfix selectInputDT()
65 | o Add `allowNew` argument to foreignTbl to support 'suggestions' instead of 'restrictions'.
66 | 0.7.0
67 | o Support multi-column natural key editing by table selection
68 | 0.6.6
69 | o Disable modal editing for non-editable columns.
70 | o Fix modal for NA logical values by using selectInput instead of checkbox.
71 | 0.6.5
72 | o checkForeignTbls() allow empty rows by default
73 | 0.6.4
74 | o Enforce fontawesome >= 0.4.0
75 | 0.6.3
76 | o fix reactivity problem when switching data
77 | o support editable = FALSE
78 | 0.6.2
79 | o use getNonNaturalKeys() to block editing of deducted columns
80 | 0.6.1
81 | o increase package test coverage + various fixes
82 | o checkForeignTbls() more verbose
83 | o error for utility column name clashes
84 | 0.5.5
85 | o fix integer64 NA values
86 | 0.5.2
87 | o Make input modal more integerated (follow colnames argument, hide hidden / non-editable columns)
88 | 0.5.1
89 | o shinyjs disabling of buttons
90 | o bugfixes POSIXct
91 | 0.5.0
92 | o automatic casting to and from `tbl`
93 | o edit buttons with modal
94 | 0.4.0
95 | o dev version
96 |
--------------------------------------------------------------------------------
/editbl/inst/extdata/artists.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openanalytics/editbl/603a4e2702f7183b4ce8447bc8b274f6c8f71942/editbl/inst/extdata/artists.xlsx
--------------------------------------------------------------------------------
/editbl/inst/extdata/chinook.sqlite:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openanalytics/editbl/603a4e2702f7183b4ce8447bc8b274f6c8f71942/editbl/inst/extdata/chinook.sqlite
--------------------------------------------------------------------------------
/editbl/man/addButtons.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/eDT.R
3 | \name{addButtons}
4 | \alias{addButtons}
5 | \title{Add modification buttons as a column}
6 | \usage{
7 | addButtons(
8 | df,
9 | columnName,
10 | ns,
11 | iCol = "i",
12 | canEditRow = TRUE,
13 | canDeleteRow = TRUE,
14 | canCloneRow = TRUE,
15 | statusCol = "_editbl_status"
16 | )
17 | }
18 | \arguments{
19 | \item{df}{\code{data.frame}}
20 |
21 | \item{columnName}{\code{character(1)}}
22 |
23 | \item{ns}{namespace function}
24 |
25 | \item{iCol}{\code{character(1)} name of column containing a unique identifier.}
26 |
27 | \item{canEditRow}{can be either of the following:
28 | \itemize{
29 | \item \code{logical}, e.g. TRUE or FALSE
30 | \item \code{function}. Needs as input an argument \code{row} which accepts a single row \code{tibble} and as output TRUE/FALSE.
31 | }}
32 |
33 | \item{canDeleteRow}{can be either of the following:
34 | \itemize{
35 | \item \code{logical}, e.g. TRUE or FALSE
36 | \item \code{function}. Needs as input an argument \code{row} which accepts a single row \code{tibble} and as output TRUE/FALSE.
37 | }}
38 |
39 | \item{canCloneRow}{can be either of the following:
40 | \itemize{
41 | \item \code{logical}, e.g. TRUE or FALSE
42 | \item \code{function}. Needs as input an argument \code{row} which accepts a single row \code{tibble} and as output TRUE/FALSE.
43 | }}
44 |
45 | \item{statusCol}{\code{character(1)} name of column with general status (e.g. modified or not).
46 | if \code{NULL}, the data is interpreted as 'unmodified'.}
47 | }
48 | \value{
49 | df with extra column containing buttons
50 | }
51 | \description{
52 | Add modification buttons as a column
53 | }
54 | \author{
55 | Jasper Schelfhout
56 | }
57 |
--------------------------------------------------------------------------------
/editbl/man/beginTransaction.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tbl.R
3 | \name{beginTransaction}
4 | \alias{beginTransaction}
5 | \title{Start a transaction for a tibble}
6 | \usage{
7 | beginTransaction(tbl)
8 | }
9 | \arguments{
10 | \item{tbl}{\code{tbl}}
11 | }
12 | \description{
13 | Start a transaction for a tibble
14 | }
15 | \author{
16 | Jasper Schelfhout
17 | }
18 |
--------------------------------------------------------------------------------
/editbl/man/canXXXRowTemplate.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/eDT.R
3 | \name{canXXXRowTemplate}
4 | \alias{canXXXRowTemplate}
5 | \title{Re-usable documentation}
6 | \usage{
7 | canXXXRowTemplate(canEditRow, canCloneRow, canDeleteRow)
8 | }
9 | \arguments{
10 | \item{canEditRow}{can be either of the following:
11 | \itemize{
12 | \item \code{logical}, e.g. TRUE or FALSE
13 | \item \code{function}. Needs as input an argument \code{row} which accepts a single row \code{tibble} and as output TRUE/FALSE.
14 | }}
15 |
16 | \item{canCloneRow}{can be either of the following:
17 | \itemize{
18 | \item \code{logical}, e.g. TRUE or FALSE
19 | \item \code{function}. Needs as input an argument \code{row} which accepts a single row \code{tibble} and as output TRUE/FALSE.
20 | }}
21 |
22 | \item{canDeleteRow}{can be either of the following:
23 | \itemize{
24 | \item \code{logical}, e.g. TRUE or FALSE
25 | \item \code{function}. Needs as input an argument \code{row} which accepts a single row \code{tibble} and as output TRUE/FALSE.
26 | }}
27 | }
28 | \description{
29 | Re-usable documentation
30 | }
31 |
--------------------------------------------------------------------------------
/editbl/man/castForDisplay.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{castForDisplay}
4 | \alias{castForDisplay}
5 | \title{Cast columns in \code{data.frame} to editable types in datatable}
6 | \usage{
7 | castForDisplay(data, cols = colnames(data))
8 | }
9 | \arguments{
10 | \item{data}{\code{data.frame}}
11 |
12 | \item{cols}{\code{character} columns to perform casting on.}
13 | }
14 | \value{
15 | \code{data.frame} with some columns cast to another type
16 | }
17 | \description{
18 | Cast columns in \code{data.frame} to editable types in datatable
19 | }
20 | \author{
21 | Jasper Schelfhout
22 | }
23 |
--------------------------------------------------------------------------------
/editbl/man/castFromTbl.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{castFromTbl}
4 | \alias{castFromTbl}
5 | \title{Cast tbl to class of template}
6 | \usage{
7 | castFromTbl(tbl, template)
8 | }
9 | \arguments{
10 | \item{tbl}{\code{tbl}}
11 |
12 | \item{template}{tabular object like \code{data.frame} or \code{data.table} or \code{tbl}.}
13 | }
14 | \value{
15 | tbl cast to the type of template
16 | }
17 | \description{
18 | Cast tbl to class of template
19 | }
20 | \author{
21 | Jasper Schelfhout
22 | }
23 |
--------------------------------------------------------------------------------
/editbl/man/castToFactor.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/foreignTbl.R
3 | \name{castToFactor}
4 | \alias{castToFactor}
5 | \title{Cast all columns that exist in a foreignTbl to factor}
6 | \usage{
7 | castToFactor(data, foreignTbls)
8 | }
9 | \arguments{
10 | \item{data}{\code{data.frame}}
11 |
12 | \item{foreignTbls}{list of foreign tbls as created by \code{\link{foreignTbl}}}
13 | }
14 | \value{
15 | data.frame
16 | }
17 | \description{
18 | Cast all columns that exist in a foreignTbl to factor
19 | }
20 | \details{
21 | Can be used to fixate possible options when editing.
22 | }
23 | \author{
24 | Jasper Schelfhout
25 | }
26 |
--------------------------------------------------------------------------------
/editbl/man/castToSQLSupportedType.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tbl_dbi.R
3 | \name{castToSQLSupportedType}
4 | \alias{castToSQLSupportedType}
5 | \title{Cast the data type to something supported by SQL.}
6 | \usage{
7 | castToSQLSupportedType(x)
8 | }
9 | \arguments{
10 | \item{x}{single value or vector of values}
11 | }
12 | \value{
13 | x, possibly cast to different type
14 | }
15 | \description{
16 | Cast the data type to something supported by SQL.
17 | }
18 | \author{
19 | Jasper Schelfhout
20 | }
21 |
--------------------------------------------------------------------------------
/editbl/man/castToTbl.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{castToTbl}
4 | \alias{castToTbl}
5 | \title{Cast data to tbl}
6 | \usage{
7 | castToTbl(data)
8 | }
9 | \arguments{
10 | \item{data}{object}
11 | }
12 | \value{
13 | tbl
14 | }
15 | \description{
16 | Cast data to tbl
17 | }
18 | \author{
19 | Jasper Schelfhout
20 | }
21 |
--------------------------------------------------------------------------------
/editbl/man/castToTemplate.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{castToTemplate}
4 | \alias{castToTemplate}
5 | \title{Cast \code{tbl} or \code{data.frame} x to the types of the template}
6 | \usage{
7 | castToTemplate(x, template)
8 | }
9 | \arguments{
10 | \item{x}{\code{data.frame}, \code{tbl} or \code{data.table}}
11 |
12 | \item{template}{\code{data.frame}, \code{tbl} or \code{data.table}}
13 | }
14 | \value{
15 | object containing data of x in the class and structure of the template.
16 | }
17 | \description{
18 | Cast \code{tbl} or \code{data.frame} x to the types of the template
19 | }
20 | \details{
21 | If template is a \code{tbl} with database source, convert to an in-memory tibble with same data types instead.
22 |
23 | Rownames might differ or get lost.
24 | }
25 | \author{
26 | Jasper Schelfhout
27 | }
28 |
--------------------------------------------------------------------------------
/editbl/man/checkForeignTbls.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/foreignTbl.R
3 | \name{checkForeignTbls}
4 | \alias{checkForeignTbls}
5 | \title{Check if all rows in tbl fufill \code{foreignTbl} constraints}
6 | \usage{
7 | checkForeignTbls(tbl, foreignTbls)
8 | }
9 | \arguments{
10 | \item{tbl}{\code{tbl}}
11 |
12 | \item{foreignTbls}{list of foreign tbls as created by \code{\link{foreignTbl}}}
13 | }
14 | \value{
15 | \code{logical} stating if tbl fufills all constraints imposed by all foreign tbls.
16 | }
17 | \description{
18 | Check if all rows in tbl fufill \code{foreignTbl} constraints
19 | }
20 | \author{
21 | Jasper Schelfhout
22 | }
23 |
--------------------------------------------------------------------------------
/editbl/man/coalesce.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{coalesce}
4 | \alias{coalesce}
5 | \title{Return first non \code{NULL} argument}
6 | \usage{
7 | coalesce(...)
8 | }
9 | \arguments{
10 | \item{...}{set of arguments}
11 | }
12 | \description{
13 | Return first non \code{NULL} argument
14 | }
15 | \author{
16 | Jasper Schelfhout
17 | }
18 |
--------------------------------------------------------------------------------
/editbl/man/coerceColumns.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{coerceColumns}
4 | \alias{coerceColumns}
5 | \title{Cast columns to the type of the template}
6 | \usage{
7 | coerceColumns(template, x)
8 | }
9 | \arguments{
10 | \item{template}{\code{data.frame}}
11 |
12 | \item{x}{\code{data.frame}}
13 | }
14 | \description{
15 | Cast columns to the type of the template
16 | }
17 | \details{
18 | only affects columns in both the template and x
19 | }
20 |
--------------------------------------------------------------------------------
/editbl/man/coerceValue.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{coerceValue}
4 | \alias{coerceValue}
5 | \title{\code{DT::coerceValue} with better \code{POSIXct} support}
6 | \usage{
7 | coerceValue(val, old)
8 | }
9 | \arguments{
10 | \item{val}{A character string.}
11 |
12 | \item{old}{An old value, whose type is the target type of \code{val}.}
13 | }
14 | \description{
15 | \code{DT::coerceValue} with better \code{POSIXct} support
16 | }
17 | \details{
18 | Will assume UTC in case no timezone is specified.
19 | }
20 | \author{
21 | Jasper Schelfhout
22 | }
23 |
--------------------------------------------------------------------------------
/editbl/man/commitTransaction.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tbl.R
3 | \name{commitTransaction}
4 | \alias{commitTransaction}
5 | \title{Start a transaction for a tibble}
6 | \usage{
7 | commitTransaction(tbl)
8 | }
9 | \arguments{
10 | \item{tbl}{\code{tbl}}
11 | }
12 | \description{
13 | Start a transaction for a tibble
14 | }
15 | \author{
16 | Jasper Schelfhout
17 | }
18 |
--------------------------------------------------------------------------------
/editbl/man/connectDB.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{connectDB}
4 | \alias{connectDB}
5 | \title{Connect to a database.}
6 | \usage{
7 | connectDB(
8 | dbname = system.file("extdata", "chinook.sqlite", package = utils::packageName()),
9 | drv = RSQLite::SQLite(),
10 | ...
11 | )
12 | }
13 | \arguments{
14 | \item{dbname}{\code{character(0)}}
15 |
16 | \item{drv}{database driver}
17 |
18 | \item{...}{arguments passed to \code{DBI::dbConnect}}
19 | }
20 | \value{
21 | database connection
22 | }
23 | \description{
24 | Connect to a database.
25 | }
26 | \details{
27 | Connects by default to a test SQLite database originally obtained here:
28 | \href{https://github.com/lerocha/chinook-database/blob/master/ChinookDatabase/DataSources/Chinook_Sqlite.sqlite}{chinook_git}
29 | }
30 | \examples{
31 |
32 | conn <- connectDB()
33 | DBI::dbDisconnect(conn)
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/editbl/man/createButtons.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/eDT.R
3 | \name{createButtons}
4 | \alias{createButtons}
5 | \title{Create buttons to modify the row.}
6 | \usage{
7 | createButtons(
8 | row,
9 | suffix,
10 | ns,
11 | canEditRow = TRUE,
12 | canDeleteRow = TRUE,
13 | canCloneRow = TRUE,
14 | statusCol = "_editbl_status"
15 | )
16 | }
17 | \arguments{
18 | \item{row}{\code{tibble} with single row}
19 |
20 | \item{suffix}{\code{character(1)}}
21 |
22 | \item{ns}{\code{character(1)} namespace}
23 |
24 | \item{canEditRow}{can be either of the following:
25 | \itemize{
26 | \item \code{logical}, e.g. TRUE or FALSE
27 | \item \code{function}. Needs as input an argument \code{row} which accepts a single row \code{tibble} and as output TRUE/FALSE.
28 | }}
29 |
30 | \item{canDeleteRow}{can be either of the following:
31 | \itemize{
32 | \item \code{logical}, e.g. TRUE or FALSE
33 | \item \code{function}. Needs as input an argument \code{row} which accepts a single row \code{tibble} and as output TRUE/FALSE.
34 | }}
35 |
36 | \item{canCloneRow}{can be either of the following:
37 | \itemize{
38 | \item \code{logical}, e.g. TRUE or FALSE
39 | \item \code{function}. Needs as input an argument \code{row} which accepts a single row \code{tibble} and as output TRUE/FALSE.
40 | }}
41 |
42 | \item{statusCol}{\code{character(1)} name of column with general status (e.g. modified or not).
43 | if \code{NULL}, the data is interpreted as 'unmodified'.}
44 | }
45 | \value{
46 | \code{character(1)} HTML
47 | }
48 | \description{
49 | Create buttons to modify the row.
50 | }
51 | \details{
52 | buttons used per row in the app.
53 | }
54 |
--------------------------------------------------------------------------------
/editbl/man/createCloneButtonHTML.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/eDT.R
3 | \name{createCloneButtonHTML}
4 | \alias{createCloneButtonHTML}
5 | \title{Generate HTML for an in-row clone button}
6 | \usage{
7 | createCloneButtonHTML(ns = "\%1$s", suffix = "\%2$s", disabled = FALSE)
8 | }
9 | \arguments{
10 | \item{ns}{\code{character(1)} namespace}
11 |
12 | \item{suffix}{\code{character(1)} id of the row}
13 |
14 | \item{disabled}{\code{logical(1)} wether or not the button has to be disabled}
15 | }
16 | \value{
17 | \code{character(1)} HTML
18 | }
19 | \description{
20 | Generate HTML for an in-row clone button
21 | }
22 |
--------------------------------------------------------------------------------
/editbl/man/createCloneButtonHTML_shiny.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/eDT.R
3 | \name{createCloneButtonHTML_shiny}
4 | \alias{createCloneButtonHTML_shiny}
5 | \title{Helper function to write HTML}
6 | \usage{
7 | createCloneButtonHTML_shiny(ns = "\%1$s", suffix = "\%2$s", disabled = FALSE)
8 | }
9 | \arguments{
10 | \item{ns}{\code{character(1)} namespace}
11 |
12 | \item{suffix}{\code{character(1)} id of the row}
13 |
14 | \item{disabled}{\code{logical(1)} wether or not the button has to be disabled}
15 | }
16 | \description{
17 | Helper function to write HTML
18 | }
19 | \details{
20 | only to be used interactively. sprintf() implementation
21 | is faster.
22 | }
23 | \seealso{
24 | createCloneButtonHTML
25 | }
26 |
--------------------------------------------------------------------------------
/editbl/man/createDeleteButtonHTML.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/eDT.R
3 | \name{createDeleteButtonHTML}
4 | \alias{createDeleteButtonHTML}
5 | \title{Generate HTML for an in-row delete button}
6 | \usage{
7 | createDeleteButtonHTML(ns = "\%1$s", suffix = "\%2$s", disabled = FALSE)
8 | }
9 | \arguments{
10 | \item{ns}{\code{character(1)} namespace}
11 |
12 | \item{suffix}{\code{character(1)} id of the row}
13 |
14 | \item{disabled}{\code{logical(1)} wether or not the button has to be disabled}
15 | }
16 | \value{
17 | \code{character(1)} HTML
18 | }
19 | \description{
20 | Generate HTML for an in-row delete button
21 | }
22 |
--------------------------------------------------------------------------------
/editbl/man/createDeleteButtonHTML_shiny.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/eDT.R
3 | \name{createDeleteButtonHTML_shiny}
4 | \alias{createDeleteButtonHTML_shiny}
5 | \title{Helper function to write HTML}
6 | \usage{
7 | createDeleteButtonHTML_shiny(ns = "\%1$s", suffix = "\%2$s", disabled = FALSE)
8 | }
9 | \arguments{
10 | \item{ns}{\code{character(1)} namespace}
11 |
12 | \item{suffix}{\code{character(1)} id of the row}
13 |
14 | \item{disabled}{\code{logical(1)} wether or not the button has to be disabled}
15 | }
16 | \description{
17 | Helper function to write HTML
18 | }
19 | \details{
20 | only to be used interactively. sprintf() implementation
21 | is faster.
22 | }
23 | \seealso{
24 | createEditButtonHTML
25 | }
26 |
--------------------------------------------------------------------------------
/editbl/man/createEditButtonHTML.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/eDT.R
3 | \name{createEditButtonHTML}
4 | \alias{createEditButtonHTML}
5 | \title{Generate HTML for an in-row edit button}
6 | \usage{
7 | createEditButtonHTML(ns, suffix, disabled = FALSE)
8 | }
9 | \arguments{
10 | \item{ns}{\code{character(1)} namespace}
11 |
12 | \item{suffix}{\code{character(1)} id of the row}
13 |
14 | \item{disabled}{\code{logical(1)} wether or not the button has to be disabled}
15 | }
16 | \value{
17 | \code{character(1)} HTML
18 | }
19 | \description{
20 | Generate HTML for an in-row edit button
21 | }
22 |
--------------------------------------------------------------------------------
/editbl/man/createEditButtonHTML_shiny.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/eDT.R
3 | \name{createEditButtonHTML_shiny}
4 | \alias{createEditButtonHTML_shiny}
5 | \title{Helper function to write HTML}
6 | \usage{
7 | createEditButtonHTML_shiny(ns = "\%1$s", suffix = "\%2$s", disabled = FALSE)
8 | }
9 | \arguments{
10 | \item{ns}{\code{character(1)} namespace}
11 |
12 | \item{suffix}{\code{character(1)} id of the row}
13 |
14 | \item{disabled}{\code{logical(1)} wether or not the button has to be disabled}
15 | }
16 | \description{
17 | Helper function to write HTML
18 | }
19 | \details{
20 | only to be used interactively. sprintf() implementation
21 | is faster.
22 | }
23 | \seealso{
24 | createEditButtonHTML
25 | }
26 |
--------------------------------------------------------------------------------
/editbl/man/customButton.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{customButton}
4 | \alias{customButton}
5 | \title{Generate a custom button for \code{\link{eDT}}}
6 | \usage{
7 | customButton(id, label, icon = "", disabled = FALSE)
8 | }
9 | \arguments{
10 | \item{id}{\code{character(1)}, namespaced id}
11 |
12 | \item{label}{\code{character(1)}}
13 |
14 | \item{icon}{\code{shiny::icon}}
15 |
16 | \item{disabled}{\code{logical}. Whether or not the button should start in a disabled state.}
17 | }
18 | \value{
19 | list to be used in \code{eDT(options = list(buttons = xxx))}
20 | }
21 | \description{
22 | Generate a custom button for \code{\link{eDT}}
23 | }
24 | \details{
25 | Combines elements of \code{shiny::actionButton} and \href{https://datatables.net/reference/option/}{datatable options}
26 | }
27 | \examples{
28 | if(interactive()){
29 |
30 | ui <- eDTOutput("data")
31 | server <- function(input,output,session){
32 | b <- customButton('print', label = 'print')
33 | eDT_result <- eDT(id = "data", mtcars, options = list(buttons = list("save", b)))
34 | observeEvent(input$print,{
35 | print(eDT_result$state())
36 | })
37 | }
38 | shinyApp(ui,server)
39 | }
40 |
41 | }
42 | \author{
43 | Jasper Schelfhout
44 | }
45 |
--------------------------------------------------------------------------------
/editbl/man/demoServer_DB.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/demoApp.R
3 | \name{demoServer_DB}
4 | \alias{demoServer_DB}
5 | \title{Server of the DB demo app}
6 | \usage{
7 | demoServer_DB(id, conn)
8 | }
9 | \arguments{
10 | \item{id}{\code{character(1)}}
11 |
12 | \item{conn}{database connection object as given by \code{\link[DBI]{dbConnect}}.}
13 | }
14 | \value{
15 | NULL, just executes the module server.
16 | }
17 | \description{
18 | Server of the DB demo app
19 | }
20 | \author{
21 | Jasper Schelfhout
22 | }
23 |
--------------------------------------------------------------------------------
/editbl/man/demoServer_custom.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/demoApp.R
3 | \name{demoServer_custom}
4 | \alias{demoServer_custom}
5 | \title{Server of the mtcars demo app}
6 | \usage{
7 | demoServer_custom(id, x)
8 | }
9 | \arguments{
10 | \item{id}{\code{character(1)}}
11 |
12 | \item{x}{\code{tbl}}
13 | }
14 | \value{
15 | NULL, just executes the module server.
16 | }
17 | \description{
18 | Server of the mtcars demo app
19 | }
20 | \author{
21 | Jasper Schelfhout
22 | }
23 |
--------------------------------------------------------------------------------
/editbl/man/demoServer_mtcars.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/demoApp.R
3 | \name{demoServer_mtcars}
4 | \alias{demoServer_mtcars}
5 | \title{Server of the mtcars demo app}
6 | \usage{
7 | demoServer_mtcars(id)
8 | }
9 | \arguments{
10 | \item{id}{\code{character(1)}}
11 | }
12 | \value{
13 | NULL, just executes the module server.
14 | }
15 | \description{
16 | Server of the mtcars demo app
17 | }
18 | \author{
19 | Jasper Schelfhout
20 | }
21 |
--------------------------------------------------------------------------------
/editbl/man/demoUI_DB.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/demoApp.R
3 | \name{demoUI_DB}
4 | \alias{demoUI_DB}
5 | \title{UI of the DB demo app}
6 | \usage{
7 | demoUI_DB(id, conn)
8 | }
9 | \arguments{
10 | \item{id}{\code{character(1)}}
11 |
12 | \item{conn}{database connection object as given by \code{\link[DBI]{dbConnect}}.}
13 | }
14 | \value{
15 | HTML
16 | }
17 | \description{
18 | UI of the DB demo app
19 | }
20 | \author{
21 | Jasper Schelfhout
22 | }
23 |
--------------------------------------------------------------------------------
/editbl/man/demoUI_custom.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/demoApp.R
3 | \name{demoUI_custom}
4 | \alias{demoUI_custom}
5 | \title{UI of the demo mtcars app}
6 | \usage{
7 | demoUI_custom(id)
8 | }
9 | \arguments{
10 | \item{id}{\code{character(1)}}
11 | }
12 | \value{
13 | HTML
14 | }
15 | \description{
16 | UI of the demo mtcars app
17 | }
18 | \author{
19 | Jasper Schelfhout
20 | }
21 |
--------------------------------------------------------------------------------
/editbl/man/demoUI_mtcars.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/demoApp.R
3 | \name{demoUI_mtcars}
4 | \alias{demoUI_mtcars}
5 | \title{UI of the demo mtcars app}
6 | \usage{
7 | demoUI_mtcars(id)
8 | }
9 | \arguments{
10 | \item{id}{\code{character(1)}}
11 | }
12 | \value{
13 | HTML
14 | }
15 | \description{
16 | UI of the demo mtcars app
17 | }
18 | \author{
19 | Jasper Schelfhout
20 | }
21 |
--------------------------------------------------------------------------------
/editbl/man/devServer.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/devApp.R
3 | \name{devServer}
4 | \alias{devServer}
5 | \title{Server of the development app}
6 | \usage{
7 | devServer(id, conn)
8 | }
9 | \arguments{
10 | \item{id}{\code{character(1)}}
11 |
12 | \item{conn}{database connection object as given by \code{\link[DBI]{dbConnect}}.}
13 | }
14 | \value{
15 | NULL, just executes the module server.
16 | }
17 | \description{
18 | Server of the development app
19 | }
20 | \author{
21 | Jasper Schelfhout
22 | }
23 |
--------------------------------------------------------------------------------
/editbl/man/devUI.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/devApp.R
3 | \name{devUI}
4 | \alias{devUI}
5 | \title{UI of the development app}
6 | \usage{
7 | devUI(id, conn)
8 | }
9 | \arguments{
10 | \item{id}{\code{character(1)}}
11 |
12 | \item{conn}{database connection object as given by \code{\link[DBI]{dbConnect}}.}
13 | }
14 | \value{
15 | HTML
16 | }
17 | \description{
18 | UI of the development app
19 | }
20 | \author{
21 | Jasper Schelfhout
22 | }
23 |
--------------------------------------------------------------------------------
/editbl/man/disableDoubleClickButtonCss.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/eDT.R
3 | \name{disableDoubleClickButtonCss}
4 | \alias{disableDoubleClickButtonCss}
5 | \title{Function to generate CSS to disable clicking events on a column}
6 | \usage{
7 | disableDoubleClickButtonCss(id)
8 | }
9 | \arguments{
10 | \item{id}{\code{character(1)} namespaced id of the datatable}
11 | }
12 | \value{
13 | \code{character} CSS
14 | }
15 | \description{
16 | Function to generate CSS to disable clicking events on a column
17 | }
18 | \details{
19 | \url{https://stackoverflow.com/questions/60406027/how-to-disable-double-click-reactivity-for-specific-columns-in-r-datatable}
20 |
21 | \url{https://stackoverflow.com/questions/75406546/apply-css-styling-to-a-single-dt-datatable}
22 | }
23 |
--------------------------------------------------------------------------------
/editbl/man/eDTOutput.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/eDT.R
3 | \name{eDTOutput}
4 | \alias{eDTOutput}
5 | \title{UI part of \code{\link{eDT}}}
6 | \usage{
7 | eDTOutput(id, ...)
8 | }
9 | \arguments{
10 | \item{id}{\code{character(1)}}
11 |
12 | \item{...}{arguments passed to \code{\link[DT]{DTOutput}}}
13 | }
14 | \value{
15 | HTML
16 | }
17 | \description{
18 | UI part of \code{\link{eDT}}
19 | }
20 | \details{
21 | Works exactly like \code{\link[DT]{DTOutput}} apart from the fact that instead of the \code{outputId}
22 | argument, \code{id} is requested. Reason being that this function is a UI to a shiny module.
23 | This means that the datatable can be found under the id \code{'{namespace}-{id}-DT'} instead of \code{'{namespace}-{outputId}'}.
24 |
25 | Also some minor CSS and javascript is executed for functional puposes.
26 | }
27 | \examples{
28 | ## Only run this example in interactive R sessions
29 | if(interactive()){
30 | # tibble support
31 | modifiedData <- editbl::eDT(tibble::as_tibble(mtcars))
32 |
33 | # data.table support
34 | modifiedData <- editbl::eDT(dtplyr::lazy_dt(data.table::data.table(mtcars)))
35 |
36 | # database support
37 | tmpFile <- tempfile(fileext = ".sqlite")
38 | file.copy(system.file("extdata", "chinook.sqlite", package = 'editbl'), tmpFile)
39 |
40 | conn <- editbl::connectDB(dbname = tmpFile)
41 | modifiedData <- editbl::eDT(dplyr::tbl(conn, "Artist"), in_place = TRUE)
42 | DBI::dbDisconnect(conn)
43 |
44 | unlink(tmpFile)
45 |
46 | # Within shiny
47 | library(shiny)
48 | library(editbl)
49 | shinyApp(
50 | ui = fluidPage(fluidRow(column(12, eDTOutput('tbl')))),
51 | server = function(input, output) {
52 | eDT('tbl',iris,)
53 | }
54 | )
55 |
56 | # Custom inputUI
57 | editbl::eDT(mtcars, inputUI = function(id, data){
58 | ns <- NS(id)
59 | textInput(
60 | ns("mpg"),
61 | label = "mpg",
62 | value = data$mpg)})
63 |
64 | # Do not allow delete
65 | editbl::eDT(mtcars, canDeleteRow = FALSE)
66 | }
67 |
68 | }
69 | \author{
70 | Jasper Schelfhout
71 | }
72 |
--------------------------------------------------------------------------------
/editbl/man/eDT_app.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/eDT_app.R
3 | \name{eDT_app}
4 | \alias{eDT_app}
5 | \title{Open interactive app to explore and modify data}
6 | \usage{
7 | eDT_app(...)
8 | }
9 | \arguments{
10 | \item{...}{arguments past to \code{\link{eDT}}}
11 | }
12 | \value{
13 | data (or a modified version thereof) once you click 'close'
14 | }
15 | \description{
16 | Open interactive app to explore and modify data
17 | }
18 | \details{
19 | When \code{\link{eDT}} is not used within the server of a shiny app, it will
20 | call this function to start up a shiny app itself. Just as \code{DT::datatable()} displays a table
21 | in the browser when called upon interactively.
22 | }
23 |
--------------------------------------------------------------------------------
/editbl/man/eDT_app_server.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/eDT_app.R
3 | \name{eDT_app_server}
4 | \alias{eDT_app_server}
5 | \title{Server of eDT_app}
6 | \usage{
7 | eDT_app_server(moduleId = "nevergonnagiveyouup", ...)
8 | }
9 | \arguments{
10 | \item{moduleId}{\code{character(1)} id to connect with eDT_app_server}
11 |
12 | \item{...}{arguments passed to \link{eDT}}
13 | }
14 | \value{
15 | moduleServer which on application stop returns version of x with made changes
16 | }
17 | \description{
18 | Server of eDT_app
19 | }
20 | \author{
21 | Jasper Schelfhout
22 | }
23 |
--------------------------------------------------------------------------------
/editbl/man/eDT_app_ui.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/eDT_app.R
3 | \name{eDT_app_ui}
4 | \alias{eDT_app_ui}
5 | \title{UI of eDT_app}
6 | \usage{
7 | eDT_app_ui(moduleId = "nevergonnagiveyouup", eDTId = "nevergonnaletyoudown")
8 | }
9 | \arguments{
10 | \item{moduleId}{\code{character(1)} id to connect with eDT_app_server}
11 |
12 | \item{eDTId}{\code{character(1)} id to connect \code{\link{eDTOutput}} to \code{\link{eDT}} within the module.}
13 | }
14 | \value{
15 | HTML
16 | }
17 | \description{
18 | UI of eDT_app
19 | }
20 | \author{
21 | Jasper Schelfhout
22 | }
23 |
--------------------------------------------------------------------------------
/editbl/man/e_rows_insert.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tbl.R
3 | \name{e_rows_insert}
4 | \alias{e_rows_insert}
5 | \title{Insert rows into a tibble}
6 | \usage{
7 | e_rows_insert(
8 | x,
9 | y,
10 | by = NULL,
11 | ...,
12 | conflict = c("error", "ignore"),
13 | copy = FALSE,
14 | in_place = FALSE
15 | )
16 | }
17 | \arguments{
18 | \item{x, y}{A pair of data frames or data frame extensions (e.g. a tibble).
19 | \code{y} must have the same columns of \code{x} or a subset.}
20 |
21 | \item{by}{An unnamed character vector giving the key columns. The key columns
22 | must exist in both \code{x} and \code{y}. Keys typically uniquely identify each row,
23 | but this is only enforced for the key values of \code{y} when \code{rows_update()},
24 | \code{rows_patch()}, or \code{rows_upsert()} are used.
25 |
26 | By default, we use the first column in \code{y}, since the first column is
27 | a reasonable place to put an identifier variable.}
28 |
29 | \item{...}{Other parameters passed onto methods.}
30 |
31 | \item{conflict}{For \code{rows_insert()}, how should keys in \code{y} that conflict
32 | with keys in \code{x} be handled? A conflict arises if there is a key in \code{y}
33 | that already exists in \code{x}.
34 |
35 | One of:
36 | \itemize{
37 | \item \code{"error"}, the default, will error if there are any keys in \code{y} that
38 | conflict with keys in \code{x}.
39 | \item \code{"ignore"} will ignore rows in \code{y} with keys that conflict with keys in
40 | \code{x}.
41 | }}
42 |
43 | \item{copy}{If \code{x} and \code{y} are not from the same data source,
44 | and \code{copy} is \code{TRUE}, then \code{y} will be copied into the
45 | same src as \code{x}. This allows you to join tables across srcs, but
46 | it is a potentially expensive operation so you must opt into it.}
47 |
48 | \item{in_place}{Should \code{x} be modified in place? This argument is only
49 | relevant for mutable backends (e.g. databases, data.tables).
50 |
51 | When \code{TRUE}, a modified version of \code{x} is returned invisibly;
52 | when \code{FALSE}, a new object representing the resulting changes is returned.}
53 | }
54 | \value{
55 | An object of the same type as \code{x}. The order of the rows and columns of \code{x}
56 | is preserved as much as possible. The output has the following properties:
57 | \itemize{
58 | \item \code{rows_update()} and \code{rows_patch()} preserve the number of rows;
59 | \code{rows_insert()}, \code{rows_append()}, and \code{rows_upsert()} return all existing
60 | rows and potentially new rows; \code{rows_delete()} returns a subset of the
61 | rows.
62 | \item Columns are not added, removed, or relocated, though the data may be
63 | updated.
64 | \item Groups are taken from \code{x}.
65 | \item Data frame attributes are taken from \code{x}.
66 | }
67 |
68 | If \code{in_place = TRUE}, the result will be returned invisibly.
69 | }
70 | \description{
71 | Insert rows into a tibble
72 | }
73 | \details{
74 | Mainly a wrapper around \code{\link[dplyr]{rows_insert}}.
75 | Allows for specific implementations should the behavior differ from what's needed by \code{editbl}.
76 | Reason for separate method is to avoid conflicts on package loading.
77 | }
78 |
--------------------------------------------------------------------------------
/editbl/man/e_rows_insert.default.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tbl.R
3 | \name{e_rows_insert.default}
4 | \alias{e_rows_insert.default}
5 | \title{Insert rows into a tibble}
6 | \usage{
7 | \method{e_rows_insert}{default}(
8 | x,
9 | y,
10 | by = NULL,
11 | ...,
12 | conflict = c("error", "ignore"),
13 | copy = FALSE,
14 | in_place = FALSE
15 | )
16 | }
17 | \arguments{
18 | \item{x, y}{A pair of data frames or data frame extensions (e.g. a tibble).
19 | \code{y} must have the same columns of \code{x} or a subset.}
20 |
21 | \item{by}{An unnamed character vector giving the key columns. The key columns
22 | must exist in both \code{x} and \code{y}. Keys typically uniquely identify each row,
23 | but this is only enforced for the key values of \code{y} when \code{rows_update()},
24 | \code{rows_patch()}, or \code{rows_upsert()} are used.
25 |
26 | By default, we use the first column in \code{y}, since the first column is
27 | a reasonable place to put an identifier variable.}
28 |
29 | \item{...}{Other parameters passed onto methods.}
30 |
31 | \item{conflict}{For \code{rows_insert()}, how should keys in \code{y} that conflict
32 | with keys in \code{x} be handled? A conflict arises if there is a key in \code{y}
33 | that already exists in \code{x}.
34 |
35 | One of:
36 | \itemize{
37 | \item \code{"error"}, the default, will error if there are any keys in \code{y} that
38 | conflict with keys in \code{x}.
39 | \item \code{"ignore"} will ignore rows in \code{y} with keys that conflict with keys in
40 | \code{x}.
41 | }}
42 |
43 | \item{copy}{If \code{x} and \code{y} are not from the same data source,
44 | and \code{copy} is \code{TRUE}, then \code{y} will be copied into the
45 | same src as \code{x}. This allows you to join tables across srcs, but
46 | it is a potentially expensive operation so you must opt into it.}
47 |
48 | \item{in_place}{Should \code{x} be modified in place? This argument is only
49 | relevant for mutable backends (e.g. databases, data.tables).
50 |
51 | When \code{TRUE}, a modified version of \code{x} is returned invisibly;
52 | when \code{FALSE}, a new object representing the resulting changes is returned.}
53 | }
54 | \value{
55 | An object of the same type as \code{x}. The order of the rows and columns of \code{x}
56 | is preserved as much as possible. The output has the following properties:
57 | \itemize{
58 | \item \code{rows_update()} and \code{rows_patch()} preserve the number of rows;
59 | \code{rows_insert()}, \code{rows_append()}, and \code{rows_upsert()} return all existing
60 | rows and potentially new rows; \code{rows_delete()} returns a subset of the
61 | rows.
62 | \item Columns are not added, removed, or relocated, though the data may be
63 | updated.
64 | \item Groups are taken from \code{x}.
65 | \item Data frame attributes are taken from \code{x}.
66 | }
67 |
68 | If \code{in_place = TRUE}, the result will be returned invisibly.
69 | }
70 | \description{
71 | Insert rows into a tibble
72 | }
73 | \details{
74 | Mainly a wrapper around \code{\link[dplyr]{rows_insert}}.
75 | Allows for specific implementations should the behavior differ from what's needed by \code{editbl}.
76 | Reason for separate method is to avoid conflicts on package loading.
77 | }
78 |
--------------------------------------------------------------------------------
/editbl/man/e_rows_insert.dtplyr_step.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tbl_dtplyr_step.R
3 | \name{e_rows_insert.dtplyr_step}
4 | \alias{e_rows_insert.dtplyr_step}
5 | \title{rows_insert implementation for \code{data.table} backends.}
6 | \usage{
7 | \method{e_rows_insert}{dtplyr_step}(x, y, by = NULL, ..., copy = FALSE, in_place = FALSE)
8 | }
9 | \arguments{
10 | \item{x, y}{A pair of data frames or data frame extensions (e.g. a tibble).
11 | \code{y} must have the same columns of \code{x} or a subset.}
12 |
13 | \item{by}{An unnamed character vector giving the key columns. The key columns
14 | must exist in both \code{x} and \code{y}. Keys typically uniquely identify each row,
15 | but this is only enforced for the key values of \code{y} when \code{rows_update()},
16 | \code{rows_patch()}, or \code{rows_upsert()} are used.
17 |
18 | By default, we use the first column in \code{y}, since the first column is
19 | a reasonable place to put an identifier variable.}
20 |
21 | \item{...}{Other parameters passed onto methods.}
22 |
23 | \item{copy}{If \code{x} and \code{y} are not from the same data source,
24 | and \code{copy} is \code{TRUE}, then \code{y} will be copied into the
25 | same src as \code{x}. This allows you to join tables across srcs, but
26 | it is a potentially expensive operation so you must opt into it.}
27 |
28 | \item{in_place}{Should \code{x} be modified in place? This argument is only
29 | relevant for mutable backends (e.g. databases, data.tables).
30 |
31 | When \code{TRUE}, a modified version of \code{x} is returned invisibly;
32 | when \code{FALSE}, a new object representing the resulting changes is returned.}
33 | }
34 | \value{
35 | An object of the same type as \code{x}. The order of the rows and columns of \code{x}
36 | is preserved as much as possible. The output has the following properties:
37 | \itemize{
38 | \item \code{rows_update()} and \code{rows_patch()} preserve the number of rows;
39 | \code{rows_insert()}, \code{rows_append()}, and \code{rows_upsert()} return all existing
40 | rows and potentially new rows; \code{rows_delete()} returns a subset of the
41 | rows.
42 | \item Columns are not added, removed, or relocated, though the data may be
43 | updated.
44 | \item Groups are taken from \code{x}.
45 | \item Data frame attributes are taken from \code{x}.
46 | }
47 |
48 | If \code{in_place = TRUE}, the result will be returned invisibly.
49 | }
50 | \description{
51 | rows_insert implementation for \code{data.table} backends.
52 | }
53 | \details{
54 | Mainly a wrapper around \code{\link[dplyr]{rows_insert}}.
55 | Allows for specific implementations should the behavior differ from what's needed by \code{editbl}.
56 | Reason for separate method is to avoid conflicts on package loading.
57 | }
58 | \author{
59 | Jasper Schelfhout
60 | }
61 |
--------------------------------------------------------------------------------
/editbl/man/e_rows_insert.tbl_dbi.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tbl_dbi.R
3 | \name{e_rows_insert.tbl_dbi}
4 | \alias{e_rows_insert.tbl_dbi}
5 | \title{rows_insert implementation for DBI backends.}
6 | \usage{
7 | \method{e_rows_insert}{tbl_dbi}(x, y, by = NULL, ..., copy = FALSE, in_place = FALSE)
8 | }
9 | \arguments{
10 | \item{x, y}{A pair of data frames or data frame extensions (e.g. a tibble).
11 | \code{y} must have the same columns of \code{x} or a subset.}
12 |
13 | \item{by}{An unnamed character vector giving the key columns. The key columns
14 | must exist in both \code{x} and \code{y}. Keys typically uniquely identify each row,
15 | but this is only enforced for the key values of \code{y} when \code{rows_update()},
16 | \code{rows_patch()}, or \code{rows_upsert()} are used.
17 |
18 | By default, we use the first column in \code{y}, since the first column is
19 | a reasonable place to put an identifier variable.}
20 |
21 | \item{...}{Other parameters passed onto methods.}
22 |
23 | \item{copy}{If \code{x} and \code{y} are not from the same data source,
24 | and \code{copy} is \code{TRUE}, then \code{y} will be copied into the
25 | same src as \code{x}. This allows you to join tables across srcs, but
26 | it is a potentially expensive operation so you must opt into it.}
27 |
28 | \item{in_place}{Should \code{x} be modified in place? This argument is only
29 | relevant for mutable backends (e.g. databases, data.tables).
30 |
31 | When \code{TRUE}, a modified version of \code{x} is returned invisibly;
32 | when \code{FALSE}, a new object representing the resulting changes is returned.}
33 | }
34 | \value{
35 | An object of the same type as \code{x}. The order of the rows and columns of \code{x}
36 | is preserved as much as possible. The output has the following properties:
37 | \itemize{
38 | \item \code{rows_update()} and \code{rows_patch()} preserve the number of rows;
39 | \code{rows_insert()}, \code{rows_append()}, and \code{rows_upsert()} return all existing
40 | rows and potentially new rows; \code{rows_delete()} returns a subset of the
41 | rows.
42 | \item Columns are not added, removed, or relocated, though the data may be
43 | updated.
44 | \item Groups are taken from \code{x}.
45 | \item Data frame attributes are taken from \code{x}.
46 | }
47 |
48 | If \code{in_place = TRUE}, the result will be returned invisibly.
49 | }
50 | \description{
51 | rows_insert implementation for DBI backends.
52 | }
53 | \details{
54 | Mainly a wrapper around \code{\link[dplyr]{rows_insert}}.
55 | Allows for specific implementations should the behavior differ from what's needed by \code{editbl}.
56 | Reason for separate method is to avoid conflicts on package loading.
57 | }
58 | \examples{
59 | library(dplyr)
60 |
61 | # Set up a test table
62 | conn <- DBI::dbConnect(RSQLite::SQLite(), ":memory:")
63 | artists_df <- data.frame(
64 | ArtistId = c(1,2),
65 | Name = c("AC/DC", "The Offspring")
66 | )
67 | DBI::dbWriteTable(conn, "Artist", artists_df)
68 |
69 | # Insert new row
70 | artists <- tbl(conn, "Artist")
71 | DBI::dbBegin(conn)
72 | e_rows_insert(artists,
73 | data.frame(ArtistId = 999, Name = "testArtist"),
74 | in_place = TRUE)
75 |
76 | DBI::dbRollback(conn)
77 | DBI::dbDisconnect(conn)
78 |
79 | }
80 | \author{
81 | Jasper Schelfhout
82 | }
83 |
--------------------------------------------------------------------------------
/editbl/man/e_rows_update.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tbl.R
3 | \name{e_rows_update}
4 | \alias{e_rows_update}
5 | \title{Update rows of a tibble}
6 | \usage{
7 | e_rows_update(
8 | x,
9 | y,
10 | by = NULL,
11 | ...,
12 | match,
13 | unmatched = c("error", "ignore"),
14 | copy = FALSE,
15 | in_place = FALSE
16 | )
17 | }
18 | \arguments{
19 | \item{x, y}{A pair of data frames or data frame extensions (e.g. a tibble).
20 | \code{y} must have the same columns of \code{x} or a subset.}
21 |
22 | \item{by}{An unnamed character vector giving the key columns. The key columns
23 | must exist in both \code{x} and \code{y}. Keys typically uniquely identify each row,
24 | but this is only enforced for the key values of \code{y} when \code{rows_update()},
25 | \code{rows_patch()}, or \code{rows_upsert()} are used.
26 |
27 | By default, we use the first column in \code{y}, since the first column is
28 | a reasonable place to put an identifier variable.}
29 |
30 | \item{...}{Other parameters passed onto methods.}
31 |
32 | \item{match}{named \code{list} consisting out of two equal length \code{data.frame}'s with columns defined in \code{by}.
33 | This allows for updates of columns defined in \code{by}.}
34 |
35 | \item{unmatched}{For \code{rows_update()}, \code{rows_patch()}, and \code{rows_delete()},
36 | how should keys in \code{y} that are unmatched by the keys in \code{x} be handled?
37 |
38 | One of:
39 | \itemize{
40 | \item \code{"error"}, the default, will error if there are any keys in \code{y} that
41 | are unmatched by the keys in \code{x}.
42 | \item \code{"ignore"} will ignore rows in \code{y} with keys that are unmatched by the
43 | keys in \code{x}.
44 | }}
45 |
46 | \item{copy}{If \code{x} and \code{y} are not from the same data source,
47 | and \code{copy} is \code{TRUE}, then \code{y} will be copied into the
48 | same src as \code{x}. This allows you to join tables across srcs, but
49 | it is a potentially expensive operation so you must opt into it.}
50 |
51 | \item{in_place}{Should \code{x} be modified in place? This argument is only
52 | relevant for mutable backends (e.g. databases, data.tables).
53 |
54 | When \code{TRUE}, a modified version of \code{x} is returned invisibly;
55 | when \code{FALSE}, a new object representing the resulting changes is returned.}
56 | }
57 | \value{
58 | An object of the same type as \code{x}. The order of the rows and columns of \code{x}
59 | is preserved as much as possible. The output has the following properties:
60 | \itemize{
61 | \item \code{rows_update()} and \code{rows_patch()} preserve the number of rows;
62 | \code{rows_insert()}, \code{rows_append()}, and \code{rows_upsert()} return all existing
63 | rows and potentially new rows; \code{rows_delete()} returns a subset of the
64 | rows.
65 | \item Columns are not added, removed, or relocated, though the data may be
66 | updated.
67 | \item Groups are taken from \code{x}.
68 | \item Data frame attributes are taken from \code{x}.
69 | }
70 |
71 | If \code{in_place = TRUE}, the result will be returned invisibly.
72 | }
73 | \description{
74 | Update rows of a tibble
75 | }
76 | \details{
77 | Mainly a wrapper around \code{\link[dplyr]{rows_update}}.
78 | Allows for specific implementations should the behavior differ from what's needed by \code{editbl}.
79 | Reason for separate method is to avoid conflicts on package loading.
80 | }
81 |
--------------------------------------------------------------------------------
/editbl/man/e_rows_update.data.frame.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tbl_df.R
3 | \name{e_rows_update.data.frame}
4 | \alias{e_rows_update.data.frame}
5 | \title{rows_update implementation for data.frame backends.}
6 | \usage{
7 | \method{e_rows_update}{data.frame}(
8 | x,
9 | y,
10 | by = NULL,
11 | match = NULL,
12 | ...,
13 | copy = FALSE,
14 | in_place = FALSE
15 | )
16 | }
17 | \arguments{
18 | \item{x, y}{A pair of data frames or data frame extensions (e.g. a tibble).
19 | \code{y} must have the same columns of \code{x} or a subset.}
20 |
21 | \item{by}{An unnamed character vector giving the key columns. The key columns
22 | must exist in both \code{x} and \code{y}. Keys typically uniquely identify each row,
23 | but this is only enforced for the key values of \code{y} when \code{rows_update()},
24 | \code{rows_patch()}, or \code{rows_upsert()} are used.
25 |
26 | By default, we use the first column in \code{y}, since the first column is
27 | a reasonable place to put an identifier variable.}
28 |
29 | \item{match}{named \code{list} consisting out of two equal length \code{data.frame}'s with columns defined in \code{by}.
30 | This allows for updates of columns defined in \code{by}.}
31 |
32 | \item{...}{Other parameters passed onto methods.}
33 |
34 | \item{copy}{If \code{x} and \code{y} are not from the same data source,
35 | and \code{copy} is \code{TRUE}, then \code{y} will be copied into the
36 | same src as \code{x}. This allows you to join tables across srcs, but
37 | it is a potentially expensive operation so you must opt into it.}
38 |
39 | \item{in_place}{Should \code{x} be modified in place? This argument is only
40 | relevant for mutable backends (e.g. databases, data.tables).
41 |
42 | When \code{TRUE}, a modified version of \code{x} is returned invisibly;
43 | when \code{FALSE}, a new object representing the resulting changes is returned.}
44 | }
45 | \value{
46 | An object of the same type as \code{x}. The order of the rows and columns of \code{x}
47 | is preserved as much as possible. The output has the following properties:
48 | \itemize{
49 | \item \code{rows_update()} and \code{rows_patch()} preserve the number of rows;
50 | \code{rows_insert()}, \code{rows_append()}, and \code{rows_upsert()} return all existing
51 | rows and potentially new rows; \code{rows_delete()} returns a subset of the
52 | rows.
53 | \item Columns are not added, removed, or relocated, though the data may be
54 | updated.
55 | \item Groups are taken from \code{x}.
56 | \item Data frame attributes are taken from \code{x}.
57 | }
58 |
59 | If \code{in_place = TRUE}, the result will be returned invisibly.
60 | }
61 | \description{
62 | rows_update implementation for data.frame backends.
63 | }
64 | \details{
65 | Mainly a wrapper around \code{\link[dplyr]{rows_update}}.
66 | Allows for specific implementations should the behavior differ from what's needed by \code{editbl}.
67 | Reason for separate method is to avoid conflicts on package loading.
68 | }
69 | \author{
70 | Jasper Schelfhout
71 | }
72 |
--------------------------------------------------------------------------------
/editbl/man/e_rows_update.default.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tbl.R
3 | \name{e_rows_update.default}
4 | \alias{e_rows_update.default}
5 | \title{Update rows of a tibble}
6 | \usage{
7 | \method{e_rows_update}{default}(
8 | x,
9 | y,
10 | by = NULL,
11 | ...,
12 | match = match,
13 | unmatched = c("error", "ignore"),
14 | copy = FALSE,
15 | in_place = FALSE
16 | )
17 | }
18 | \arguments{
19 | \item{x, y}{A pair of data frames or data frame extensions (e.g. a tibble).
20 | \code{y} must have the same columns of \code{x} or a subset.}
21 |
22 | \item{by}{An unnamed character vector giving the key columns. The key columns
23 | must exist in both \code{x} and \code{y}. Keys typically uniquely identify each row,
24 | but this is only enforced for the key values of \code{y} when \code{rows_update()},
25 | \code{rows_patch()}, or \code{rows_upsert()} are used.
26 |
27 | By default, we use the first column in \code{y}, since the first column is
28 | a reasonable place to put an identifier variable.}
29 |
30 | \item{...}{Other parameters passed onto methods.}
31 |
32 | \item{match}{named \code{list} consisting out of two equal length \code{data.frame}'s with columns defined in \code{by}.
33 | This allows for updates of columns defined in \code{by}.}
34 |
35 | \item{unmatched}{For \code{rows_update()}, \code{rows_patch()}, and \code{rows_delete()},
36 | how should keys in \code{y} that are unmatched by the keys in \code{x} be handled?
37 |
38 | One of:
39 | \itemize{
40 | \item \code{"error"}, the default, will error if there are any keys in \code{y} that
41 | are unmatched by the keys in \code{x}.
42 | \item \code{"ignore"} will ignore rows in \code{y} with keys that are unmatched by the
43 | keys in \code{x}.
44 | }}
45 |
46 | \item{copy}{If \code{x} and \code{y} are not from the same data source,
47 | and \code{copy} is \code{TRUE}, then \code{y} will be copied into the
48 | same src as \code{x}. This allows you to join tables across srcs, but
49 | it is a potentially expensive operation so you must opt into it.}
50 |
51 | \item{in_place}{Should \code{x} be modified in place? This argument is only
52 | relevant for mutable backends (e.g. databases, data.tables).
53 |
54 | When \code{TRUE}, a modified version of \code{x} is returned invisibly;
55 | when \code{FALSE}, a new object representing the resulting changes is returned.}
56 | }
57 | \value{
58 | An object of the same type as \code{x}. The order of the rows and columns of \code{x}
59 | is preserved as much as possible. The output has the following properties:
60 | \itemize{
61 | \item \code{rows_update()} and \code{rows_patch()} preserve the number of rows;
62 | \code{rows_insert()}, \code{rows_append()}, and \code{rows_upsert()} return all existing
63 | rows and potentially new rows; \code{rows_delete()} returns a subset of the
64 | rows.
65 | \item Columns are not added, removed, or relocated, though the data may be
66 | updated.
67 | \item Groups are taken from \code{x}.
68 | \item Data frame attributes are taken from \code{x}.
69 | }
70 |
71 | If \code{in_place = TRUE}, the result will be returned invisibly.
72 | }
73 | \description{
74 | Update rows of a tibble
75 | }
76 | \details{
77 | Mainly a wrapper around \code{\link[dplyr]{rows_update}}.
78 | Allows for specific implementations should the behavior differ from what's needed by \code{editbl}.
79 | Reason for separate method is to avoid conflicts on package loading.
80 | }
81 |
--------------------------------------------------------------------------------
/editbl/man/e_rows_update.dtplyr_step.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tbl_dtplyr_step.R
3 | \name{e_rows_update.dtplyr_step}
4 | \alias{e_rows_update.dtplyr_step}
5 | \title{rows_update implementation for data.table backends.}
6 | \usage{
7 | \method{e_rows_update}{dtplyr_step}(
8 | x,
9 | y,
10 | by = NULL,
11 | match = NULL,
12 | ...,
13 | copy = FALSE,
14 | in_place = FALSE
15 | )
16 | }
17 | \arguments{
18 | \item{x, y}{A pair of data frames or data frame extensions (e.g. a tibble).
19 | \code{y} must have the same columns of \code{x} or a subset.}
20 |
21 | \item{by}{An unnamed character vector giving the key columns. The key columns
22 | must exist in both \code{x} and \code{y}. Keys typically uniquely identify each row,
23 | but this is only enforced for the key values of \code{y} when \code{rows_update()},
24 | \code{rows_patch()}, or \code{rows_upsert()} are used.
25 |
26 | By default, we use the first column in \code{y}, since the first column is
27 | a reasonable place to put an identifier variable.}
28 |
29 | \item{match}{named \code{list} consisting out of two equal length \code{data.frame}'s with columns defined in \code{by}.
30 | This allows for updates of columns defined in \code{by}.}
31 |
32 | \item{...}{Other parameters passed onto methods.}
33 |
34 | \item{copy}{If \code{x} and \code{y} are not from the same data source,
35 | and \code{copy} is \code{TRUE}, then \code{y} will be copied into the
36 | same src as \code{x}. This allows you to join tables across srcs, but
37 | it is a potentially expensive operation so you must opt into it.}
38 |
39 | \item{in_place}{Should \code{x} be modified in place? This argument is only
40 | relevant for mutable backends (e.g. databases, data.tables).
41 |
42 | When \code{TRUE}, a modified version of \code{x} is returned invisibly;
43 | when \code{FALSE}, a new object representing the resulting changes is returned.}
44 | }
45 | \value{
46 | An object of the same type as \code{x}. The order of the rows and columns of \code{x}
47 | is preserved as much as possible. The output has the following properties:
48 | \itemize{
49 | \item \code{rows_update()} and \code{rows_patch()} preserve the number of rows;
50 | \code{rows_insert()}, \code{rows_append()}, and \code{rows_upsert()} return all existing
51 | rows and potentially new rows; \code{rows_delete()} returns a subset of the
52 | rows.
53 | \item Columns are not added, removed, or relocated, though the data may be
54 | updated.
55 | \item Groups are taken from \code{x}.
56 | \item Data frame attributes are taken from \code{x}.
57 | }
58 |
59 | If \code{in_place = TRUE}, the result will be returned invisibly.
60 | }
61 | \description{
62 | rows_update implementation for data.table backends.
63 | }
64 | \details{
65 | Mainly a wrapper around \code{\link[dplyr]{rows_update}}.
66 | Allows for specific implementations should the behavior differ from what's needed by \code{editbl}.
67 | Reason for separate method is to avoid conflicts on package loading.
68 | }
69 | \author{
70 | Jasper Schelfhout
71 | }
72 |
--------------------------------------------------------------------------------
/editbl/man/e_rows_update.tbl_dbi.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tbl_dbi.R
3 | \name{e_rows_update.tbl_dbi}
4 | \alias{e_rows_update.tbl_dbi}
5 | \title{rows_update implementation for DBI backends.}
6 | \usage{
7 | \method{e_rows_update}{tbl_dbi}(
8 | x,
9 | y,
10 | by = NULL,
11 | match = NULL,
12 | ...,
13 | copy = FALSE,
14 | in_place = FALSE
15 | )
16 | }
17 | \arguments{
18 | \item{x, y}{A pair of data frames or data frame extensions (e.g. a tibble).
19 | \code{y} must have the same columns of \code{x} or a subset.}
20 |
21 | \item{by}{An unnamed character vector giving the key columns. The key columns
22 | must exist in both \code{x} and \code{y}. Keys typically uniquely identify each row,
23 | but this is only enforced for the key values of \code{y} when \code{rows_update()},
24 | \code{rows_patch()}, or \code{rows_upsert()} are used.
25 |
26 | By default, we use the first column in \code{y}, since the first column is
27 | a reasonable place to put an identifier variable.}
28 |
29 | \item{match}{named \code{list} consisting out of two equal length \code{data.frame}'s with columns defined in \code{by}.
30 | This allows for updates of columns defined in \code{by}.}
31 |
32 | \item{...}{Other parameters passed onto methods.}
33 |
34 | \item{copy}{If \code{x} and \code{y} are not from the same data source,
35 | and \code{copy} is \code{TRUE}, then \code{y} will be copied into the
36 | same src as \code{x}. This allows you to join tables across srcs, but
37 | it is a potentially expensive operation so you must opt into it.}
38 |
39 | \item{in_place}{Should \code{x} be modified in place? This argument is only
40 | relevant for mutable backends (e.g. databases, data.tables).
41 |
42 | When \code{TRUE}, a modified version of \code{x} is returned invisibly;
43 | when \code{FALSE}, a new object representing the resulting changes is returned.}
44 | }
45 | \value{
46 | An object of the same type as \code{x}. The order of the rows and columns of \code{x}
47 | is preserved as much as possible. The output has the following properties:
48 | \itemize{
49 | \item \code{rows_update()} and \code{rows_patch()} preserve the number of rows;
50 | \code{rows_insert()}, \code{rows_append()}, and \code{rows_upsert()} return all existing
51 | rows and potentially new rows; \code{rows_delete()} returns a subset of the
52 | rows.
53 | \item Columns are not added, removed, or relocated, though the data may be
54 | updated.
55 | \item Groups are taken from \code{x}.
56 | \item Data frame attributes are taken from \code{x}.
57 | }
58 |
59 | If \code{in_place = TRUE}, the result will be returned invisibly.
60 | }
61 | \description{
62 | rows_update implementation for DBI backends.
63 | }
64 | \details{
65 | Mainly a wrapper around \code{\link[dplyr]{rows_update}}.
66 | Allows for specific implementations should the behavior differ from what's needed by \code{editbl}.
67 | Reason for separate method is to avoid conflicts on package loading.
68 | }
69 | \examples{
70 | library(dplyr)
71 |
72 | # Set up a test table
73 | conn <- DBI::dbConnect(RSQLite::SQLite(), ":memory:")
74 | artists_df <- data.frame(
75 | ArtistId = c(1,2),
76 | Name = c("AC/DC", "The Offspring")
77 | )
78 | DBI::dbWriteTable(conn, "Artist", artists_df)
79 |
80 | # Update rows without changing the key.
81 | artists <- tbl(conn, "Artist")
82 | DBI::dbBegin(conn)
83 | y <- data.frame(ArtistId = 1, Name = "DC/AC")
84 | e_rows_update(
85 | x = artists,
86 | y = y,
87 | by = "ArtistId",
88 | in_place = TRUE)
89 | DBI::dbRollback(conn)
90 |
91 | # Update key values of rows.
92 | DBI::dbBegin(conn)
93 | y <- data.frame(ArtistId = 999, Name = "DC/AC")
94 | match <- list(
95 | x = data.frame("ArtistId" = 1),
96 | y = data.frame("ArtistId" = 999)
97 | )
98 | e_rows_update(
99 | x = artists,
100 | y = y,
101 | match = match,
102 | by = "ArtistId",
103 | in_place = TRUE)
104 | DBI::dbRollback(conn)
105 | DBI::dbDisconnect(conn)
106 |
107 | }
108 | \author{
109 | Jasper Schelfhout
110 | }
111 |
--------------------------------------------------------------------------------
/editbl/man/evalCanCloneRow.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/eDT.R
3 | \name{evalCanCloneRow}
4 | \alias{evalCanCloneRow}
5 | \title{Determine if a row can be cloned}
6 | \usage{
7 | evalCanCloneRow(row, canCloneRow = TRUE, statusCol = "_editbl_status")
8 | }
9 | \arguments{
10 | \item{row}{\code{tibble}, single row.}
11 |
12 | \item{canCloneRow}{\code{function} with argument 'row' defining logic on wether or
13 | not the row can be cloned. Can also be \code{logical} TRUE or FALSE.}
14 |
15 | \item{statusCol}{\code{character(1)} name of column with general status (e.g. modified or not).}
16 | }
17 | \value{
18 | \code{boolean}
19 | }
20 | \description{
21 | Determine if a row can be cloned
22 | }
23 | \details{
24 | calling this around the user passed on function ensures
25 | that newly inserted rows are being excempt from the logic.
26 | Moreover, the output of the function can be checked.
27 | }
28 | \author{
29 | Saar Junius
30 | }
31 |
--------------------------------------------------------------------------------
/editbl/man/evalCanDeleteRow.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/eDT.R
3 | \name{evalCanDeleteRow}
4 | \alias{evalCanDeleteRow}
5 | \title{Determine if a row can be deleted}
6 | \usage{
7 | evalCanDeleteRow(row, canDeleteRow = TRUE, statusCol = "_editbl_status")
8 | }
9 | \arguments{
10 | \item{row}{\code{tibble}, single row}
11 |
12 | \item{canDeleteRow}{\code{function} with argument 'row' defining logic on wether or
13 | not the row can be modified. Can also be \code{logical} TRUE or FALSE.}
14 |
15 | \item{statusCol}{\code{character(1)} name of column with general status (e.g. modified or not).}
16 | }
17 | \value{
18 | \code{boolean}
19 | }
20 | \description{
21 | Determine if a row can be deleted
22 | }
23 | \details{
24 | calling this around the user passed on function ensures
25 | that newly inserted rows are being excempt from the logic.
26 | Moreover, the output of the function can be checked.
27 | }
28 | \author{
29 | Jasper Schelfhout
30 | }
31 |
--------------------------------------------------------------------------------
/editbl/man/evalCanEditRow.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/eDT.R
3 | \name{evalCanEditRow}
4 | \alias{evalCanEditRow}
5 | \title{Determine if a row can be edited}
6 | \usage{
7 | evalCanEditRow(row, canEditRow = TRUE, statusCol = "_editbl_status")
8 | }
9 | \arguments{
10 | \item{row}{\code{tibble}, single row.}
11 |
12 | \item{canEditRow}{\code{function} with argument 'row' defining logic on wether or
13 | not the row can be modified. Can also be \code{logical} TRUE or FALSE.}
14 |
15 | \item{statusCol}{\code{character(1)} name of column with general status (e.g. modified or not).}
16 | }
17 | \value{
18 | \code{boolean}
19 | }
20 | \description{
21 | Determine if a row can be edited
22 | }
23 | \details{
24 | calling this around the user passed on function ensures
25 | that newly inserted rows are being excempt from the logic.
26 | Moreover, the output of the function can be checked.
27 | }
28 | \author{
29 | Jasper Schelfhout
30 | }
31 |
--------------------------------------------------------------------------------
/editbl/man/fillDeductedColumns.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/foreignTbl.R
3 | \name{fillDeductedColumns}
4 | \alias{fillDeductedColumns}
5 | \title{Fill data columns based on foreignTbls}
6 | \usage{
7 | fillDeductedColumns(tbl, foreignTbls)
8 | }
9 | \arguments{
10 | \item{tbl}{\code{tbl}}
11 |
12 | \item{foreignTbls}{list of foreign tbls as created by \code{\link{foreignTbl}}}
13 | }
14 | \value{
15 | tbl
16 | }
17 | \description{
18 | Fill data columns based on foreignTbls
19 | }
20 | \details{
21 | When a combination of columns is not found in the foreignTbl,
22 | fill the deductedColumns with NA.
23 |
24 | on foreignTbls suggesting conflicting data,
25 | an arbitrary choice is made. It is best to afterwards check with
26 | checkForeignTbls to see if a valid result is obtained.
27 | }
28 | \author{
29 | Jasper Schelfhout
30 | }
31 |
--------------------------------------------------------------------------------
/editbl/man/fixInteger64.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{fixInteger64}
4 | \alias{fixInteger64}
5 | \title{Replace instances of integer64 with actual NA values instead of weird default 9218868437227407266}
6 | \usage{
7 | fixInteger64(x)
8 | }
9 | \arguments{
10 | \item{x}{\code{data.frame}}
11 | }
12 | \value{
13 | x with \code{integer64} columns set to \code{bit64::as.integer64(NA)}
14 | }
15 | \description{
16 | Replace instances of integer64 with actual NA values instead of weird default 9218868437227407266
17 | }
18 | \details{
19 | \href{https://github.com/Rdatatable/data.table/issues/4561}{github issue}
20 | }
21 | \author{
22 | Jasper Schelfhout
23 | }
24 |
--------------------------------------------------------------------------------
/editbl/man/foreignTbl.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/foreignTbl.R
3 | \name{foreignTbl}
4 | \alias{foreignTbl}
5 | \title{Create a foreign tibble}
6 | \usage{
7 | foreignTbl(
8 | x,
9 | y,
10 | by = intersect(dplyr::tbl_vars(x), dplyr::tbl_vars(y)),
11 | naturalKey = dplyr::tbl_vars(y),
12 | allowNew = FALSE
13 | )
14 | }
15 | \arguments{
16 | \item{x}{\code{tbl}. The referencing table.}
17 |
18 | \item{y}{\code{tbl}. The referenced table.}
19 |
20 | \item{by}{\code{character}. Column names to match on.
21 | Note that you should rename and/or typecast the columns in y should they not exactly match the columns in x.}
22 |
23 | \item{naturalKey}{\code{character}. The columns that form the natural key in y.
24 | These are the only ones that can actually get modified in \code{eDT} when changing cells in the table.
25 | Reasoning being that these columns should be sufficient to uniquely identify a row in the referenced table.
26 | All other columns will be automatically fetched and filled in.}
27 |
28 | \item{allowNew}{\code{logical}. Whether or not new values are allowed. If \code{TRUE},
29 | the rows in the foreignTbl will only be used as suggestions, not restrictions.}
30 | }
31 | \value{
32 | List with unmodified arguments. However, they have now been checked for validity.
33 | \itemize{
34 | \item y, see argument \code{y}.
35 | \item by, see argument \code{by}.
36 | \item naturalKey, see argument \code{naturalKey}.
37 | \item allowNew, see argument \code{allowNew}
38 | }
39 | }
40 | \description{
41 | Create a foreign tibble
42 | }
43 | \details{
44 | This is a tibble that can be passed onto \code{\link{eDT}} as a referenced table.
45 |
46 | It is the equivalent of a database table to which the \code{data} tbl of eDT has a foreign key.
47 |
48 | It will be merged with the tbl passed onto the \code{data} argument allowing to provide restrictions
49 | for certain columns.
50 |
51 | Note that row uniqueness for the columns used in \code{by} and \code{naturalKey} is assumed.
52 | This assumption will however not be checked since it is an expensive operation on big datasets.
53 | However, if violated, it might give errors or unexpected results during usage of the eDT module.
54 | }
55 | \examples{
56 | a <- tibble::tibble(
57 | first_name = c("Albert","Donald","Mickey"),
58 | last_name_id = c(1,2,2)
59 | )
60 |
61 | b <- foreignTbl(
62 | a,
63 | tibble::tibble(
64 | last_name = c("Einstein", "Duck", "Mouse"),
65 | last_name_id = c(1,2,3)
66 | ),
67 | by = "last_name_id",
68 | naturalKey = "last_name"
69 | )
70 |
71 | ## Only run this in interactive R sessions
72 | if(interactive()){
73 | eDT(a,
74 | foreignTbls = list(b),
75 | options = list(columnDefs = list(list(visible=FALSE, targets="last_name_id")))
76 | )
77 | }
78 |
79 |
80 | }
81 | \author{
82 | Jasper Schelfhout
83 | }
84 |
--------------------------------------------------------------------------------
/editbl/man/getColumnTypeSums.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{getColumnTypeSums}
4 | \alias{getColumnTypeSums}
5 | \title{Get types of columns in a tbl}
6 | \usage{
7 | getColumnTypeSums(tbl)
8 | }
9 | \arguments{
10 | \item{tbl}{\code{tbl}}
11 | }
12 | \value{
13 | named list with types of the colums
14 | }
15 | \description{
16 | Get types of columns in a tbl
17 | }
18 | \author{
19 | Jasper Schelfhout
20 | }
21 |
--------------------------------------------------------------------------------
/editbl/man/getNonNaturalKeyCols.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/foreignTbl.R
3 | \name{getNonNaturalKeyCols}
4 | \alias{getNonNaturalKeyCols}
5 | \title{Get all columns that are not natural keys}
6 | \usage{
7 | getNonNaturalKeyCols(foreignTbls)
8 | }
9 | \arguments{
10 | \item{foreignTbls}{list of foreign tbls as created by \code{\link{foreignTbl}}}
11 | }
12 | \value{
13 | \code{character}
14 | }
15 | \description{
16 | Get all columns that are not natural keys
17 | }
18 | \author{
19 | Jasper Schelfhout
20 | }
21 |
--------------------------------------------------------------------------------
/editbl/man/get_db_table_name.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tbl_dbi.R
3 | \name{get_db_table_name}
4 | \alias{get_db_table_name}
5 | \title{Get name of the tbl in the database}
6 | \usage{
7 | get_db_table_name(x)
8 | }
9 | \arguments{
10 | \item{x}{\code{tbl_dbi}}
11 | }
12 | \value{
13 | SQL, the table name as used in the database
14 | }
15 | \description{
16 | Get name of the tbl in the database
17 | }
18 |
--------------------------------------------------------------------------------
/editbl/man/initData.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/eDT.R
3 | \name{initData}
4 | \alias{initData}
5 | \title{Add some extra columns to data to allow for / keep track of modifications}
6 | \usage{
7 | initData(
8 | data,
9 | ns,
10 | buttonCol = "buttons",
11 | statusCol = "_editbl_status",
12 | deleteCol = "_editbl_deleted",
13 | iCol = "i",
14 | canDeleteRow = TRUE,
15 | canEditRow = TRUE,
16 | canCloneRow = TRUE
17 | )
18 | }
19 | \arguments{
20 | \item{data}{\code{data.frame}}
21 |
22 | \item{ns}{namespace function}
23 |
24 | \item{buttonCol}{\code{character(1)} name of column with buttons}
25 |
26 | \item{statusCol}{\code{character(1)} name of column with general status (e.g. modified or not).}
27 |
28 | \item{deleteCol}{\code{character(1)} name of the column with deletion status.}
29 |
30 | \item{iCol}{\code{character(1)} name of column containing a unique identifier.}
31 |
32 | \item{canDeleteRow}{can be either of the following:
33 | \itemize{
34 | \item \code{logical}, e.g. TRUE or FALSE
35 | \item \code{function}. Needs as input an argument \code{row} which accepts a single row \code{tibble} and as output TRUE/FALSE.
36 | }}
37 |
38 | \item{canEditRow}{can be either of the following:
39 | \itemize{
40 | \item \code{logical}, e.g. TRUE or FALSE
41 | \item \code{function}. Needs as input an argument \code{row} which accepts a single row \code{tibble} and as output TRUE/FALSE.
42 | }}
43 |
44 | \item{canCloneRow}{can be either of the following:
45 | \itemize{
46 | \item \code{logical}, e.g. TRUE or FALSE
47 | \item \code{function}. Needs as input an argument \code{row} which accepts a single row \code{tibble} and as output TRUE/FALSE.
48 | }}
49 | }
50 | \value{
51 | data with extra columns buttons, status, i.
52 | }
53 | \description{
54 | Add some extra columns to data to allow for / keep track of modifications
55 | }
56 | \author{
57 | Jasper Schelfhout
58 | }
59 |
--------------------------------------------------------------------------------
/editbl/man/inputServer.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/shinyInput.R
3 | \name{inputServer}
4 | \alias{inputServer}
5 | \title{An input server for a \code{data.frame}}
6 | \usage{
7 | inputServer(id, data, ...)
8 | }
9 | \arguments{
10 | \item{id}{\code{character(1)} module id}
11 |
12 | \item{data}{single row \code{data.frame}}
13 |
14 | \item{...}{further arguments for methods}
15 | }
16 | \value{
17 | modified version of data
18 | }
19 | \description{
20 | An input server for a \code{data.frame}
21 | }
22 | \details{
23 | A new method for this can be added if you wish to alter the default behavior of the pop-up modals in \code{\link{eDT}}.
24 | }
25 | \examples{
26 | if(interactive()){
27 | library(shiny)
28 | ui <- inputUI('id')
29 | server <- function(input,output,session){
30 | input <- inputServer("id", mtcars[1,])
31 | observe({print(input())})
32 | }
33 | shinyApp(ui, server)
34 | }
35 |
36 | }
37 | \author{
38 | Jasper Schelfhout
39 | }
40 |
--------------------------------------------------------------------------------
/editbl/man/inputServer.default.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/shinyInput.R
3 | \name{inputServer.default}
4 | \alias{inputServer.default}
5 | \title{An input server for a \code{data.frame}}
6 | \usage{
7 | \method{inputServer}{default}(id, data, colnames, notEditable, foreignTbls, ...)
8 | }
9 | \arguments{
10 | \item{id}{\code{character(1)} module id}
11 |
12 | \item{data}{single row \code{data.frame}}
13 |
14 | \item{colnames}{named \code{character}}
15 |
16 | \item{notEditable}{\code{character} columns that should not be edited}
17 |
18 | \item{foreignTbls}{list of foreignTbls. See \code{\link{foreignTbl}}}
19 |
20 | \item{...}{for compatibility with other methods}
21 | }
22 | \value{
23 | reactive modified version of data
24 | }
25 | \description{
26 | An input server for a \code{data.frame}
27 | }
28 | \details{
29 | Reads all inputs ids that are identical to column names of the data
30 | and updates the data.
31 | }
32 | \author{
33 | Jasper Schelfhout
34 | }
35 |
--------------------------------------------------------------------------------
/editbl/man/inputUI.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/shinyInput.R
3 | \name{inputUI}
4 | \alias{inputUI}
5 | \title{An input UI for a \code{data.frame}}
6 | \usage{
7 | inputUI(id, ...)
8 | }
9 | \arguments{
10 | \item{id}{\code{character(1)} module id}
11 |
12 | \item{...}{arguments passed onto methods}
13 | }
14 | \value{
15 | HTML. A set of input fields corresponding to the given row.
16 | }
17 | \description{
18 | An input UI for a \code{data.frame}
19 | }
20 | \details{
21 | A new method for this can be added if you wish to alter the default behavior of the pop-up modals in \code{\link{eDT}}.
22 | }
23 | \examples{
24 | if(interactive()){
25 | library(shiny)
26 | ui <- inputUI('id')
27 | server <- function(input,output,session){
28 | input <- inputServer("id", mtcars[1,])
29 | observe({print(input())})
30 | }
31 | shinyApp(ui, server)
32 | }
33 |
34 | }
35 | \author{
36 | Jasper Schelfhout
37 | }
38 |
--------------------------------------------------------------------------------
/editbl/man/inputUI.default.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/shinyInput.R
3 | \name{inputUI.default}
4 | \alias{inputUI.default}
5 | \title{UI part for modal with input fields for editing}
6 | \usage{
7 | \method{inputUI}{default}(id, ...)
8 | }
9 | \arguments{
10 | \item{id}{character module id}
11 |
12 | \item{...}{for compatibility with method}
13 | }
14 | \value{
15 | HTML. A set of input fields corresponding to the given row.
16 | }
17 | \description{
18 | UI part for modal with input fields for editing
19 | }
20 | \details{
21 | The UI elements that have an id identical to a column name are used for updating the data.
22 | }
23 | \author{
24 | Jasper Schelfhout
25 | }
26 |
--------------------------------------------------------------------------------
/editbl/man/joinForeignTbl.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/foreignTbl.R
3 | \name{joinForeignTbl}
4 | \alias{joinForeignTbl}
5 | \title{Merge a tbl with it a foreignTbl}
6 | \usage{
7 | joinForeignTbl(
8 | tbl,
9 | foreignTbl,
10 | keepNA = TRUE,
11 | by = foreignTbl$by,
12 | copy = TRUE,
13 | type = c("inner", "left")[1]
14 | )
15 | }
16 | \arguments{
17 | \item{tbl}{\code{tbl}}
18 |
19 | \item{foreignTbl}{\code{list} as created by \code{\link{foreignTbl}}}
20 |
21 | \item{keepNA}{\code{logical} keep rows from tbl with NA keys.}
22 |
23 | \item{by}{named \code{character}, columns to join on.}
24 |
25 | \item{copy}{\code{logical}, whether or not to copy the \code{foreignTbl} to the source of argument \code{tbl} for joining.}
26 |
27 | \item{type}{\code{character(1)}, type of joint to perform. Can be 'inner' or 'left'.}
28 | }
29 | \value{
30 | \code{tbl}, containing both columns from argument \code{tbl} and argument \code{foreignTbl}.
31 | }
32 | \description{
33 | Merge a tbl with it a foreignTbl
34 | }
35 | \details{
36 | see also \code{dplyr} join functions, for example \code{dplyr::left_join}.
37 | }
38 | \author{
39 | Jasper Schelfhout
40 | }
41 |
--------------------------------------------------------------------------------
/editbl/man/overwriteDefaults.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{overwriteDefaults}
4 | \alias{overwriteDefaults}
5 | \title{Overwrite default settings with provided settings}
6 | \usage{
7 | overwriteDefaults(defaults, settings)
8 | }
9 | \arguments{
10 | \item{defaults}{named character vector}
11 |
12 | \item{settings}{named character vector}
13 | }
14 | \value{
15 | named character vector
16 | }
17 | \description{
18 | Overwrite default settings with provided settings
19 | }
20 | \author{
21 | Jasper Schelfhout
22 | }
23 |
--------------------------------------------------------------------------------
/editbl/man/rollbackTransaction.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tbl.R
3 | \name{rollbackTransaction}
4 | \alias{rollbackTransaction}
5 | \title{Start a transaction for a tibble}
6 | \usage{
7 | rollbackTransaction(tbl)
8 | }
9 | \arguments{
10 | \item{tbl}{\code{tbl}}
11 | }
12 | \description{
13 | Start a transaction for a tibble
14 | }
15 | \author{
16 | Jasper Schelfhout
17 | }
18 |
--------------------------------------------------------------------------------
/editbl/man/rowInsert.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tbl_dbi.R
3 | \name{rowInsert}
4 | \alias{rowInsert}
5 | \title{Add a row to a table in the database.}
6 | \usage{
7 | rowInsert(conn, table, values)
8 | }
9 | \arguments{
10 | \item{conn}{database connection object as given by \code{\link[DBI]{dbConnect}}.}
11 |
12 | \item{table}{character}
13 |
14 | \item{values}{named list, row to add. Names are database column names. Unspecified columns will get database defaults.}
15 | }
16 | \value{
17 | integer number of affected rows.
18 | }
19 | \description{
20 | Add a row to a table in the database.
21 | }
22 |
--------------------------------------------------------------------------------
/editbl/man/rowUpdate.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tbl_dbi.R
3 | \name{rowUpdate}
4 | \alias{rowUpdate}
5 | \title{Update rows in the database.}
6 | \usage{
7 | rowUpdate(conn, table, values, where)
8 | }
9 | \arguments{
10 | \item{conn}{database connection object as given by \code{\link[DBI]{dbConnect}}.}
11 |
12 | \item{table}{character}
13 |
14 | \item{values}{named list, values to be set. Names are database column names.}
15 |
16 | \item{where}{named list, values to filter on. Names are database column names. If NULL no filter is applied.}
17 | }
18 | \value{
19 | integer number of affected rows.
20 | }
21 | \description{
22 | Update rows in the database.
23 | }
24 |
--------------------------------------------------------------------------------
/editbl/man/rows_delete.dtplyr_step.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tbl_dtplyr_step.R
3 | \name{rows_delete.dtplyr_step}
4 | \alias{rows_delete.dtplyr_step}
5 | \title{rows_delete implementation for data.table backends.}
6 | \usage{
7 | \method{rows_delete}{dtplyr_step}(x, y, by = NULL, ..., unmatched, copy = FALSE, in_place = FALSE)
8 | }
9 | \arguments{
10 | \item{x, y}{A pair of data frames or data frame extensions (e.g. a tibble).
11 | \code{y} must have the same columns of \code{x} or a subset.}
12 |
13 | \item{by}{An unnamed character vector giving the key columns. The key columns
14 | must exist in both \code{x} and \code{y}. Keys typically uniquely identify each row,
15 | but this is only enforced for the key values of \code{y} when \code{rows_update()},
16 | \code{rows_patch()}, or \code{rows_upsert()} are used.
17 |
18 | By default, we use the first column in \code{y}, since the first column is
19 | a reasonable place to put an identifier variable.}
20 |
21 | \item{...}{Other parameters passed onto methods.}
22 |
23 | \item{unmatched}{For \code{rows_update()}, \code{rows_patch()}, and \code{rows_delete()},
24 | how should keys in \code{y} that are unmatched by the keys in \code{x} be handled?
25 |
26 | One of:
27 | \itemize{
28 | \item \code{"error"}, the default, will error if there are any keys in \code{y} that
29 | are unmatched by the keys in \code{x}.
30 | \item \code{"ignore"} will ignore rows in \code{y} with keys that are unmatched by the
31 | keys in \code{x}.
32 | }}
33 |
34 | \item{copy}{If \code{x} and \code{y} are not from the same data source,
35 | and \code{copy} is \code{TRUE}, then \code{y} will be copied into the
36 | same src as \code{x}. This allows you to join tables across srcs, but
37 | it is a potentially expensive operation so you must opt into it.}
38 |
39 | \item{in_place}{Should \code{x} be modified in place? This argument is only
40 | relevant for mutable backends (e.g. databases, data.tables).
41 |
42 | When \code{TRUE}, a modified version of \code{x} is returned invisibly;
43 | when \code{FALSE}, a new object representing the resulting changes is returned.}
44 | }
45 | \value{
46 | An object of the same type as \code{x}. The order of the rows and columns of \code{x}
47 | is preserved as much as possible. The output has the following properties:
48 | \itemize{
49 | \item \code{rows_update()} and \code{rows_patch()} preserve the number of rows;
50 | \code{rows_insert()}, \code{rows_append()}, and \code{rows_upsert()} return all existing
51 | rows and potentially new rows; \code{rows_delete()} returns a subset of the
52 | rows.
53 | \item Columns are not added, removed, or relocated, though the data may be
54 | updated.
55 | \item Groups are taken from \code{x}.
56 | \item Data frame attributes are taken from \code{x}.
57 | }
58 |
59 | If \code{in_place = TRUE}, the result will be returned invisibly.
60 | }
61 | \description{
62 | rows_delete implementation for data.table backends.
63 | }
64 | \author{
65 | Jasper Schelfhout
66 | }
67 |
--------------------------------------------------------------------------------
/editbl/man/runDemoApp.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/demoApp.R
3 | \name{runDemoApp}
4 | \alias{runDemoApp}
5 | \title{Run a demo app}
6 | \usage{
7 | runDemoApp(app = "database", ...)
8 | }
9 | \arguments{
10 | \item{app}{demoApp to run. Options: database / mtcars / custom}
11 |
12 | \item{...}{arguments passed onto the demoApp}
13 | }
14 | \value{
15 | An object that represents the app. Printing the object or passing it
16 | to \code{\link[shiny:runApp]{runApp()}} will run the app.
17 | }
18 | \description{
19 | Run a demo app
20 | }
21 | \details{
22 | These apps are for illustrative purposes.
23 | }
24 | \examples{
25 | ## Only run this example in interactive R sessions
26 | if(interactive()){
27 |
28 | # Database
29 | tmpFile <- tempfile(fileext = ".sqlite")
30 | file.copy(system.file("extdata", "chinook.sqlite", package = 'editbl'), tmpFile)
31 |
32 | conn <- connectDB(dbname = tmpFile)
33 |
34 | runDemoApp(app = "database", conn = conn)
35 | DBI::dbDisconnect(conn)
36 |
37 | unlink(tmpFile)
38 |
39 | # mtcars
40 | runDemoApp(app = "mtcars")
41 |
42 | # Any tibble of your liking
43 | runDemoApp(app = "custom", dplyr::tibble(iris))
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/editbl/man/runDemoApp_DB.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/demoApp.R
3 | \name{runDemoApp_DB}
4 | \alias{runDemoApp_DB}
5 | \title{Run a demo app}
6 | \usage{
7 | runDemoApp_DB()
8 | }
9 | \value{
10 | An object that represents the app. Printing the object or passing it
11 | to \code{\link[shiny:runApp]{runApp()}} will run the app.
12 | }
13 | \description{
14 | Run a demo app
15 | }
16 |
--------------------------------------------------------------------------------
/editbl/man/runDemoApp_custom.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/demoApp.R
3 | \name{runDemoApp_custom}
4 | \alias{runDemoApp_custom}
5 | \title{Run a custom demo app}
6 | \usage{
7 | runDemoApp_custom(x)
8 | }
9 | \arguments{
10 | \item{x}{\code{tbl}}
11 | }
12 | \value{
13 | An object that represents the app. Printing the object or passing it
14 | to \code{\link[shiny:runApp]{runApp()}} will run the app.
15 | }
16 | \description{
17 | Run a custom demo app
18 | }
19 |
--------------------------------------------------------------------------------
/editbl/man/runDemoApp_mtcars.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/demoApp.R
3 | \name{runDemoApp_mtcars}
4 | \alias{runDemoApp_mtcars}
5 | \title{Run a demo app}
6 | \usage{
7 | runDemoApp_mtcars()
8 | }
9 | \value{
10 | An object that represents the app. Printing the object or passing it
11 | to \code{\link[shiny:runApp]{runApp()}} will run the app.
12 | }
13 | \description{
14 | Run a demo app
15 | }
16 |
--------------------------------------------------------------------------------
/editbl/man/runDevApp.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/devApp.R
3 | \name{runDevApp}
4 | \alias{runDevApp}
5 | \title{Run a development app}
6 | \usage{
7 | runDevApp()
8 | }
9 | \value{
10 | An object that represents the app. Printing the object or passing it
11 | to \code{\link[shiny:runApp]{runApp()}} will run the app.
12 | }
13 | \description{
14 | Run a development app
15 | }
16 | \details{
17 | This app prints some of the server objects and has a button to interactively browse the code.
18 | This is useful for debugging and experimenting with new features.
19 | }
20 |
--------------------------------------------------------------------------------
/editbl/man/selectInputDT_Server.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/selectInputDT.R
3 | \name{selectInputDT_Server}
4 | \alias{selectInputDT_Server}
5 | \title{Server part to use a \code{\link[DT]{datatable}} as select input}
6 | \usage{
7 | selectInputDT_Server(
8 | id,
9 | label = "",
10 | choices,
11 | selected = NULL,
12 | multiple = FALSE
13 | )
14 | }
15 | \arguments{
16 | \item{id}{\code{character(1)} same one as used in \code{\link{selectInputDT_UI}}}
17 |
18 | \item{label}{\code{character(1)}}
19 |
20 | \item{choices}{\code{data.frame}}
21 |
22 | \item{selected}{\code{data.frame} with rows available in \code{choices}.}
23 |
24 | \item{multiple}{\code{logical}. Whether or not multiple row selection is allowed}
25 | }
26 | \value{
27 | A selection of rows from the \code{data.frame} provided under choices.
28 | }
29 | \description{
30 | Server part to use a \code{\link[DT]{datatable}} as select input
31 | }
32 | \examples{
33 | ## Only run this example in interactive R sessions
34 | if(interactive()){
35 | ui <- selectInputDT_UI('id')
36 | data <- data.frame(id = 1:3, name = letters[1:3])
37 | server <- function(input,output, session){
38 | selected = selectInputDT_Server('id', choices = data, selected = data[1,] )
39 | observe({print(selected())})
40 | }
41 | shiny::shinyApp(ui, server)
42 |
43 | }
44 | }
45 | \seealso{
46 | \code{shiny::selectInput}. This function can be more convenient for selecting rows
47 | with multiple columns.
48 | }
49 | \author{
50 | Jasper Schelfhout
51 | }
52 |
--------------------------------------------------------------------------------
/editbl/man/selectInputDT_UI.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/selectInputDT.R
3 | \name{selectInputDT_UI}
4 | \alias{selectInputDT_UI}
5 | \title{UI part of a DT select input}
6 | \usage{
7 | selectInputDT_UI(id)
8 | }
9 | \arguments{
10 | \item{id}{\code{character(1)} same one as used in \code{\link{selectInputDT_Server}}}
11 | }
12 | \value{
13 | HTML
14 | }
15 | \description{
16 | UI part of a DT select input
17 | }
18 | \examples{
19 | ## Only run this example in interactive R sessions
20 | if(interactive()){
21 | ui <- selectInputDT_UI('id')
22 | data <- data.frame(id = 1:3, name = letters[1:3])
23 | server <- function(input,output, session){
24 | selected = selectInputDT_Server('id', choices = data, selected = data[1,] )
25 | observe({print(selected())})
26 | }
27 | shiny::shinyApp(ui, server)
28 |
29 | }
30 | }
31 | \author{
32 | Jasper Schelfhout
33 | }
34 |
--------------------------------------------------------------------------------
/editbl/man/shinyInput.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/shinyInput.R
3 | \name{shinyInput}
4 | \alias{shinyInput}
5 | \title{Get a shiny input for a column of a \code{tbl}}
6 | \usage{
7 | shinyInput(x, inputId, label, selected)
8 | }
9 | \arguments{
10 | \item{x}{column}
11 |
12 | \item{inputId}{shiny input Id}
13 |
14 | \item{label}{\code{character(1)}}
15 |
16 | \item{selected}{object of class of x}
17 | }
18 | \value{
19 | shiny input
20 | }
21 | \description{
22 | Get a shiny input for a column of a \code{tbl}
23 | }
24 | \author{
25 | Jasper Schelfhout
26 | }
27 |
--------------------------------------------------------------------------------
/editbl/man/standardizeArgument_colnames.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{standardizeArgument_colnames}
4 | \alias{standardizeArgument_colnames}
5 | \title{Standardize colnames argument to the format of named character vector}
6 | \usage{
7 | standardizeArgument_colnames(colnames, data)
8 | }
9 | \arguments{
10 | \item{colnames}{if missing, the column names of the data; otherwise it can be
11 | an unnamed character vector of names you want to show in the table header
12 | instead of the default data column names; alternatively, you can provide a
13 | \emph{named} numeric or character vector of the form \code{'newName1' = i1,
14 | 'newName2' = i2} or \code{c('newName1' = 'oldName1', 'newName2' =
15 | 'oldName2', ...)}, where \code{newName} is the new name you want to show in
16 | the table, and \code{i} or \code{oldName} is the index of the current
17 | column name}
18 |
19 | \item{data}{\code{tbl}. The function will automatically cast to tbl if needed.}
20 | }
21 | \value{
22 | named character vector
23 | }
24 | \description{
25 | Standardize colnames argument to the format of named character vector
26 | }
27 | \author{
28 | Jasper Schelfhout
29 | }
30 |
--------------------------------------------------------------------------------
/editbl/man/standardizeArgument_editable.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{standardizeArgument_editable}
4 | \alias{standardizeArgument_editable}
5 | \title{Standardized editable argument to be in the form of a list}
6 | \usage{
7 | standardizeArgument_editable(editable, data)
8 | }
9 | \arguments{
10 | \item{editable}{\code{FALSE} to disable the table editor, or \code{TRUE} (or
11 | \code{"cell"}) to enable editing a single cell. Alternatively, you can set
12 | it to \code{"row"} to be able to edit a row, or \code{"column"} to edit a
13 | column, or \code{"all"} to edit all cells on the current page of the table.
14 | In all modes, start editing by doubleclicking on a cell. This argument can
15 | also be a list of the form \code{list(target = TARGET, disable =
16 | list(columns = INDICES))}, where \code{TARGET} can be \code{"cell"},
17 | \code{"row"}, \code{"column"}, or \code{"all"}, and \code{INDICES} is an
18 | integer vector of column indices. Use the list form if you want to disable
19 | editing certain columns. You can also restrict the editing to accept only
20 | numbers by setting this argument to a list of the form \code{list(target =
21 | TARGET, numeric = INDICES)} where \code{INDICES} can be the vector of the
22 | indices of the columns for which you want to restrict the editing to
23 | numbers or \code{"all"} to restrict the editing to numbers for all columns.
24 | If you don't set \code{numeric}, then the editing is restricted to numbers
25 | for all numeric columns; set \code{numeric = "none"} to disable this
26 | behavior. It is also possible to edit the cells in text areas, which are
27 | useful for large contents. For that, set the \code{editable} argument to a
28 | list of the form \code{list(target = TARGET, area = INDICES)} where
29 | \code{INDICES} can be the vector of the indices of the columns for which
30 | you want the text areas, or \code{"all"} if you want the text areas for
31 | all columns. Of course, you can request the numeric editing for some
32 | columns and the text areas for some other columns by setting
33 | \code{editable} to a list of the form \code{list(target = TARGET, numeric
34 | = INDICES1, area = INDICES2)}. Finally, you can edit date cells with a
35 | calendar with \code{list(target = TARGET, date = INDICES)}; the target
36 | columns must have the \code{Date} type. If you don't set \code{date} in
37 | the \code{editable} list, the editing with the calendar is automatically
38 | set for all \code{Date} columns.}
39 |
40 | \item{data}{\code{tbl}. The function will automatically cast to tbl if needed.}
41 | }
42 | \value{
43 | list of the form \code{list(target = foo, ...)}
44 | }
45 | \description{
46 | Standardized editable argument to be in the form of a list
47 | }
48 | \author{
49 | Jasper Schelfhout
50 | }
51 |
--------------------------------------------------------------------------------
/editbl/man/whereSQL.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tbl_dbi.R
3 | \name{whereSQL}
4 | \alias{whereSQL}
5 | \title{Generate where sql}
6 | \usage{
7 | whereSQL(conn, table, column, operator = "in", values = NULL)
8 | }
9 | \arguments{
10 | \item{conn}{database connection object as given by \code{\link[DBI]{dbConnect}}.}
11 |
12 | \item{table}{character table name (or alias used in query)}
13 |
14 | \item{column}{character column of table}
15 |
16 | \item{operator}{character}
17 |
18 | \item{values}{character vector of values}
19 | }
20 | \value{
21 | character sql
22 | }
23 | \description{
24 | Generate where sql
25 | }
26 | \author{
27 | Jasper Schelfhout
28 | }
29 |
--------------------------------------------------------------------------------
/editbl/tests/testthat.R:
--------------------------------------------------------------------------------
1 | library(testthat)
2 | library(editbl)
3 |
4 | test_check("editbl")
5 |
--------------------------------------------------------------------------------
/editbl/tests/testthat/test-demoApp.R:
--------------------------------------------------------------------------------
1 | test_that("Starting demo app db works", {
2 | runDemoApp (app = "database")
3 | expect_true(TRUE)
4 | })
5 |
6 | test_that("Starting demo app mtcars works", {
7 | runDemoApp (app = "mtcars")
8 | expect_true(TRUE)
9 | })
10 |
11 | test_that("Starting demo app custom works", {
12 | runDemoApp(app = "custom", dplyr::tibble(iris))
13 | expect_true(TRUE)
14 | })
--------------------------------------------------------------------------------
/editbl/tests/testthat/test-devApp.R:
--------------------------------------------------------------------------------
1 | test_that("Starting dev app db works", {
2 | runDevApp()
3 | expect_true(TRUE)
4 | })
--------------------------------------------------------------------------------
/editbl/tests/testthat/test-eDT.R:
--------------------------------------------------------------------------------
1 | test_that("passing on an empty tibble works", {
2 | data <- dplyr::as_tibble(sleep)
3 | data <- data[0,]
4 |
5 | # Try running the app manually
6 | ui <- eDTOutput("app")
7 | server <- function(input,output,session){
8 | eDTServer(id = "app", data = reactive(data))
9 | }
10 | shiny::shinyApp(ui, server)
11 |
12 | # Actual test
13 | shiny::testServer(
14 | app = eDTServer,
15 | args = list(data = data),
16 | expr = {
17 | session$setInputs(add = 1)
18 | session$setInputs(save = 1)
19 | session$setInputs(confirmCommit = 1)
20 | expect_true(nrow(rv$committedData) == 1)
21 | })
22 | })
23 |
24 | test_that("Editing a single table column works", {
25 | data <- dplyr::tibble(one_column = c(1,2))
26 |
27 | # Try running the app manually
28 | ui <- eDTOutput("app")
29 | server <- function(input,output,session){
30 | eDTServer(id = "app", data = reactive(data))
31 | }
32 | shiny::shinyApp(ui, server)
33 |
34 | # Actual test
35 | shiny::testServer(
36 | app = eDTServer,
37 | args = list(data = data),
38 | expr = {
39 | session$setInputs(edit_row_1 = 1)
40 | session$setInputs(confirmCommit = 1)
41 | })
42 | expect_true(TRUE)
43 | })
44 |
45 | test_that("Deletion of a row works", {
46 | data <- dplyr::tibble(id = 1:2, name = letters[1:2])
47 |
48 | shiny::testServer(
49 | app = eDTServer,
50 | args = list(data = data),
51 | expr = {
52 | session$flushReact()
53 | test_id = rv$modifiedData[1,"_editbl_identity"] # generated uuid
54 | session$setInputs(current_id = paste0('delete_row_', test_id))
55 | session$setInputs(delete = 1)
56 | session$flushReact()
57 | session$setInputs(confirmCommit = 1)
58 | expect_equal(nrow(result()),1)
59 | }
60 | )
61 | })
62 |
63 | test_that("Can not delete row when canDeleteRow blocks it", {
64 | data <- dplyr::tibble(id = 1:2, name = letters[1:2])
65 |
66 | shiny::testServer(
67 | app = eDTServer,
68 | args = list(data = data, canDeleteRow = function(...){FALSE}),
69 | expr = {
70 | session$flushReact()
71 | test_id = rv$modifiedData[1,"_editbl_identity"] # generated uuid
72 | session$setInputs(current_id = paste0('delete_row_', test_id))
73 | session$setInputs(delete = 1)
74 | session$flushReact()
75 | session$setInputs(confirmCommit = 1)
76 | expect_equal(nrow(result()),2)
77 | }
78 | )
79 | })
80 |
81 | test_that("working with selectInputDT works.", {
82 | songs <- tibble::tibble(
83 | song = c("Never gonna give you up", "Self Esteem"),
84 | artist_id = c(1,2)
85 | )
86 |
87 | artists <- dplyr::tibble(
88 | artist_id = c(1,2),
89 | first_name = c("Rick", "Dexter"),
90 | last_name = c('Astley', "Holland")
91 | )
92 |
93 | # Try running the app manually
94 | ui <- eDTOutput("app")
95 | server <- function(input,output,session){
96 | eDTServer(id = "app",
97 | data = songs,
98 | foreignTbls = list(
99 | foreignTbl(songs, artists, by = "artist_id", naturalKey = c("first_name", "last_name"))
100 | ),
101 | columnOrder = c("artist_id", "last_name", "first_name", "song")
102 | )
103 | }
104 |
105 | # Test if using edit a second time still works
106 | # Reactivity problem.
107 | # TODO: write proper tests
108 |
109 | expect_true(TRUE)
110 |
111 | shiny::shinyApp(ui, server)
112 | })
113 |
114 | test_that("Row dragging works when filter is on", {
115 | ui <- eDTOutput("app")
116 | server <- function(input,output,session){
117 | eDTServer(id = "app",
118 | data = mtcars,
119 | filter = "bottom")
120 | }
121 |
122 | # Test
123 | # 1. add on a filter for a column
124 | # 2. drag a cell using making use of the autofill extension
125 | # 3. remove the filter
126 | # 4. check if the same cell is still modified
127 | # TODO: try to encode this
128 | expect_true(TRUE)
129 |
130 | shiny::shinyApp(ui, server)
131 | })
132 |
133 | test_that("Can support all data types",{
134 | df = tibble(
135 | integer = 1L,
136 | double = 0.5,
137 | time = as.POSIXct('2020-01-01 01:02:03'),
138 | date = as.Date('2020-01-01')
139 | )
140 | ui <- eDTOutput("app")
141 | server <- function(input,output,session){
142 | eDTServer(id = "app",data = df)
143 | }
144 |
145 | # This just ensures the app doesn't crash on these different
146 | # data types
147 | expect_true(TRUE)
148 |
149 | shiny::shinyApp(ui, server)
150 | })
151 |
--------------------------------------------------------------------------------
/editbl/tests/testthat/test-foreignTbl.R:
--------------------------------------------------------------------------------
1 | test_that("passing on a foreign tibble works", {
2 | a <- tibble::tibble(
3 | a = c(1,2,3),
4 | key1 = c(NA,2,4),
5 | key2 = c(NA,2,4))
6 |
7 | b <- foreignTbl(
8 | a,
9 | tibble::tibble(
10 | b = c("a", "b", "c"),
11 | key1 = c(1,2,3),
12 | key2 = c(1,2,3)
13 | ),
14 | naturalKey = "b"
15 | )
16 |
17 | # Try running the app manually
18 | ui <- eDTOutput("app")
19 | server <- function(input,output,session){
20 | eDT(id = "app",
21 | data = reactive(a),
22 | foreignTbls = list(b),
23 | options = list(columnDefs = list(list(visible=FALSE, targets="key2"))))
24 | }
25 | shiny::shinyApp(ui, server)
26 |
27 | # Actual test
28 | shiny::testServer(
29 | app = eDTServer,
30 | args = list(data = reactive(a), foreignTbls = list(b)),
31 | expr = {
32 | expect_true(TRUE)
33 | })
34 | })
35 |
36 | test_that("Can use foreign tibbles to fill in non natural key values.",{
37 | tbl <- tibble::tibble(
38 | a = c(1,2),
39 | key1 = as.double(c(NA,NA)),
40 | key2 = as.double(c(NA,NA))
41 | )
42 |
43 | merged_tbl <- cbind(tbl, tibble(
44 | b = c("b1", "b2"),
45 | c = c("c1", "c2")))
46 |
47 |
48 | b <- foreignTbl(
49 | tbl,
50 | tibble::tibble(
51 | b = c("b1", "b2", "b3"),
52 | key1 = c(1,2,3)
53 | ),
54 | by = "key1",
55 | naturalKey = "b"
56 | )
57 |
58 | c <- foreignTbl(
59 | tbl,
60 | tibble::tibble(
61 | c = c("c1", "c2", "c3"),
62 | key2 = c(1,2,3)
63 | ),
64 | by = "key2",
65 | naturalKey = "c"
66 | )
67 |
68 | foreignTbls = list(b, c)
69 |
70 | result <- fillDeductedColumns(merged_tbl, foreignTbls)
71 |
72 | expect_equal(result$key1, c(1,2))
73 | expect_equal(result$key2, c(1,2))
74 | })
75 |
76 | test_that("Contradicting foreign tibbles give error when filling in data.",{
77 | tbl <- tibble::tibble(
78 | a = c(1,2),
79 | key = as.double(c(NA,NA))
80 | )
81 |
82 | merged_tbl <- tibble::tibble(cbind(tbl,
83 | tibble::tibble(color = c("blue", "orange"))
84 | ))
85 |
86 | b <- foreignTbl(
87 | tbl,
88 | tibble::tibble(
89 | color = c("blue", "orange"),
90 | key = c(1,2)
91 | ),
92 | by = "key",
93 | naturalKey = "color"
94 | )
95 |
96 | c <- foreignTbl(
97 | tbl,
98 | tibble::tibble(
99 | color = c("blue", "purple"),
100 | key = c(2,1)
101 | ),
102 | by = "key",
103 | naturalKey = "color"
104 | )
105 |
106 | expect_error({fillDeductedColumns(tbl, foreignTbls = list(b,c))})
107 | expect_error({fillDeductedColumns(tbl, foreignTbls = list(c,b))})
108 |
109 | })
110 |
111 | test_that("Empty foreign tibbles list returns given tibble",{
112 | tbl <- tibble::tibble(
113 | a = c(1,2),
114 | key = as.double(c(NA,NA)),
115 | color = c("blue", "orange")
116 | )
117 |
118 | result <- fillDeductedColumns(tbl, foreignTbls = list())
119 | expect_equal(result, tbl)
120 | })
121 |
122 | test_that("checkForeignTbls throws error",{
123 |
124 | tbl <- tibble::tibble(
125 | a = c(1,2, 3),
126 | key = c(1,2, 3)
127 | )
128 |
129 | merged_tbl <- cbind(tbl,
130 | tibble::tibble(color = c("blue", "orange", "purple"))
131 | )
132 |
133 | b <- foreignTbl(
134 | tbl,
135 | tibble::tibble(
136 | color = c("blue", "orange"),
137 | key = c(1,2)
138 | ),
139 | by = "key",
140 | naturalKey = "color"
141 | )
142 |
143 | expect_error(checkForeignTbls(tbl = merged_tbl,foreignTbls = list(b)))
144 | })
145 |
146 | test_that("joinForeignTbl() type 'inner' works.",{
147 | a <- tibble::tibble(
148 | a = c(1,2,3),
149 | key1 = c(NA,2,4),
150 | key2 = c(NA,2,4))
151 |
152 | b <- tibble::tibble(
153 | b = c("a", "b", "c"),
154 | key1 = c(1,2,3),
155 | key2 = c(1,2,3)
156 | )
157 |
158 | foreignTbl <- foreignTbl(a,b)
159 |
160 | result <- joinForeignTbl(a, foreignTbl, type = 'inner')
161 |
162 | # NA keys in 'a' are expected to stay.
163 | expect_equal(colnames(result), c("a", "key1", "key2", "b"))
164 | expect_true(nrow(result) == 2)
165 | })
166 |
167 | test_that("joinForeignTbl() type 'left' works.",{
168 | a <- tibble::tibble(
169 | a = c(1,2,3),
170 | key1 = c(NA,2,4),
171 | key2 = c(NA,2,4))
172 |
173 | b <- tibble::tibble(
174 | b = c("a", "b", "c"),
175 | key1 = c(1,2,3),
176 | key2 = c(1,2,3)
177 | )
178 |
179 | foreignTbl <- foreignTbl(a,b)
180 |
181 | result <- joinForeignTbl(a, foreignTbl, type = 'left')
182 |
183 | expect_equal(colnames(result), c("a", "key1", "key2", "b"))
184 | expect_true(nrow(result) == 3)
185 | })
186 |
187 |
--------------------------------------------------------------------------------
/editbl/tests/testthat/test-selectInputDT.R:
--------------------------------------------------------------------------------
1 | test_that("selectInputDT works", {
2 | # Try running the app manually
3 | df <- data.frame(
4 | id = 1:10,
5 | name = letters[1:10]
6 | )
7 |
8 | ui <- selectInputDT_UI("app")
9 |
10 | server <- function(input,output,session){
11 | selectInputDT_Server(id = "app", label = "test", choices = df, selected = df[4,])
12 | }
13 | shiny::shinyApp(ui, server)
14 |
15 | # Actual test
16 | shiny::testServer(
17 | app = selectInputDT_Server,
18 | args = list(id = "app", label = "test", choices = df, selected = df[4,]),
19 | expr = {
20 | session$setInputs(DT_rows_selected = 2)
21 | expect_equal(data_selection_first()[input$DT_rows_selected,]$name, "a") # Not 'b' because selection always comes first
22 | })
23 | })
24 |
--------------------------------------------------------------------------------
/editbl/tests/testthat/test-shinyInput.R:
--------------------------------------------------------------------------------
1 | test_that("Input generation for single column data.frame works", {
2 | data <- data.frame(x = as.numeric(1))
3 |
4 | # Try running the app manually
5 | ui <- inputUI.default(id = "app")
6 | server <- function(input,output,session){
7 | inputServer.default(id = "app", data = reactive(data))
8 | }
9 | shiny::shinyApp(ui, server)
10 |
11 | # Actual test
12 | shiny::testServer(
13 | app = inputServer.default,
14 | args = list(data = data),
15 | expr = {
16 | session$flushReact()
17 | session$setInputs(x = 2)
18 | expect_equal(newData(), data.frame(x = 2))
19 | })
20 | })
21 |
--------------------------------------------------------------------------------
/editbl/tests/testthat/test-tbl.R:
--------------------------------------------------------------------------------
1 | conn <- DBI::dbConnect(RSQLite::SQLite(), ":memory:")
2 | test_df <- data.frame(
3 | id = 1:3,
4 | name = letters[1:3]
5 | )
6 | DBI::dbCreateTable(conn, "test", test_df)
7 | DBI::dbAppendTable(conn, "test", test_df)
8 |
9 | test_tbl <- dplyr::tbl(conn, "test")
10 |
11 | test_that("beginTransaction and commitTransaction works for tbl_dbi",{
12 | beginTransaction(test_tbl)
13 |
14 | # rows_delete will start nested transaction if 'y' is not of same source as 'x'
15 | dbplyr::db_copy_to(conn, table = 'y1_temp', values = data.frame(id = 2), in_transaction = FALSE)
16 | y <- tbl(conn, 'y1_temp')
17 |
18 | rows_delete(x = test_tbl, y = y, in_place = TRUE, copy = TRUE, unmatched = "ignore")
19 | commitTransaction(test_tbl)
20 | result <- DBI::dbGetQuery(conn, "select * from test")
21 | expect_true(nrow(result) == 2)
22 | })
23 |
24 | test_that("beginTransaction and rollbackTransaction works for tbl_dbi",{
25 | beginTransaction(test_tbl)
26 |
27 | # rows_delete will start nested transaction if 'y' is not of same source as 'x'
28 | dbplyr::db_copy_to(conn, table = 'y2_temp', values = data.frame(id = 1), in_transaction = FALSE)
29 | y <- tbl(conn, 'y2_temp')
30 | rows_delete(x = test_tbl, y = y, in_place = TRUE, copy = TRUE, unmatched = "ignore")
31 |
32 | rollbackTransaction(test_tbl)
33 | result <- DBI::dbGetQuery(conn, "select * from test")
34 | expect_true(nrow(result) == 2)
35 | })
36 |
37 | DBI::dbDisconnect(conn)
38 |
--------------------------------------------------------------------------------
/editbl/tests/testthat/test-tbl_dbi.R:
--------------------------------------------------------------------------------
1 | conn <- DBI::dbConnect(RSQLite::SQLite(), ":memory:")
2 | test_df <- data.frame(
3 | id = 1:3,
4 | name = letters[1:3]
5 | )
6 | DBI::dbCreateTable(conn, "test", test_df)
7 | DBI::dbAppendTable(conn, "test", test_df)
8 |
9 | test_tbl <- dplyr::tbl(conn, "test")
10 |
11 | test_that("e_rows_insert.tbl_dbi works",{
12 | e_rows_insert.tbl_dbi(test_tbl, data.frame(id = 4, name = "insert"), in_place = TRUE)
13 | result <- DBI::dbGetQuery(conn, "select * from test where id = 4")$name
14 | expect_equal(result,'insert')
15 | })
16 |
17 | test_that("e_rows_update.tbl_dbi works",{
18 | e_rows_update(test_tbl, data.frame(id = 1, name = "update"), by = "id", in_place = TRUE)
19 | result <- DBI::dbGetQuery(conn, "select * from test where id = 1")$name
20 | expect_equal(result,'update')
21 | })
22 |
23 | DBI::dbDisconnect(conn)
24 |
--------------------------------------------------------------------------------
/editbl/tests/testthat/test-tbl_df.R:
--------------------------------------------------------------------------------
1 | test_that("e_rows_update can update a row in a data.frame", {
2 | x <- mtcars
3 | x$id <- seq_len(nrow(x))
4 | y <- x[1,]
5 | y$mpg <- 1000
6 |
7 | result <- e_rows_update(x = x, y = y, by = "id")
8 | expect_equal(result[1,"mpg"][[1]], 1000)
9 | })
10 |
--------------------------------------------------------------------------------
/editbl/tests/testthat/test-tbl_dtplyr_step.R:
--------------------------------------------------------------------------------
1 | test_that("e_rows_insert add row to data.table", {
2 | x <- dtplyr::lazy_dt(data.table::data.table(mtcars))
3 | y <- mtcars[1,]
4 | result <- e_rows_insert(x,y)
5 | expect_equal(nrow(x) + 1, nrow(result))
6 | })
7 |
8 | test_that("e_rows_update can update a row in a data.table", {
9 | dt <- data.table::data.table(mtcars)
10 | dt$id <- seq_len(nrow(dt))
11 | x <- dtplyr::lazy_dt(dt)
12 | y <- dt[1,]
13 | y$mpg <- 1000
14 |
15 | result <- e_rows_update(x = x, y = y, by = "id")
16 | expect_equal(data.table::as.data.table(result)[1,"mpg"][[1]], 1000)
17 | })
18 |
19 | test_that("rows_delete can delete a row in a data.table", {
20 | dt <- data.table::data.table(mtcars)
21 | dt$id <- seq_len(nrow(dt))
22 | x <- dtplyr::lazy_dt(dt)
23 | y <- dt[1,]
24 | y$mpg <- 1000
25 |
26 | result <- rows_delete(x = x, y = y, by = "id")
27 | expect_equal(nrow(result), nrow(dt) - 1)
28 | })
29 |
30 |
--------------------------------------------------------------------------------
/editbl/tests/testthat/test-utils.R:
--------------------------------------------------------------------------------
1 | # Data.frame with multiple data types
2 | df_typed <- data.frame(
3 | char = "x",
4 | num = 1,
5 | int = 1L,
6 | date = as.Date('2023-01-01'),
7 | time = as.POSIXct('2023-01-01 00:00:01', tz = "UTC")
8 | )
9 |
10 | df_char <- data.frame(
11 | char = "x",
12 | num = "1",
13 | int = "1",
14 | date = "2023-01-01",
15 | time = '2023-01-01 00:00:01'
16 | )
17 |
18 | df_template <- df_typed[0,]
19 |
20 |
21 |
22 | # Actual tests
23 | test_that("coerceColumns works",{
24 | result <- coerceColumns(df_template, df_char)
25 | expect_equal(
26 | result,
27 | df_typed
28 | )
29 | })
30 |
31 | test_that('coalesce returns first value',{
32 | result <- coalesce(x = 2, y = 3)
33 | expect_equal(result,2)
34 | })
35 |
36 | test_that('coalesce skips null',{
37 | result <- coalesce(x = NULL, y = 3, z = 4)
38 | expect_equal(result,3)
39 | })
40 |
41 | test_that('coalesce gives NULL when only NULL available',{
42 | result <- coalesce(x = NULL, y = NULL)
43 | expect_equal(result,NULL)
44 | })
45 |
46 | test_that('coalesce gives when no parameters available',{
47 | result <- coalesce()
48 | expect_equal(result,NULL)
49 | })
50 |
51 | test_that('castForDisplay gives when no parameters available',{
52 | result <- castForDisplay(df_typed)
53 | int <- c("int")
54 | notInt <- setdiff(colnames(df_typed), "int")
55 | expect_equal(result[,notInt],df_char[,notInt])
56 | expect_equal(result[,int],df_typed[,int])
57 | })
58 |
59 | test_that("castToTbl works for data.frame",{
60 | result <- castToTbl(df_typed)
61 | expect_equal(result, tibble::tibble(
62 | char = "x",
63 | num = 1,
64 | int = 1L,
65 | date = as.Date("2023-01-01"),
66 | time = as.POSIXct("2023-01-01 00:00:01", tz = "UTC")
67 | ))
68 | })
69 |
70 | test_that("castFromTbl can cast to data.frame",{
71 | result <- castFromTbl(tibble::as_tibble(mtcars), mtcars)
72 | expect_true(is.data.frame(result))
73 | expect_equal(colnames(result), colnames(mtcars))
74 | })
75 |
76 | test_that("castFromTbl can cast to data.table",{
77 | result <- castFromTbl(tibble::as_tibble(mtcars), data.table::as.data.table(mtcars))
78 | expect_true(data.table::is.data.table(result))
79 | expect_equal(colnames(result), colnames(mtcars))
80 | })
81 |
82 | test_that("castToTemplate can cast from data.frame to data.table",{
83 | result <- castToTemplate(x = iris, template = data.table::data.table(iris))
84 | expect_equal(result, data.table::as.data.table(iris))
85 | })
86 |
87 | test_that("castToTemplate can cast from data.frame to data.table with empty template",{
88 | result <- castToTemplate(x = iris, template = data.table::data.table(iris[0,]))
89 | expect_equal(result, data.table::as.data.table(iris))
90 | })
91 |
92 | test_that("castToTemplate can cast from data.frame to tbl",{
93 | result <- castToTemplate(x = iris, template = dplyr::as_tibble(iris))
94 | expect_equal(result, dplyr::as_tibble(iris))
95 | })
96 |
97 | test_that("castToTemplate can cast from data.frame to tbl with empty template",{
98 | result <- castToTemplate(x = iris, template = dplyr::as_tibble(iris[0,]))
99 | expect_equal(result, dplyr::as_tibble(iris))
100 | })
101 |
102 | test_that("castToTemplate can cast from tbl to data.frame",{
103 | result <- castToTemplate(x = dplyr::as_tibble(iris), template = iris)
104 | expect_equal(result, iris)
105 | })
106 |
107 | test_that("castToTemplate can cast from tbl to data.frame with empty template",{
108 | result <- castToTemplate(x = dplyr::as_tibble(iris), template = iris[0,])
109 | expect_equal(result, iris)
110 | })
111 |
112 | test_that("castToTemplate can cast from data.table to data.frame",{
113 | result <- castToTemplate(x = data.table::as.data.table(iris), template = iris)
114 | expect_equal(result, iris)
115 | })
116 |
117 | test_that("castToTemplate can cast from data.table to data.frame with empty template",{
118 | result <- castToTemplate(x = data.table::as.data.table(iris), template = iris[0,])
119 | expect_equal(result, iris)
120 | })
121 |
122 | test_that("castToTemplate can cast from data.table to tbl",{
123 | result <- castToTemplate(x = data.table::as.data.table(iris), template = dplyr::as_tibble(iris))
124 | expect_equal(result, dplyr::as_tibble(iris))
125 | })
126 |
127 | test_that("castToTemplate can cast from data.table to tbl with empty template",{
128 | result <- castToTemplate(x = data.table::as.data.table(iris), template = dplyr::as_tibble(iris[0,]))
129 | expect_equal(result, dplyr::as_tibble(iris))
130 | })
131 |
132 | test_that("castToTemplate can cast from tbl to data.table",{
133 | result <- castToTemplate(x = dplyr::as_tibble(iris), template = data.table::as.data.table(iris))
134 | expect_equal(result, data.table::as.data.table(iris))
135 | })
136 |
137 | test_that("castToTemplate can cast from tbl to data.table with empty template",{
138 | result <- castToTemplate(x = iris, template = data.table::as.data.table(iris[0,]))
139 | expect_equal(result, data.table::as.data.table(iris))
140 | })
141 |
142 | test_that("standardizeArgument_colnames returns named character for named integer",{
143 | result <- standardizeArgument_colnames(c("test" = 1), dplyr::as_tibble(mtcars))
144 | expect_equal(result, c("test" = "mpg"))
145 | })
146 |
147 | test_that("standardizeArgument_colnames returns named character for unnamed character",{
148 | result <- standardizeArgument_colnames(c("test"), dplyr::as_tibble(mtcars))
149 | expect_equal(result, c("test" = "mpg"))
150 | })
151 |
152 | test_that("standardizeArgument_colnames returns named character for named character",{
153 | result <- standardizeArgument_colnames(c("test" = "mpg"), dplyr::as_tibble(mtcars))
154 | expect_equal(result, c("test" = "mpg"))
155 | })
156 |
157 | test_that("standardizeArgument_editable TRUE",{
158 | result <- standardizeArgument_editable(TRUE)
159 | expect_equal(result, list(target = "cell"))
160 | })
161 |
162 | test_that("standardizeArgument_editable FALSE",{
163 | result <- standardizeArgument_editable(FALSE, mtcars)
164 | expect_equal(result, list(target = "cell", disable = list(columns = 1:11)))
165 | })
166 |
167 | test_that("standardizeArgument_editable 'row'",{
168 | result <- standardizeArgument_editable('row')
169 | expect_equal(result, list(target = "row"))
170 | })
171 |
172 | test_that("standardizeArgument_editable list",{
173 | result <- standardizeArgument_editable(list(target = "cell", numeric = 2))
174 | expect_equal(result, list(target = "cell", numeric = 2))
175 | })
176 |
177 | test_that("standardizeArgument_editable Error",{
178 | expect_error(standardizeArgument_editable(1))
179 | })
180 |
181 | test_that('overwriteDefaults works',{
182 | x = c(a = 'a', b = 'b')
183 | y = c(a='c')
184 | result <- overwriteDefaults(x,y)
185 | expect_equal(result, c(a = 'c', b = 'b'))
186 | })
187 |
--------------------------------------------------------------------------------
/editbl/vignettes/howto_relational_db_dm.rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Edit tables in a relational database - dm package"
3 | date: "`r Sys.Date()`"
4 | output:
5 | rmarkdown::html_vignette
6 | vignette: >
7 | %\VignetteEncoding{UTF-8}
8 | %\VignetteIndexEntry{How to: dm package integration for database editor.}
9 | %\VignetteEngine{knitr::rmarkdown}
10 | ---
11 |
12 |
13 | If it is your first time using
14 | `editbl`, make sure to first read the introductory vignette
15 | on how to work with relational databases (`vignette("howto_relational_db")`).
16 |
17 | This document describes how make use of the
18 | [{dm} package](https://CRAN.R-project.org/package=dm). This package is
19 | useful to extract relational data models out of a database into R. Which can
20 | help in setting up a correct configuration for `editbl`.
21 |
22 | ```{r echo = TRUE, results = 'hide'}
23 | library(dplyr)
24 | library(shiny)
25 | library(editbl)
26 | library(dm)
27 | ```
28 |
29 | ## Creating a database connection
30 |
31 | The first thing you need is a database connection. Here we connect to an
32 | [sqlite](https://en.wikipedia.org/wiki/SQLite) file, which is a portable
33 | database format.
34 |
35 | ```{r echo = TRUE, results = 'hide'}
36 | tmpFile <- tempfile(fileext = ".sqlite")
37 | file.copy(system.file("extdata", "chinook.sqlite", package = 'editbl'), tmpFile)
38 | conn <- DBI::dbConnect(
39 | dbname = tmpFile,
40 | drv = RSQLite::SQLite()
41 | )
42 | ```
43 |
44 | ## Setting up the data model
45 |
46 | ```{r echo = TRUE, results = 'hide'}
47 | dm <- dm::dm_from_con(conn, learn_keys = FALSE)
48 | ```
49 |
50 | For some databases this is all you need to do.
51 | Currently `dm` can not (yet) learn the keys from SQLite.
52 | Thus we still manually have to specify the primary and foreign keys.
53 | ```{r echo = TRUE, results = 'hide'}
54 | dm <- dm %>%
55 | dm_add_pk(Artist, ArtistId) %>%
56 | dm_add_pk(Album, AlbumId) %>%
57 | dm_add_pk(Customer, CustomerId) %>%
58 | dm_add_pk(Employee, EmployeeId) %>%
59 | dm_add_pk(Genre, GenreId) %>%
60 | dm_add_pk(Invoice, InvoiceId) %>%
61 | dm_add_pk(InvoiceLine, InvoiceLineId) %>%
62 | dm_add_pk(MediaType, MediaTypeId) %>%
63 | dm_add_pk(Playlist, PlaylistId) %>%
64 | dm_add_pk(PlaylistTrack, c(PlaylistId, TrackId)) %>%
65 | dm_add_pk(Track, TrackId)
66 |
67 | ```
68 |
69 | ```{r echo = TRUE, results = 'hide'}
70 | dm <- dm %>%
71 | dm_add_fk(
72 | table = Album,
73 | columns = ArtistId,
74 | ref_table = Artist) %>%
75 | dm_add_fk(
76 | table = Invoice,
77 | columns = CustomerId,
78 | ref_table = Customer) %>%
79 | dm_add_fk(
80 | table = InvoiceLine,
81 | columns = InvoiceId,
82 | ref_table = Invoice) %>%
83 | dm_add_fk(
84 | table = InvoiceLine,
85 | columns = TrackId,
86 | ref_table = Track) %>%
87 | dm_add_fk(
88 | table = PlaylistTrack,
89 | columns = TrackId,
90 | ref_table = Track) %>%
91 | dm_add_fk(
92 | table = PlaylistTrack,
93 | columns = PlaylistId,
94 | ref_table = Playlist) %>%
95 | dm_add_fk(
96 | table = Track,
97 | columns = AlbumId,
98 | ref_table = Album) %>%
99 | dm_add_fk(
100 | table = Track,
101 | columns = MediaTypeId,
102 | ref_table = MediaType) %>%
103 | dm_add_fk(
104 | table = Track,
105 | columns = GenreId,
106 | ref_table = Genre)
107 | ```
108 |
109 | ## Fully fletched table editor
110 |
111 | A relational database consists out of many normalized tables. This is a perfect
112 | model for storing data, since it avoids duplicate information. However, it
113 | often leads to rather incomprehensible tables with a lot of 'id' columns. The
114 | goal of this editor is therefore to give people the opportunity to edit a table
115 | in its 'flat' form. Meaning you join all tables with additional information
116 | based on these 'id' keys. See also this function of `dm`:
117 |
118 | ```{r}
119 | dm::dm_flatten_to_tbl(dm, "Album", .recursive = TRUE)
120 | ```
121 | As you can see, providing the `ArtistName` to a user is way more convenient than
122 | just the `ArtistId`.
123 |
124 | `editbl::eDT` can handle similar joins by its
125 | `foreignTbls` argument. Let us define a function that
126 | extracts the needed information from a `dm` object.
127 |
128 | ```{r}
129 | getForeignTbls <- function(dm, table){
130 | dm_fks <- dm::dm_get_all_fks(dm)
131 | dm_fks <- dm_fks[dm_fks$child_table == table,]
132 | tbl_list <- dm::dm_get_tables(dm)
133 |
134 | foreignTbls <- lapply(seq_len(nrow(dm_fks)), function(i){
135 | r <- dm_fks[i,]
136 | x <- tbl_list[r$child_table][[1]]
137 | y <- dm::dm_flatten_to_tbl(dm, !!(r$parent_table), .recursive = TRUE)
138 |
139 | child_fks <- unlist(r$child_fk_cols)
140 | parent_fks <- unlist(r$parent_key_cols)
141 |
142 | # Renaming of parent colums to avoid naming conflicts
143 | # Done a bit heuristically here for convenience.
144 | lookup <- parent_fks
145 | names(lookup) <- child_fks
146 | other_parent_cols <- setdiff(colnames(y), parent_fks)
147 | names(other_parent_cols) <- paste(r$parent_table, other_parent_cols, sep = '.')
148 | lookup <- c(lookup, other_parent_cols)
149 | y <- y %>% dplyr::rename(all_of(lookup))
150 |
151 | editbl::foreignTbl(
152 | x = x,
153 | y = y,
154 | by = child_fks,
155 | naturalKey = colnames(y)
156 | )
157 |
158 | })
159 | foreignTbls
160 | }
161 |
162 | ```
163 |
164 | Next, let's use this to build a shiny app.
165 |
166 | ```{r}
167 | dbUI <- function(id) {
168 | ns <- NS(id)
169 | fluidPage(
170 | uiOutput(outputId = ns('selectUI')),
171 | eDTOutput(id = ns('DT'))
172 | )
173 | }
174 |
175 | dbServer <- function(id, dm) {
176 | moduleServer(
177 | id,
178 | function(input, output, session) {
179 | ns <- session$ns
180 |
181 | tables <- dm::dm_get_tables(dm)
182 |
183 | output$selectUI <- renderUI({
184 | selectInput(ns('table'), label = 'table', choices = names(tables))
185 | })
186 |
187 | data <- reactive({
188 | req(input$table)
189 | tables[input$table][[1]]
190 | })
191 |
192 | foreignTbls <- reactive({
193 | req(input$table)
194 | getForeignTbls(dm, input$table)
195 | })
196 |
197 | eDT(
198 | id = "DT",
199 | data = data,
200 | foreignTbls = foreignTbls,
201 | in_place = TRUE
202 | )
203 |
204 | invisible()
205 | }
206 | )
207 | }
208 |
209 | ```
210 |
211 |
212 | ```{r, screenshot.opts = list(vwidth = 700, vheight = 500, delay = 1), screenshot.alt = 'screenshots/howto_relational_db_dm_1.png'}
213 | shiny::shinyApp(
214 | ui = dbUI('id'),
215 | server = function(input, output,session){
216 | dbServer('id', dm)
217 | })
218 | ```
219 |
220 | As you click the 'edit' button, you will notice you can now select rows from the
221 | referenced tables. This makes it easier to navigate compared to just having
222 | id's to work with.
223 |
224 |
225 |
226 |
--------------------------------------------------------------------------------
/editbl/vignettes/howto_row_level_access.rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Row level access"
3 | date: "`r Sys.Date()`"
4 | output:
5 | rmarkdown::html_vignette
6 | vignette: >
7 | %\VignetteEncoding{UTF-8}
8 | %\VignetteIndexEntry{How to: implement row level access.}
9 | %\VignetteEngine{knitr::rmarkdown}
10 | ---
11 |
12 | Sometimes you do not want to give a user access to the entire dataset.
13 | You can either hide rows or give read-only access.
14 |
15 | ## Hide rows completely
16 |
17 | In this example we only allow user Mickey to see his own row.
18 |
19 | We simply can use `dplyr::filter()` on the table.
20 | Note that this is most useful if you combine this with backends that support
21 | `in_place` editing. E.g. you can retrieve only a subset
22 | of rows from a database and specifically modify those.
23 | Take a look at the 'relational database' vignettes for more information
24 | on how to work with a database.
25 |
26 | ```{r, screenshot.opts = list(vwidth = 700, vheight = 500), , screenshot.alt = 'screenshots/howto_row_level_access_1.png'}
27 | library(editbl)
28 | library(shiny)
29 | conn <- DBI::dbConnect(RSQLite::SQLite(), "")
30 | df <- data.frame(
31 | user = c("Albert","Donald","Mickey"),
32 | email = c('albert@einstein.com', 'donald@duck.com', 'mickey@mouse.com')
33 | )
34 | DBI::dbWriteTable(conn, "characters", df)
35 | tibble <- dplyr::tbl(conn, 'characters')
36 |
37 | CURRENT_USER = 'Mickey'
38 |
39 | shiny::shinyApp(
40 | ui = editbl::eDTOutput('id'),
41 | server = function(input, output,session){
42 | result <- eDT(id='id',
43 | data = tibble %>% filter(user == CURRENT_USER),
44 | in_place = TRUE
45 | )
46 | })
47 |
48 | print(tibble)
49 |
50 | DBI::dbDisconnect(conn)
51 | ```
52 |
53 | ## Read-only access
54 |
55 | In this example we only allow user Mickey to modify his own row. In contrast to
56 | the previous example, he can still read data from others.
57 |
58 | The arguments `canEditRow` and `canDeleteRow` can be used
59 | to specify logic describing if modfications are allowed.
60 | The passed on logic shoud be a function with the argument `row`. This is a
61 | single row of the displayed table in datatype `tibble`.
62 |
63 | ```{r, screenshot.opts = list(vwidth = 700, vheight = 500), , screenshot.alt = 'screenshots/howto_row_level_access_2.png'}
64 | library(editbl)
65 | df <- tibble::tibble(
66 | user = c("Albert","Donald","Mickey"),
67 | email = c('albert@einstein.com', 'donald@duck.com', 'mickey@mouse.com')
68 | )
69 |
70 | CURRENT_USER = 'Mickey'
71 |
72 | rowModificationLogic <- function(row){
73 | if (row[,'user'] == CURRENT_USER){
74 | TRUE
75 | } else {
76 | FALSE
77 | }
78 | }
79 |
80 | shiny::shinyApp(
81 | ui = editbl::eDTOutput('id'),
82 | server = function(input, output,session){
83 | eDT(id='id',
84 | data = df,
85 | canEditRow = rowModificationLogic,
86 | canDeleteRow = rowModificationLogic
87 | )
88 | })
89 |
90 | ```
91 |
--------------------------------------------------------------------------------
/editbl/vignettes/howto_switch_from_DT.rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Switching from DT"
3 | date: "`r Sys.Date()`"
4 | output:
5 | rmarkdown::html_vignette
6 | vignette: >
7 | %\VignetteEncoding{UTF-8}
8 | %\VignetteIndexEntry{How to: adjust your `DT` code to use `eDT` instead.}
9 | %\VignetteEngine{knitr::rmarkdown}
10 | ---
11 |
12 | Let's say you already use `DT::datatable()` to display your data, but want to
13 | switch to `editbl::eDT()` to be able to edit it. What should you look out for?
14 |
15 | * `eDTOutput()` uses an `id` argument instead of `outputId` since it's actually a
16 | [shiny module](https://mastering-shiny.org/scaling-modules.html).
17 | * `eDT()` adds extra (hidden) columns to your `datatable`. Try to format using
18 | column names instead of indexes.
19 | * Your `datatable` now exists within a module (e.g. child namespace). This means
20 | your own chosen `outputId` is now `moduleId-DT`. This influences for example
21 | the values accessible under `input`. Example: switch from
22 | `input$outputId_cell_clicked` to `input[["moduleId-DT_cell_clicked"]]`.
23 | * `eDT()` accepts all arguments of `DT::datatable()`, but has some different
24 | defaults for convenience.
25 | * Any additional formatting should be done by passing a function to the
26 | `format` argument of `eDT()`.
27 | * As always be careful when using
28 | [extensions](https://datatables.net/extensions/index) or custom javascript,
29 | not everything works well together. The
30 | [KeyTable](https://datatables.net/extensions/keytable/) and
31 | [AutoFill](https://datatables.net/extensions/autofill/) extensions of
32 | datatable are used by default and should be well integrated.
33 |
34 | Here is an example covering the above:
35 |
36 | ```{r echo = TRUE, results = 'hide'}
37 | library(shiny)
38 | library(DT)
39 | library(editbl)
40 | ```
41 |
42 | ```{r, screenshot.opts = list(vwidth = 700), screenshot.alt = 'screenshots/howto_switch_from_DT_1.png'}
43 | ui <- fluidPage(DTOutput("DT"))
44 | server <- function(input, output, session){
45 | output$DT <- renderDataTable({
46 | datatable(mtcars) %>%
47 | formatRound('disp', 1)
48 | })
49 | observe({
50 | print(input[["DT_cell_clicked"]])
51 | })
52 | }
53 | shinyApp(ui, server)
54 | ```
55 |
56 | Reworked into `eDT()`:
57 | ```{r, screenshot.opts = list(vwidth = 700), screenshot.alt = 'screenshots/howto_switch_from_DT_2.png'}
58 | ui <- fluidPage(eDTOutput("DT"))
59 | server <- function(input, output, session){
60 | editbl::eDT(
61 | id = "DT",
62 | data = mtcars,
63 | format = function(x){formatRound(x,'disp', 1)})
64 |
65 | observe({
66 | print(input[["DT-DT_cell_clicked"]])
67 | })
68 | }
69 | shinyApp(ui, server)
70 | ```
71 |
72 |
--------------------------------------------------------------------------------
/editbl/vignettes/screenshots/howto_relational_db_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openanalytics/editbl/603a4e2702f7183b4ce8447bc8b274f6c8f71942/editbl/vignettes/screenshots/howto_relational_db_1.png
--------------------------------------------------------------------------------
/editbl/vignettes/screenshots/howto_relational_db_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openanalytics/editbl/603a4e2702f7183b4ce8447bc8b274f6c8f71942/editbl/vignettes/screenshots/howto_relational_db_2.png
--------------------------------------------------------------------------------
/editbl/vignettes/screenshots/howto_relational_db_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openanalytics/editbl/603a4e2702f7183b4ce8447bc8b274f6c8f71942/editbl/vignettes/screenshots/howto_relational_db_3.png
--------------------------------------------------------------------------------
/editbl/vignettes/screenshots/howto_relational_db_dm_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openanalytics/editbl/603a4e2702f7183b4ce8447bc8b274f6c8f71942/editbl/vignettes/screenshots/howto_relational_db_dm_1.png
--------------------------------------------------------------------------------
/editbl/vignettes/screenshots/howto_row_level_access_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openanalytics/editbl/603a4e2702f7183b4ce8447bc8b274f6c8f71942/editbl/vignettes/screenshots/howto_row_level_access_1.png
--------------------------------------------------------------------------------
/editbl/vignettes/screenshots/howto_row_level_access_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openanalytics/editbl/603a4e2702f7183b4ce8447bc8b274f6c8f71942/editbl/vignettes/screenshots/howto_row_level_access_2.png
--------------------------------------------------------------------------------
/editbl/vignettes/screenshots/howto_switch_from_DT_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openanalytics/editbl/603a4e2702f7183b4ce8447bc8b274f6c8f71942/editbl/vignettes/screenshots/howto_switch_from_DT_1.png
--------------------------------------------------------------------------------
/editbl/vignettes/screenshots/howto_switch_from_DT_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openanalytics/editbl/603a4e2702f7183b4ce8447bc8b274f6c8f71942/editbl/vignettes/screenshots/howto_switch_from_DT_2.png
--------------------------------------------------------------------------------
/editbl_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openanalytics/editbl/603a4e2702f7183b4ce8447bc8b274f6c8f71942/editbl_logo.png
--------------------------------------------------------------------------------