diff --git a/DESCRIPTION b/DESCRIPTION index eac8f5d5a..826b50ce3 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Package: soilDB Type: Package Title: Soil Database Interface -Version: 2.6.0 -Date: 2020-12-02 +Version: 2.6.1 +Date: 2021-01-06 Authors@R: c(person(given="Dylan", family="Beaudette", role = c("aut"), email = "dylan.beaudette@usda.gov"), person(given="Jay", family="Skovlin", role = c("aut")), person(given="Stephen", family="Roecker", role = c("aut")), diff --git a/R/createStaticNASIS.R b/R/createStaticNASIS.R new file mode 100644 index 000000000..50152deb8 --- /dev/null +++ b/R/createStaticNASIS.R @@ -0,0 +1,120 @@ +#' Method for "dumping" contents of an entire NASIS table +#' +#' @param table_name Character name of table. +#' @param static_path Optional: path to SQLite database containing NASIS table structure; Default: \code{NULL} +#' +#' @return A data.frame or other result of \code{DBI::dbGetQuery} +#' +.dump_NASIS_table <- function(table_name, static_path = NULL) { + # connect to NASIS, identify columns + con <- dbConnectNASIS(static_path) + allcols <- "*" + + # handling for MSSQL/ODBC weirdness + if (is.null(static_path)) { + columns <- odbc::odbcConnectionColumns(con, table_name) + + # re-arrange VARCHAR(MAX) columns + longcols <- subset(columns, columns$field.type == "varchar" & columns$column_size == 0)$name + allcols <- columns$name + + if (length(longcols) > 0) { + allcols[which(allcols %in% longcols)] <- NA + allcols <- c(na.omit(allcols), longcols) + } + } + + # construct query and return result + q <- sprintf("SELECT %s FROM %s", paste(allcols, collapse = ", "), table_name) + return(dbQueryNASIS(con, q)) +} + +#' Create a memory or file-based instance of NASIS database (for selected tables) +#' +#' @param tables Character vector of target tables. Default: \code{NULL} is all tables meeting the following criteria. +#' @param SS Logical. Include "selected set" tables (ending with suffix \code{"_View1"}). Default: \code{FALSE} +#' @param systables Logical. Include "system" tables (starting with prefix \code{"system"}). Default: \code{FALSE} +#' @param static_path Optional: path to SQLite database containing NASIS table structure; Default: \code{NULL} +#' @param output_path Optional: path to new/existing SQLite database to write tables to. Default: \code{NULL} returns table results as named list. +#' @return A named list of results from calling \code{dbQueryNASIS} for all columns in each NASIS table. +#' @export +#' +#' @importFrom odbc dbListTables +#' @importFrom RSQLite dbListTables RSQLite +#' @importFrom DBI dbConnect dbDisconnect dbWriteTable +#' +#' @examples +#' +#' \dontrun{ +#' str(createStaticNASIS(tables = c("calculation","formtext"))) +#' } +#' +createStaticNASIS <- function(tables = NULL, SS = FALSE, systables = FALSE, + static_path = NULL, output_path = NULL) { + # can make static DB from another static DB, or default is local NASIS install (static_path=NULL) + con <- dbConnectNASIS(static_path = static_path) + + nasis_table_names <- NULL + + # explicit handling of the connection types currently allowed + if (inherits(con, 'OdbcConnection')) nasis_table_names <- odbc::dbListTables(con) + + # you can read/write from SQLite with this method just as well as ODBC + if (inherits(con, 'SQLiteConnection')) nasis_table_names <- RSQLite::dbListTables(con) + + # must know names of tables in data source + stopifnot(!is.null(nasis_table_names)) + + # never pull the system table + if (!systables) { + systables <- grep("^system", nasis_table_names) + + if (length(systables) > 0) { + nasis_table_names <- nasis_table_names[-systables] + } + } + + # keep only explicitly listed tables, if any + if (!is.null(tables) & length(tables) > 0 & is.character(tables)) { + nasis_table_names <- nasis_table_names[nasis_table_names %in% tables] + } + + # remove selected set tables + if (!SS) { + sstables <- grep("_View1$", nasis_table_names) + nasis_table_names <- nasis_table_names[!nasis_table_names %in% sstables] + } + + # return list result if no output path + if (is.null(output_path)) { + + # return named list of data.frames or try-error (one per table) + res <- lapply(nasis_table_names, function(n) try(.dump_NASIS_table, static_path)) + names(res) <- nasis_table_names + return(res) + + # otherwise, we are writing SQLite to output_path + } else { + + # TODO: validation of output_path? + + # create sqlite db + outcon <- DBI::dbConnect(RSQLite::SQLite(), output_path) + + # returns TRUE, invisibly, or try-error (one per table) + return(lapply(nasis_table_names, function(n) { + return(try({ + DBI::dbWriteTable(conn = outcon, name = n, + value = .dump_NASIS_table(n, + static_path = static_path), + overwrite = TRUE) + })) + })) + + # close output connection + DBI::dbDisconnect(outcon) + } + + # close input connection + DBI::dbDisconnect(con) +} diff --git a/R/fetchNASISLabData.R b/R/fetchNASISLabData.R index f14da80e7..e1c5540f1 100644 --- a/R/fetchNASISLabData.R +++ b/R/fetchNASISLabData.R @@ -2,7 +2,7 @@ fetchNASISLabData <- function(SS = TRUE) { # test connection - if (!local_NASIS_defined()) + if (!local_NASIS_defined(static_path)) stop('Local NASIS ODBC connection has not been setup. Please see the `setup_ODBC_local_NASIS.pdf` document included with this package.') # 1. load data in pieces, results are DF objects diff --git a/R/fetchNASIS_pedons.R b/R/fetchNASIS_pedons.R index bb08fa15e..5e6098a6c 100644 --- a/R/fetchNASIS_pedons.R +++ b/R/fetchNASIS_pedons.R @@ -7,7 +7,7 @@ static_path = NULL) { # test connection - if (!local_NASIS_defined()) + if (!local_NASIS_defined(static_path)) stop('Local NASIS ODBC connection has not been setup. Please see `http://ncss-tech.github.io/AQP/soilDB/setup_local_nasis.html`.') # sanity check diff --git a/R/fetchVegdata.R b/R/fetchVegdata.R index 780da7f6b..8d6611697 100644 --- a/R/fetchVegdata.R +++ b/R/fetchVegdata.R @@ -4,7 +4,7 @@ fetchVegdata <- function(SS=TRUE, stringsAsFactors = default.stringsAsFactors()) { # test connection - if (!local_NASIS_defined()) + if (!local_NASIS_defined(static_path)) stop('Local NASIS ODBC connection has not been setup. Please see `http://ncss-tech.github.io/AQP/soilDB/setup_local_nasis.html`.') # 1. load data in pieces diff --git a/R/getHzErrorsNASIS.R b/R/getHzErrorsNASIS.R index 009bbc696..5bd0fa5bd 100644 --- a/R/getHzErrorsNASIS.R +++ b/R/getHzErrorsNASIS.R @@ -1,19 +1,20 @@ #' Check pedon horizon table for logic errors #' #' @param strict how strict should horizon boundaries be checked for consistency: TRUE=more | FALSE=less -#' +#' @param SS fetch data from the currently loaded selected set in NASIS or from the entire local database (default: TRUE) +#' @param static_path Optional: path to local SQLite database containing NASIS table structure; default: NULL #' @return A data.frame containing problematic records with columns: 'peiid','pedon_id','hzdept','hzdepb','hzname' #' @export #' -getHzErrorsNASIS <- function(strict = TRUE) { +getHzErrorsNASIS <- function(strict = TRUE, SS = TRUE, static_path = NULL) { - if (!local_NASIS_defined()) + if (!local_NASIS_defined(static_path)) stop('Local NASIS ODBC connection has not been setup. Please see `http://ncss-tech.github.io/AQP/soilDB/setup_local_nasis.html`.') # get data - site_data <- get_site_data_from_NASIS_db() + site_data <- get_site_data_from_NASIS_db(SS = SS, static_path = static_path) site_data$pedon_id <- NULL - hz_data <- get_hz_data_from_NASIS_db() + hz_data <- get_hz_data_from_NASIS_db(SS = SS, static_path = static_path) if (nrow(site_data) == 0) { message("No Site records in NASIS database") diff --git a/R/get_cosoilmoist_from_NASIS.R b/R/get_cosoilmoist_from_NASIS.R index ac34711b0..127b471cd 100644 --- a/R/get_cosoilmoist_from_NASIS.R +++ b/R/get_cosoilmoist_from_NASIS.R @@ -1,4 +1,4 @@ -get_cosoilmoist_from_NASIS <- function(impute = TRUE, stringsAsFactors = default.stringsAsFactors(), static_path = NULL) { +get_cosoilmoist_from_NASIS <- function(SS = TRUE, impute = TRUE, stringsAsFactors = default.stringsAsFactors(), static_path = NULL) { q.cosoilmoist <- "SELECT dmuiidref AS dmuiid, coiid, compname, comppct_r, drainagecl, month, flodfreqcl, floddurcl, pondfreqcl, ponddurcl, cosoilmoistiid, soimoistdept_l, soimoistdept_r, soimoistdept_h, soimoistdepb_l, soimoistdepb_r, soimoistdepb_h, soimoiststat @@ -14,6 +14,11 @@ get_cosoilmoist_from_NASIS <- function(impute = TRUE, stringsAsFactors = default if (inherits(channel, 'try-error')) return(data.frame()) + # toggle selected set vs. local DB + if (SS == FALSE) { + q.cosoilmoist <- gsub(pattern = '_View_1', replacement = '', x = q.cosoilmoist, fixed = TRUE) + } + # exec query d.cosoilmoist <- dbQueryNASIS(channel, q.cosoilmoist) diff --git a/R/get_vegplot_data_from_NASIS_db.R b/R/get_vegplot_data_from_NASIS_db.R index ff1e7d78e..f7e19f6b7 100644 --- a/R/get_vegplot_data_from_NASIS_db.R +++ b/R/get_vegplot_data_from_NASIS_db.R @@ -3,8 +3,8 @@ get_vegplot_from_NASIS_db <- function(SS=TRUE, stringsAsFactors = default.stringsAsFactors(), static_path = NULL) { # must have stringr installed - if(!requireNamespace('stringr')) - stop('please install the `stringr` package', call.=FALSE) + if (!requireNamespace('stringr')) + stop('please install the `stringr` package', call. = FALSE) q.vegplot <- "SELECT siteiid, p.peiid, usiteid as site_id, assocuserpedonid as pedon_id, v.vegplotid as vegplot_id, vegplotiid, vegplotname, obsdate, primarydatacollector, datacollectionpurpose, vegdataorigin, vegplotsize, soilprofileindicator, soil232idlegacy, ahorizondepth, alkalinesalineindicator, alkalineaffected, salinityclass, restrictivelayerdepthlegacy, legacysoilcompname, legacysoilphase, legacylocalsoilphase, legacysoilsurftext, legacysurftextmod, legacyterminlieu, erosionclasslegacy, landformgrouplegacy, cryptogamcovcllegacy, rangelandusehistory, cancovpctplotave, cancovtotalpct, cancovtotalclass, overstorycancontotalpct, overstorycancovtotalclass, dblsampannualprodave, compyieldproductionave, abovegroundbiomasstotave, understoryreprodabundance, woodyunderstoryabundance, herbundertoryabundance, lichensunderstoryabundance, crowncanclosurepct, crowncancloseassessmethod, crowncompfactorlpp, crowncomplppavedbh, basalcoverpctave, basalareaplottotal, basalareaassessmethod, constreeshrubgrp, windbreakrowonedirection, windbreaktrappedsoildepth, windbreaktrappedsoiltexture, understorydescindicator, mensurationdataindicator, vigorclasslegacy, siteconditionlegacy, overstoryspecieslegacy, plantmoiststate, currenttreedensity, currenttreespacing, currentdxspacing, currentplotavedbh, plotbasalareafactor, currentbasalarea, foreststandtype, foreststratainventoried, foreststandregen, foreststandquality, desiredtreedensity, desireddxspacing, desiredbasalarea, excessbasalarea, excesstreedensity, stockingchangepct, treepctgoodcondition, treepctfaircondition, treepctpoorcondition, treecounttotal, treesnagdensityhard, treesnagdensitysoft, pastureforagetype, pasturestanddensityave, pastureplanthtave, pastureprodave, pcidesirableplants, pciplantcover, pciplantdiversity, pcigroundcovresidue, pcistandingdeadforage, pciplantresiduecompscore, pciplantvigor, pcilegumepctclass, pciuseuniformity, pcilivestockconcareas, pcisoilcompaction, pcisheetrillerosion, pciwinderosion, pcistreamshoreerosion, pcigullyerosion, pcierosioncompscore, pcipastureconditionscore, refplantcommunity, repannualprod, totestannualprod, totallowableannualprod, totpalatableannualprod, similarityindex, annualuseableprod, harvesteffpct, takehalfleavehalf, acresperaum, aumperacre, audperacre, desirableplantvigor, desirableseedlingabundance, decadentplantabundance, plantresidueadequacy, undesirableinvadingspecies, majorinvadingspecies, invadingspeciescancovpct, soilsurferosion, soilcrusting, soilcompaction, baregroundpct, gullyrillpresence, soildegradationrating, rangetrendcurrent, rangetrendplanned, qcreviewperson, qcreviewdate, qareviewperson, qareviewdate, swcdlegacy, fieldofficelegacy, nrcsarealegacy, aktotallichencoverpct, aktotallitter1coverpct, aktotallitter2coverpct, aktotalmosscoverpct, aktotalrockcoverpct, aktotalsoilcoverpct, aktotalwatercoverpct, akecologicalsitestatus, aktotalbedrockcoverpct, akfieldecositeid FROM @@ -15,7 +15,7 @@ get_vegplot_from_NASIS_db <- function(SS=TRUE, stringsAsFactors = default.string ORDER BY s.siteiid;" channel <- dbConnectNASIS(static_path) - + if (inherits(channel, 'try-error')) return(data.frame()) @@ -24,17 +24,15 @@ get_vegplot_from_NASIS_db <- function(SS=TRUE, stringsAsFactors = default.string # toggle selected set vs. local DB if (SS == FALSE) { - q <- gsub(pattern = '_View_1', replacement = '', x = q, fixed = TRUE) + q.vegplot <- gsub(pattern = '_View_1', replacement = '', x = q.vegplot, fixed = TRUE) } - d <- uncode(d.vegplot) - # test for no data - if (nrow(d) == 0) + if (nrow(d.vegplot) == 0) stop('there are no NASIS vegplots in your selected set!') # uncode metadata domains - d <- uncode(d, stringsAsFactors = stringsAsFactors) + d <- uncode(d.vegplot, stringsAsFactors = stringsAsFactors) # done return(d) @@ -57,23 +55,21 @@ get_vegplot_location_from_NASIS_db <- function(SS=TRUE, stringsAsFactors = defau if (inherits(channel, 'try-error')) return(data.frame()) - # exec query - d.plotlocation <- dbQueryNASIS(channel, q.plotlocation, stringsAsFactors=FALSE) - # toggle selected set vs. local DB if (SS == FALSE) { - q <- gsub(pattern = '_View_1', replacement = '', x = q, fixed = TRUE) + q.plotlocation <- gsub(pattern = '_View_1', replacement = '', x = q.plotlocation, fixed = TRUE) } + + # exec query + d.plotlocation <- dbQueryNASIS(channel, q.plotlocation, stringsAsFactors = FALSE) - d <- uncode(d.plotlocation) - + # uncode metadata domains + d <- uncode(d.plotlocation, stringsAsFactors = stringsAsFactors) + # test for no data if (nrow(d) == 0) stop('there are no NASIS vegplots in your selected set!') - # uncode metadata domains - d <- uncode(d, stringsAsFactors = stringsAsFactors) - # hack for CRAN check state_FIPS_codes <- NULL @@ -117,23 +113,22 @@ get_vegplot_trhi_from_NASIS_db <- function(SS=TRUE, stringsAsFactors = default.s if (inherits(channel, 'try-error')) return(data.frame()) - # exec query - d.vegplotrhi <- dbQueryNASIS(channel, q.vegplotrhi) - # toggle selected set vs. local DB if (SS == FALSE) { - q <- gsub(pattern = '_View_1', replacement = '', x = q, fixed = TRUE) + q.vegplotrhi <- gsub(pattern = '_View_1', replacement = '', x = q.vegplotrhi, fixed = TRUE) } + + # exec query + d.vegplotrhi <- dbQueryNASIS(channel, q.vegplotrhi) - d <- uncode(d.vegplotrhi) + # uncode metadata domains + d <- uncode(d.vegplotrhi, stringsAsFactors = stringsAsFactors) # test for no data - if (nrow(d) == 0) + if (nrow(d) == 0) { stop('there are no NASIS vegplots in your selected set!') - - # uncode metadata domains - d <- uncode(d, stringsAsFactors = stringsAsFactors) - + } + # done return(d) } @@ -156,22 +151,21 @@ get_vegplot_species_from_NASIS_db <- function(SS=TRUE, stringsAsFactors = defaul if (inherits(channel, 'try-error')) return(data.frame()) - # exec query - d.vegplotspecies <- dbQueryNASIS(channel, q.vegplotspecies) - # toggle selected set vs. local DB if (SS == FALSE) { - q <- gsub(pattern = '_View_1', replacement = '', x = q, fixed = TRUE) + q.vegplotspecies <- gsub(pattern = '_View_1', replacement = '', x = q.vegplotspecies, fixed = TRUE) } - - d <- uncode(d.vegplotspecies) - - # test for no data - if(nrow(d) == 0) - stop('there are no NASIS vegplots in your selected set!') + + # exec query + d.vegplotspecies <- dbQueryNASIS(channel, q.vegplotspecies) # uncode metadata domains - d <- uncode(d, stringsAsFactors = stringsAsFactors) + d <- uncode(d.vegplotspecies, stringsAsFactors = stringsAsFactors) + + # test for no data + if (nrow(d) == 0) { + stop('there are no NASIS vegplots in your selected set!', call. = FALSE) + } # done return(d) @@ -195,23 +189,22 @@ get_vegplot_transect_from_NASIS_db <- function(SS=TRUE, stringsAsFactors = defau if (inherits(channel, 'try-error')) return(data.frame()) - - # exec query - d.vegtransect <- dbQueryNASIS(channel, q.vegtransect) - + # toggle selected set vs. local DB if (SS == FALSE) { - q <- gsub(pattern = '_View_1', replacement = '', x = q, fixed = TRUE) + q.vegtransect <- gsub(pattern = '_View_1', replacement = '', x = q.vegtransect, fixed = TRUE) } - d <- uncode(d.vegtransect) + # exec query + d.vegtransect <- dbQueryNASIS(channel, q.vegtransect) # test for no data - if (nrow(d) == 0) - stop('there are no NASIS vegplots transects in your selected set!') - + if (nrow(d.vegtransect) == 0) { + stop('there are no NASIS vegplots transects in your selected set!', call. = FALSE) + } + # uncode metadata domains - d <- uncode(d, stringsAsFactors = stringsAsFactors) + d <- uncode(d.vegtransect, stringsAsFactors = stringsAsFactors) # done return(d) @@ -236,23 +229,21 @@ get_vegplot_transpecies_from_NASIS_db <- function(SS=TRUE, stringsAsFactors = de if (inherits(channel, 'try-error')) return(data.frame()) - - # exec query - d.vegtransplantsum <- dbQueryNASIS(channel, q.vtps) - + # toggle selected set vs. local DB if (SS == FALSE) { - q <- gsub(pattern = '_View_1', replacement = '', x = q, fixed = TRUE) + q.vtps <- gsub(pattern = '_View_1', replacement = '', x = q.vtps, fixed = TRUE) } - - d <- uncode(d.vegtransplantsum) + + # exec query + d.vegtransplantsum <- dbQueryNASIS(channel, q.vtps) # test for no data - if (nrow(d) == 0) + if (nrow(d.vegtransplantsum) == 0) stop('there are no NASIS vegplots transect species in your selected set!') # uncode metadata domains - d <- uncode(d, stringsAsFactors = stringsAsFactors) + d <- uncode(d.vegtransplantsum, stringsAsFactors = stringsAsFactors) # done return(d) @@ -262,7 +253,7 @@ get_vegplot_transpecies_from_NASIS_db <- function(SS=TRUE, stringsAsFactors = de get_vegplot_tree_si_summary_from_NASIS_db <- function(SS=TRUE, stringsAsFactors = default.stringsAsFactors(), static_path = NULL) { # plot tree site index summary data - q.pltsis<- "SELECT vegplotiidref AS vegplotiid, pltsis.seqnum, plantiidref, plantsym, plantsciname, plantnatvernm, plantnativity, siteindexbase, speciestreecount, siteindexplotave, speciesdbhaverage, treeageave, treecanopyhttopave, plottreesiteindsumiid + q.pltsis <- "SELECT vegplotiidref AS vegplotiid, pltsis.seqnum, plantiidref, plantsym, plantsciname, plantnatvernm, plantnativity, siteindexbase, speciestreecount, siteindexplotave, speciesdbhaverage, treeageave, treecanopyhttopave, plottreesiteindsumiid FROM site_View_1 AS s @@ -276,20 +267,20 @@ get_vegplot_tree_si_summary_from_NASIS_db <- function(SS=TRUE, stringsAsFactors if (inherits(channel, 'try-error')) return(data.frame()) - - # exec query - d.vegsiteindexsum <- dbQueryNASIS(channel, q.pltsis) - + # toggle selected set vs. local DB if (SS == FALSE) { - q <- gsub(pattern = '_View_1', replacement = '', x = q, fixed = TRUE) + q.pltsis <- gsub(pattern = '_View_1', replacement = '', x = q.pltsis, fixed = TRUE) } + + # exec query + d.vegsiteindexsum <- dbQueryNASIS(channel, q.pltsis) d <- uncode(d.vegsiteindexsum) # test for no data if (nrow(d) == 0) - stop('there are no NASIS vegplots tree site index data in your selected set!') + stop('there are no NASIS vegplots tree site index data in your selected set!', call. = FALSE) # uncode metadata domains d <- uncode(d, stringsAsFactors = stringsAsFactors) @@ -320,20 +311,20 @@ get_vegplot_tree_si_details_from_NASIS_db <- function(SS=TRUE, stringsAsFactors if (inherits(channel, 'try-error')) return(data.frame()) - # exec query - d.vegsiteindexdet <- dbQueryNASIS(channel, q.pltsid) - # toggle selected set vs. local DB if (SS == FALSE) { - q <- gsub(pattern = '_View_1', replacement = '', x = q, fixed = TRUE) + q.pltsid <- gsub(pattern = '_View_1', replacement = '', x = q.pltsid, fixed = TRUE) } + + # exec query + d.vegsiteindexdet <- dbQueryNASIS(channel, q.pltsid) d <- uncode(d.vegsiteindexdet) # test for no data - if(nrow(d) == 0) - stop('there are no NASIS vegplots tree site index data in your selected set!') - + if (nrow(d) == 0) { + stop('there are no NASIS vegplots tree site index data in your selected set!', call. = FALSE) + } # uncode metadata domains d <- uncode(d, stringsAsFactors = stringsAsFactors) @@ -355,22 +346,20 @@ FROM vegplottext_View_1;" if (inherits(channel, 'try-error')) return(data.frame()) - # exec query - d.vegplottext <- dbQueryNASIS(channel, q.vegplottext) - # toggle selected set vs. local DB if (SS == FALSE) { - q <- gsub(pattern = '_View_1', replacement = '', x = q, fixed = TRUE) + q.vegplottext <- gsub(pattern = '_View_1', replacement = '', x = q.vegplottext, fixed = TRUE) } - - d <- uncode(d.vegplottext) + + # exec query + d.vegplottext <- dbQueryNASIS(channel, q.vegplottext) # test for no data - if (nrow(d) == 0) - stop('there are no NASIS vegplots textnotes in your selected set!') + if (nrow(d.vegplottext) == 0) + stop('there are no NASIS vegplots textnotes in your selected set!', call. = FALSE) # uncode metadata domains - d <- uncode(d, stringsAsFactors = stringsAsFactors) + d <- uncode(d.vegplottext, stringsAsFactors = stringsAsFactors) # optionally convert \r\n -> \n if (fixLineEndings) { diff --git a/R/openNASISchannel.R b/R/openNASISchannel.R index e3f04f698..ce459cb42 100644 --- a/R/openNASISchannel.R +++ b/R/openNASISchannel.R @@ -3,7 +3,7 @@ .openNASISchannel <- function(static_path = NULL) { use_sqlite <- !is.null(static_path) - + if (is.null(getOption('soilDB.NASIS.credentials'))) stop("soilDB.NASIS.credentials not set") @@ -31,6 +31,8 @@ #' Check for presence of `nasis_local` ODBC data source #' +#' @param static_path Optional: path to local SQLite database containing NASIS table structure; default: NULL +#' #' @return logical #' @export local_NASIS_defined #' @@ -42,11 +44,15 @@ #' message('could not find `nasis_local` ODBC data source') #' } #' @importFrom odbc odbcListDataSources -#' -local_NASIS_defined <- function() { - if ('nasis_local' %in% odbc::odbcListDataSources()$name) { - return(TRUE) +#' @importFrom RSQLite dbCanConnect SQLite +local_NASIS_defined <- function(static_path = NULL) { + if (is.null(static_path)) { + if ('nasis_local' %in% odbc::odbcListDataSources()$name) { + return(TRUE) + } else { + return(FALSE) + } } else { - return(FALSE) + return(RSQLite::dbCanConnect(RSQLite::SQLite(), static_path)) } } diff --git a/man/createStaticNASIS.Rd b/man/createStaticNASIS.Rd new file mode 100644 index 000000000..f1c95933e --- /dev/null +++ b/man/createStaticNASIS.Rd @@ -0,0 +1,38 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/createStaticNASIS.R +\name{createStaticNASIS} +\alias{createStaticNASIS} +\title{Create a memory or file-based instance of NASIS database (for selected tables)} +\usage{ +createStaticNASIS( + tables = NULL, + SS = FALSE, + systables = FALSE, + static_path = NULL, + output_path = NULL +) +} +\arguments{ +\item{tables}{Character vector of target tables. Default: \code{NULL} is all tables meeting the following criteria.} + +\item{SS}{Logical. Include "selected set" tables (ending with suffix \code{"_View1"}). Default: \code{FALSE}} + +\item{systables}{Logical. Include "system" tables (starting with prefix \code{"system"}). Default: \code{FALSE}} + +\item{static_path}{Optional: path to SQLite database containing NASIS table structure; Default: \code{NULL}} + +\item{output_path}{Optional: path to new/existing SQLite database to write tables to. Default: \code{NULL} returns table results as named list.} +} +\value{ +A named list of results from calling \code{dbQueryNASIS} for all columns in each NASIS table. +} +\description{ +Create a memory or file-based instance of NASIS database (for selected tables) +} +\examples{ + +\dontrun{ + str(createStaticNASIS(tables = c("calculation","formtext"))) +} + +} diff --git a/man/dot-dump_NASIS_table.Rd b/man/dot-dump_NASIS_table.Rd new file mode 100644 index 000000000..eac488705 --- /dev/null +++ b/man/dot-dump_NASIS_table.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/createStaticNASIS.R +\name{.dump_NASIS_table} +\alias{.dump_NASIS_table} +\title{Method for "dumping" contents of an entire NASIS table} +\usage{ +.dump_NASIS_table(table_name, static_path = NULL) +} +\arguments{ +\item{table_name}{Character name of table.} + +\item{static_path}{Optional: path to SQLite database containing NASIS table structure; Default: \code{NULL}} +} +\value{ +A data.frame or other result of \code{DBI::dbGetQuery} +} +\description{ +Method for "dumping" contents of an entire NASIS table +} diff --git a/man/getHzErrorsNASIS.Rd b/man/getHzErrorsNASIS.Rd index 9dd1d7440..f85671595 100644 --- a/man/getHzErrorsNASIS.Rd +++ b/man/getHzErrorsNASIS.Rd @@ -4,10 +4,14 @@ \alias{getHzErrorsNASIS} \title{Check pedon horizon table for logic errors} \usage{ -getHzErrorsNASIS(strict = TRUE) +getHzErrorsNASIS(strict = TRUE, SS = TRUE, static_path = NULL) } \arguments{ \item{strict}{how strict should horizon boundaries be checked for consistency: TRUE=more | FALSE=less} + +\item{SS}{fetch data from the currently loaded selected set in NASIS or from the entire local database (default: TRUE)} + +\item{static_path}{Optional: path to local SQLite database containing NASIS table structure; default: NULL} } \value{ A data.frame containing problematic records with columns: 'peiid','pedon_id','hzdept','hzdepb','hzname' diff --git a/man/local_NASIS_defined.Rd b/man/local_NASIS_defined.Rd index d6ac9a264..cf065f9e4 100644 --- a/man/local_NASIS_defined.Rd +++ b/man/local_NASIS_defined.Rd @@ -4,7 +4,10 @@ \alias{local_NASIS_defined} \title{Check for presence of `nasis_local` ODBC data source} \usage{ -local_NASIS_defined() +local_NASIS_defined(static_path = NULL) +} +\arguments{ +\item{static_path}{Optional: path to local SQLite database containing NASIS table structure; default: NULL} } \value{ logical diff --git a/misc/validate-NASIS-results.R b/misc/validate-NASIS-results.R index 1c255ca99..db31ca5a0 100644 --- a/misc/validate-NASIS-results.R +++ b/misc/validate-NASIS-results.R @@ -36,6 +36,9 @@ f <- read.table(text = "R/fetchNASIS.R library(soilDB) +# selected SET? +selected_set <- FALSE + fnames <- sapply(f, function(x) { spv <- evalSource(x, package = "soilDB") # source(x) @@ -45,7 +48,9 @@ fnames <- sapply(f, function(x) { test <- lapply(fnames, function(fname) { lapply(fname, function(FUN) { message(sprintf("Testing: %s", FUN)) - try(get(FUN, envir = as.environment("package:soilDB"))()) + if (FUN == "local_NASIS_defined") try(get(FUN, envir = as.environment("package:soilDB"))(static_path = "testStatic.sqlite")) + else try(get(FUN, envir = as.environment("package:soilDB"))(SS = selected_set, static_path = "testStatic.sqlite")) + # try(get(FUN, envir = as.environment("package:soilDB"))()) }) }) @@ -57,6 +62,8 @@ res <- unlist(lapply(names(test), function(x) lapply(seq_along(test[[x]]), funct res[which(res)] +# get_vegplot_tree_si_details_from_NASIS_db(SS = FALSE, static_path = "testStatic.sqlite") + # Fixed: Text fields must come at end of query per MSSQL specs # get_text_notes_from_NASIS_db() diff --git a/tests/testthat/test-fetchNASIS.R b/tests/testthat/test-fetchNASIS.R index 7a1ade98c..983dad83d 100644 --- a/tests/testthat/test-fetchNASIS.R +++ b/tests/testthat/test-fetchNASIS.R @@ -1,5 +1,8 @@ context("fetchNASIS() -- requires local NASIS and ODBC connection") +# TODO: develop minimal test set for NASIS data, stored as static SQLite DB +static_path <- NULL + ## helper functions used to skip tests that rely on special conditions # http://r-pkgs.had.co.nz/tests.html # @@ -7,36 +10,36 @@ context("fetchNASIS() -- requires local NASIS and ODBC connection") # * pedons / component missing from local database -check_local_NASIS_pedons_available <- function() { +check_local_NASIS_pedons_available <- function(static_path = NULL) { # attempt to load pedons # these functions will return empty data.frame objects when there are no data in the SS - res1 <- try(suppressWarnings(get_site_data_from_NASIS_db()), silent = TRUE) - res2 <- try(suppressWarnings(get_hz_data_from_NASIS_db()), silent = TRUE) + res1 <- try(suppressWarnings(get_site_data_from_NASIS_db(static_path = static_path)), silent = TRUE) + res2 <- try(suppressWarnings(get_hz_data_from_NASIS_db(static_path = static_path)), silent = TRUE) - if(nrow(res1) == 0) { + if (nrow(res1) == 0) { skip("no Site/Pedon records in local NASIS database") } - if(nrow(res2) == 0) { + if (nrow(res2) == 0) { skip("no Pedon Horizon records in local NASIS database") } } -check_local_NASIS_components_available <- function() { +check_local_NASIS_components_available <- function(static_path = NULL) { # attempt to load components # these functions will return empty data.frame objects when there are no data in the SS - res1 <- try(suppressWarnings(get_component_data_from_NASIS_db()), silent = TRUE) - res2 <- try(suppressWarnings(get_component_horizon_data_from_NASIS_db()), silent = TRUE) + res1 <- try(suppressWarnings(get_component_data_from_NASIS_db(static_path = static_path)), silent = TRUE) + res2 <- try(suppressWarnings(get_component_horizon_data_from_NASIS_db(static_path = static_path)), silent = TRUE) # res <- try(suppressWarnings(fetchNASIS(from='pedons')), silent = TRUE) # note: this was too broad of a test -- any error in fetchNASIS will result in skipping the test! #if(class(res) == 'try-error'){ - if(nrow(res1) == 0) { + if (nrow(res1) == 0) { skip("no Component records in local NASIS database") } - if(nrow(res2) == 0) { + if (nrow(res2) == 0) { skip("no Component Horizon records in local NASIS database") } } @@ -47,17 +50,17 @@ check_local_NASIS_components_available <- function() { test_that("fetchNASIS(from='pedons') returns reasonable data", { # test for conditions permitting this test to run - if(!local_NASIS_defined()) { + if (!local_NASIS_defined(static_path = static_path)) { skip("local NASIS database not available") } # pedons must be present for tests - check_local_NASIS_pedons_available() + check_local_NASIS_pedons_available(static_path = static_path) # get data # ignore warnings for now - x <- suppressWarnings(fetchNASIS(from='pedons')) - + x <- suppressWarnings(fetchNASIS(from = 'pedons')) + # expected outcomes expect_true(inherits(x, 'SoilProfileCollection')) expect_equal(nrow(site(x)) > 0, TRUE) @@ -75,38 +78,38 @@ test_that("fetchNASIS(from='pedons') returns reasonable data", { test_that("fetchNASIS(from='pedons') nullFragsAreZero works as expected", { # test for conditions permitting this test to run - if(!local_NASIS_defined()) { + if (!local_NASIS_defined(static_path = static_path)) { skip("local NASIS database not available") } # components must be present for tests - check_local_NASIS_pedons_available() + check_local_NASIS_pedons_available(static_path = static_path) # get data # ignore warnings for now - x <- suppressWarnings(fetchNASIS(from='pedons')) - y <- suppressWarnings(fetchNASIS(from='pedons', nullFragsAreZero=FALSE)) + x <- suppressWarnings(fetchNASIS(from = 'pedons')) + y <- suppressWarnings(fetchNASIS(from = 'pedons', nullFragsAreZero = FALSE)) # no NA in total fragments using default arguments - expect_true(all(horizons(x)[is.na(y$total_frags_pct),'total_frags_pct'] ==0)) - expect_true(all(horizons(x)[is.na(y$total_art_pct),'total_art_pct'] ==0)) + expect_true(all(horizons(x)[is.na(y$total_frags_pct),'total_frags_pct'] == 0)) + expect_true(all(horizons(x)[is.na(y$total_art_pct),'total_art_pct'] == 0)) }) test_that("fetchNASIS(from='components') returns reasonable data", { # hack for in-house testing only # WWW services aren't always available and will cause CRAN to drop our package if tests fail - if(!local_NASIS_defined()) { - skip("in-house testing only") + if (!local_NASIS_defined(static_path = static_path)) { + skip("local NASIS database not available") } # must have components to complete test - check_local_NASIS_components_available() + check_local_NASIS_components_available(static_path = static_path) # get data # ignore warnings for now - x <- suppressWarnings(fetchNASIS(from='components')) - + x <- suppressWarnings(fetchNASIS(from = 'components')) + # expected outcomes expect_true(inherits(x, 'SoilProfileCollection')) expect_equal(nrow(site(x)) > 0, TRUE) @@ -117,17 +120,17 @@ test_that("fetchNASIS(from='components') returns reasonable data", { }) test_that("get_text_notes_from_NASIS_db works", { - if (!local_NASIS_defined()) { - skip("in-house testing only") + if (!local_NASIS_defined(static_path = static_path)) { + skip("local NASIS database not available") } expect_silent({get_text_notes_from_NASIS_db()}) }) test_that("getHzErrorsNASIS works", { - if (!local_NASIS_defined()) { - skip("in-house testing only") + if (!local_NASIS_defined(static_path = static_path)) { + skip("local NASIS database not available") } - expect_silent({suppressMessages(getHzErrorsNASIS())}) + expect_silent({suppressMessages(getHzErrorsNASIS(static_path = static_path))}) })