Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

0.1.0 Release #21

Merged
merged 18 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: datasetjson
Type: Package
Title: Read and Write CDISC Dataset JSON Files
Version: 0.0.1
Version: 0.1.0
Authors@R: c(
person(given = "Mike",
family = "Stackhouse",
Expand All @@ -24,8 +24,8 @@ Roxygen: list(markdown = TRUE)
RoxygenNote: 7.2.3
Depends: R (>= 3.5)
Imports:
jsonlite (>= 1.8.7),
jsonvalidate (>= 1.3.2)
jsonlite (>= 1.8.0),
jsonvalidate (>= 1.3.1)
Suggests:
testthat (>= 2.1.0),
knitr,
Expand Down
13 changes: 11 additions & 2 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# datasetjson 0.0.0.9999
# datasetjson 0.1.0

- Capability to read and validate Dataset JSON files from URLs has been added (#8)
- Remove autoset of fileOID using output path (#3)
- Don't auto-populate optional attributes with NA (#16)
- Push dependency versions back (#18)
- Default `pretty` parameter on `write_dataset_json()` to false (#20)

# datasetjson 0.0.1

Initial development version of datasetjson, introducing core objects, readers and writers.

Intial development version of datasetjson, introducing core objects, readers and writers.
2 changes: 1 addition & 1 deletion R/data_metadata.R
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
#' data_meta_updated <- set_metadata_version(data_meta_updated, "MDV.MSGv2.0.SDTMIG.3.3.SDTM.1.7")
#' data_meta_updated <- set_study_oid(data_meta_updated, "SOMESTUDY")
#'
data_metadata <- function(study = "NA", metadata_version = "NA", metadata_ref = "NA") {
data_metadata <- function(study = NULL, metadata_version = NULL, metadata_ref = NULL) {

x <- list(
studyOID = study,
Expand Down
26 changes: 19 additions & 7 deletions R/file_metadata.R
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#' file."
#' @param sys_version sourceSystemVersion, defined as "The version of the
#' sourceSystem"
#' @param file_oid fileOID parameter, defined as "A unique identifier for this
#' file."
#' @param version Dataset JSON schema version being used
#'
#' @return file_metadata object
Expand All @@ -26,17 +28,17 @@
#' file_meta_updated <- set_file_oid(file_meta, "/some/path")
#' file_meta_updated <- set_originator(file_meta_updated, "Some Org")
#' file_meta_updated <- set_source_system(file_meta_updated, "source system", "1.0")
file_metadata <- function(originator="NA", sys = "NA", sys_version = "NA", version = "1.0.0") {
file_metadata <- function(originator=NULL, sys = NULL, sys_version = NULL, file_oid = NULL, version = "1.0.0") {

if (!(version %in% c("1.0.0"))) {
stop("Unsupported version specified - currently only version 1.0.0 is supported", call.=FALSE)
}

x <- list(
"creationDateTime"= get_datetime(),
"creationDateTime"= character(),
"datasetJSONVersion"= version,
"fileOID" = character(),
"asOfDateTime" = character(),
"fileOID" = file_oid,
"asOfDateTime" = NULL, # Not sure we want this to exist?
"originator" = originator,
"sourceSystem" = sys,
"sourceSystemVersion" = sys_version
Expand All @@ -59,10 +61,20 @@ get_datetime <- function() {
format(Sys.time(), "%Y-%m-%dT%H:%M:%S")
}

#' Set source system information
#' File Metadata Setters
#'
#' Set information about the file and source system used to generate the Dataset
#' JSON object.
#'
#' @details
#'
#' Set information about the source system used to generate the Dataset JSON
#' object.
#' The fileOID parameter should be structured following description outlined in
#' the ODM V2.0 specification. "FileOIDs should be universally unique if at all
#' possible. One way to ensure this is to prefix every FileOID with an internet
#' domain name owned by the creator of the ODM file or database (followed by a
#' forward slash, "/"). For example,
#' FileOID="BestPharmaceuticals.com/Study5894/1" might be a good way to denote
#' the first file in a series for study 5894 from Best Pharmaceuticals."
#'
#' @param x datasetjson object
#' @param sys sourceSystem parameter, defined as "The computer system or
Expand Down
20 changes: 15 additions & 5 deletions R/read_dataset_json.R
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#' Read a Dataset JSON to datasetjson object
#'
#' This function validated a dataset JSON file on disk against the Dataset JSON schema, and if valid
#' returns a datasetjson object
#' This function validates a dataset JSON file against the Dataset JSON schema,
#' and if valid returns a datasetjson object. The Dataset JSON file can be
#' either a file path on disk of a URL which contains the Dataset JSON file.
#'
#' @param file File path on disk, or a pre-loaded Dataset JSON file in a single element character string
#' @param file File path or URL of a Dataset JSON file
#'
#' @return datasetjson object
#' @export
Expand All @@ -12,15 +13,24 @@
#' # Read from disk
#' \dontrun{
#' dat <- read_dataset_json("path/to/file.json")
#' # Read file from URL
#' dat <- dataset_json('https://www.somesite.com/file.json')
#' }
#'
#' # Read from an already imported character vector
#' ds_json <- dataset_json(iris, "IG.IRIS", "IRIS", "Iris", iris_items)
#' js <- write_dataset_json(ds_json)
#' dat <- read_dataset_json(js)
read_dataset_json <- function(file) {

if (path_is_url(file)) {
file_contents <- read_from_url(file)
} else {
file_contents <- readLines(file)
}

# Validate the input file against the schema
valid <- jsonvalidate::json_validate(file, schema_1_0_0, engine="ajv")
valid <- jsonvalidate::json_validate(file_contents, schema = schema_1_0_0, engine="ajv")

if (!valid) {
stop(paste0(c("Dataset JSON file is invalid per the JSON schema. ",
Expand All @@ -29,7 +39,7 @@ read_dataset_json <- function(file) {
}

# Read the file and convert to datasetjson object
ds_json <- jsonlite::fromJSON(file)
ds_json <- jsonlite::fromJSON(file_contents)

# Pull the object out with a lot of assumptions because the format has already
# been validated
Expand Down
61 changes: 61 additions & 0 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,64 @@ set_col_attr <- function(nm, d, attr, items) {
attr(x, attr) <- items[items$name == nm,][[attr]]
x
}

#' Get the index of nulls in a list
#'
#' @param x A list
#'
#' @return Integer vector of indices
#' @noRd
get_null_inds <- function(x) {
which(vapply(x, is.null, FUN.VALUE = TRUE))
}

#' Remove nulls from a Dataset JSON object
#'
#' Only targets the file and data metadata to pull off optional elements
#'
#' @param x A Dataset JSON object
#'
#' @return A Dataset JSON object
#' @noRd
remove_nulls <- function(x) {

# Specifically target the data metadata
dm_nulls <- get_null_inds(x[[get_data_type(x)]])
if (length(dm_nulls) > 0) {
x[[get_data_type(x)]] <- x[[get_data_type(x)]][-dm_nulls]
}

# Top level
fm_nulls <- get_null_inds(x)
if (length(fm_nulls) > 0) {
x <- x[-fm_nulls]
}

x
}

#' Check if given path is a URL
#'
#' @param path character string
#'
#' @return Boolean
#' @noRd
path_is_url <- function(path) {
grepl("^((http|ftp)s?|sftp|file)://", path)
}

#' Read data from a URL
#'
#' This function will let you pull data that's provided from a simple curl of a
#' URL
#'
#' @param path valid URL string
#'
#' @return Contents of URL
#' @noRd
read_from_url <- function(path) {
con <- url(path, method = "libcurl")
x <- readLines(con, warn=FALSE) # the EOL warning shouldn't be a problem for readers
close(con)
x
}
19 changes: 14 additions & 5 deletions R/validate_dataset_json.R
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#' Validate a Dataset JSON file
#'
#' This function calls `jsonvalidate::json_validate()` directly, with the parameters
#' necessary to retrieve the error information of an invalid JSON file per the
#' Dataset JSON schema.
#' This function calls `jsonvalidate::json_validate()` directly, with the
#' parameters necessary to retrieve the error information of an invalid JSON
#' file per the Dataset JSON schema.
#'
#' @param x Path to a Dataset JSON file or a character vector holding JSON text
#' @param x File path or URL of a Dataset JSON file, or a character vector
#' holding JSON text
#'
#' @return A data frame
#' @export
Expand All @@ -13,14 +14,22 @@
#'
#' \dontrun{
#' validate_dataset_json('path/to/file.json')
#' validate_dataset_json('https://www.somesite.com/file.json')
#' }
#'
#' ds_json <- dataset_json(iris, "IG.IRIS", "IRIS", "Iris", iris_items)
#' js <- write_dataset_json(ds_json)
#'
#' validate_dataset_json(js)
validate_dataset_json <- function(x) {
v <- jsonvalidate::json_validate(x, schema_1_0_0, engine="ajv", verbose=TRUE)
# If contents are a URL then pull out the content
if (path_is_url(x)) {
js <- read_from_url(x)
} else {
js <- x
}

v <- jsonvalidate::json_validate(js, schema_1_0_0, engine="ajv", verbose=TRUE)
if (!v) {
warning("File contains errors!")
return(attr(v, 'errors'))
Expand Down
13 changes: 5 additions & 8 deletions R/write_dataset_json.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,19 @@
#' \dontrun{
#' write_dataset_json(ds_json, "path/to/file.json")
#' }
write_dataset_json <- function(x, file, pretty=TRUE) {
write_dataset_json <- function(x, file, pretty=FALSE) {
stopifnot_datasetjson(x)

# Populate the as-of datetime
x[['asOfDateTime']] <- get_datetime()
# Populate the creation datetime
x[['creationDateTime']] <- get_datetime()

x <- remove_nulls(x)

if (!missing(file)) {
# Make sure the output path exists
if(!dir.exists(dirname(file))) {
stop("Folder supplied to `file` does not exist", call.=FALSE)
}

# Attach the file OID
x <- set_file_oid(x, tools::file_path_sans_ext(file))
} else{
x <- set_file_oid(x, "NA")
}

# Create the JSON text
Expand Down
10 changes: 4 additions & 6 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@ knitr::opts_chunk$set(echo = TRUE)
library(datasetjson)
```

# datasetjson

<!-- Please do not edit the README.md file as it is auto-generated after PR merges. Only edit the README.Rmd file -->
<!-- The purpose of this is to enable dynamic links using dyn_link function above to access devel/main admiral homepage respectively -->
<!-- To test this in your feature branch use code: rmarkdown::render("README.Rmd", output_format ="md_document") -->
# **datasetjson** <img src="man/figures/logo.svg" align="right" alt="" width="120" />

<!-- badges: start -->
[<img src="https://img.shields.io/codecov/c/github/atorus-research/datasetjson">](https://app.codecov.io/gh/atorus-research/datasetjson)
Expand Down Expand Up @@ -67,7 +63,7 @@ write_dataset_json(ds_updated, file = "./iris.json")
Or if you don't provide a file path, the JSON text will return directly.

```{r write_print}
js_text <- write_dataset_json(ds_updated)
js_text <- write_dataset_json(ds_updated, pretty=TRUE)
cat(js_text)
```

Expand All @@ -87,6 +83,8 @@ print(attr(dat$Sepal.Width, "type"))
```
Note that Dataset JSON is an early CDISC standard and is still subject to change, as as such this package will be updated. Backwards compatibility will be enforced once the standard itself is more stable. Until then, it is not recommended to use this package within production activities.

# [<img src="man/figures/cdisc.png" alt="" width="120" />](https://www.cdisc.org/)

## Acknowledgements

Thank you to Ben Straub and Eric Simms (GSK) for help and input during the original CDISC Dataset JSON hackathon that motivated this work.
Expand Down
16 changes: 7 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@

# datasetjson
# **datasetjson** <img src="man/figures/logo.svg" align="right" alt="" width="120" />

<!-- Please do not edit the README.md file as it is auto-generated after PR merges. Only edit the README.Rmd file -->
<!-- The purpose of this is to enable dynamic links using dyn_link function above to access devel/main admiral homepage respectively -->
<!-- To test this in your feature branch use code: rmarkdown::render("README.Rmd", output_format ="md_document") -->
<!-- badges: start -->

[<img src="https://img.shields.io/codecov/c/github/atorus-research/datasetjson">](https://app.codecov.io/gh/atorus-research/datasetjson)
Expand Down Expand Up @@ -73,15 +70,14 @@ write_dataset_json(ds_updated, file = "./iris.json")
Or if you don’t provide a file path, the JSON text will return directly.

``` r
js_text <- write_dataset_json(ds_updated)
js_text <- write_dataset_json(ds_updated, pretty=TRUE)
cat(js_text)
```

## {
## "creationDateTime": "2023-09-15T17:57:31",
## "creationDateTime": "2023-09-25T12:20:23",
## "datasetJSONVersion": "1.0.0",
## "fileOID": "NA",
## "asOfDateTime": "2023-09-15T17:57:31",
## "fileOID": "/some/path",
## "originator": "Some Org",
## "sourceSystem": "source system",
## "sourceSystemVersion": "1.0",
Expand Down Expand Up @@ -174,7 +170,7 @@ attached as attributes on the data frame itself:
print(attr(dat, "creationDateTime"))
```

## [1] "2023-09-15T17:57:31"
## [1] "2023-09-25T12:20:23"

``` r
print(attr(dat$Sepal.Length, "OID"))
Expand All @@ -194,6 +190,8 @@ compatibility will be enforced once the standard itself is more stable.
Until then, it is not recommended to use this package within production
activities.

# [<img src="man/figures/cdisc.png" alt="" width="120" />](https://www.cdisc.org/)

## Acknowledgements

Thank you to Ben Straub and Eric Simms (GSK) for help and input during
Expand Down
2 changes: 1 addition & 1 deletion man/data_metadata.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added man/figures/cdisc.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading