Convenience R functions for querying the SENAITE API
Developed these R functions to query the SENAITE API and to put the data into a more manageable format
get_senaite_datacan query data or metadata from any endpoint, specifying parametersdownload_senaite_reportwill download a PDF lab report given the URL and expected file nameview_senaite_outputviews the data in an easier format for viewingflatten_senaite_outputconverts the data to an easier format for analysis
Dependencies: cli, httr, keyring (make sure to save your senaite_username and senaite_password secrets before running these functions), tibble, tidyr
# functions.R
# Plone API: [plone.jsonapi.routes — plone.jsonapi.routes 0.9.3 documentation](https://plonejsonapiroutes.readthedocs.io/en/latest/index.html)
# SENAITE API: [senaite.jsonapi — senaite.jsonapi 1.2.5 documentation](https://senaitejsonapi.readthedocs.io/en/latest/index.html)
construct_senaite_url <- function(base_url, endpoint, params = list()) {
base_url <- sub('/$', '', base_url)
endpoint <- sub('^/', '', endpoint)
api_url <- paste0(base_url, '/@@API/senaite/v1/', endpoint)
if (length(params) > 0) {
query_string <- paste(names(params),
params,
sep = '=',
collapse = '&')
api_url <- paste0(api_url, '?', query_string)
}
api_url
}
get_senaite_data <- function(base_url,
endpoint = c(
'analysis',
'analysiscategories',
'analysiscategory',
'analysisprofile',
'analysisprofiles',
'analysisrequest',
'analysisrequestsfolder',
'analysisservice',
'analysisservices',
'analysisspec',
'analysisspecs',
'antibiotic',
'antibioticclass',
'arreport',
'artemplate',
'artemplates',
'astpanel',
'astpanelfolder',
'attachment',
'attachmenttype',
'attachmenttypes',
'auditlog',
'autoimportlog',
'batch',
'batchfolder',
'batchlabel',
'batchlabels',
'bikasetup',
'breakpointstable',
'breakpointstables',
'calculation',
'calculations',
'catalogs',
'client',
'clientfolder',
'contact',
'samplecontainer',
'samplecontainers',
'containertype',
'containertypes',
'databox',
'databoxfolder',
'department',
'departments',
'duplicateanalysis',
'dynamicanalysisspec',
'dynamicanalysisspecs',
'instrument',
'instrumentcalibration',
'instrumentcertification',
'instrumentlocation',
'instrumentlocations',
'instrumentmaintenancetask',
'instruments',
'instrumentscheduledtask',
'instrumenttype',
'instrumenttypes',
'instrumentvalidation',
'interpretationtemplate',
'invoice',
'labcontact',
'labcontacts',
'laboratory',
'labproduct',
'labproducts',
'manufacturer',
'manufacturers',
'method',
'methods',
'microorganism',
'microorganismcategory',
'microorganismcategories',
'microorganismfolder',
'multifile',
'patient',
'patientfolder',
'plone_site',
'preservation',
'preservations',
'pricelist',
'pricelistfolder',
'referenceanalysis',
'referencedefinition',
'referencedefinitions',
'referencesample',
'referencesamplesfolder',
'reflexrule',
'reflexrulefolder',
'rejectanalysis',
'report',
'reportfolder',
'samplecondition',
'sampleconditions',
'samplematrices',
'samplematrix',
'samplepoint',
'samplepoints',
'sampletype',
'sampletypes',
'samplingdeviation',
'samplingdeviations',
'search',
'storagelocation',
'storagelocations',
'subgroup',
'subgroups',
'supplier',
'suppliercontact',
'suppliers',
'supplyorder',
'supplyorderfolder',
'version',
'worksheet',
'worksheetfolder',
'worksheettemplate',
'worksheettemplates',
'version'
),
params = list(),
limit = 100,
verbose = TRUE) {
if (!is.list(params))
stop('`params` should be a list')
params <- c(params, limit = limit)
endpoint <- match.arg(endpoint)
url <- construct_senaite_url(base_url, endpoint, params)
if (verbose)
cli::cli_alert_info('Downloading: {url}')
response <- httr::GET(url,
httr::authenticate(
keyring::key_get('senaite_username'),
keyring::key_get('senaite_password')
))
output <- httr::content(response)
if (!is.null(output$pages)) {
n_pages <- output$pages
if (n_pages > 1) {
b_start <- seq(params$limit + 1, n_pages * params$limit, params$limit)
next_urls <- paste(url, '&b_start=', b_start, sep = '')
next_outputs <- lapply(next_urls, function(x) {
if (verbose)
cli::cli_alert_info('Downloading: {x}')
response <- httr::GET(x,
httr::authenticate(
keyring::key_get('senaite_username'),
keyring::key_get('senaite_password')
))
httr::content(response)
})
items <- unlist(c(
list(output$items),
lapply(next_outputs, function(x)
x$items)
), recursive = FALSE)
} else {
items <- output$items
}
items
} else {
if (verbose)
cli::cli_alert_warning('Not found')
NULL
}
}
download_senaite_report <- function(url, output_file) {
response <- httr::GET(url,
httr::authenticate(
keyring::key_get('senaite_username'),
keyring::key_get('senaite_password')
))
if (httr::status_code(response) == 200) {
writeBin(httr::content(response, 'raw'), output_file)
cli::cli_alert_info('Download successful: {output_file}')
} else {
cli::cli_alert_info('Failed to download file.\nHTTP Status: {httr::status_code(response)}')
}
}
view_senaite_output <- function(x, new_tab = TRUE) {
senaite_output <- tibble::enframe(unlist(x))
if (new_tab)
View(senaite_output, 'SENAITE output')
invisible(senaite_output)
}
flatten_senaite_output <- function(x, collapse = '➕') {
unpacked_list <- lapply(x, function(item) {
enframed <- tibble::enframe(unlist(item))
enframed |> tidyr::pivot_wider(
names_from = 'name',
values_from = 'value',
values_fn = \(x) paste0(x, collapse = collapse)
)
})
data.table::rbindlist(unpacked_list, use.names = TRUE, fill = TRUE)
}Examples for testing
# paul_testing.R
source('R/functions.R')
# Examples
BASE_URL <- 'https://senaite.example.com/yourlab'
# Example 1
analysis1 <- get_senaite_data(BASE_URL,
endpoint = 'analysis',
params = list(complete = TRUE, children = TRUE))
length(analysis1)
analysis2 <- get_senaite_data(
BASE_URL,
endpoint = 'analysis',
params = list(
complete = TRUE,
children = TRUE,
review_state = 'published'
)
)
length(analysis2)
analysis3 <- get_senaite_data(
BASE_URL,
endpoint = 'analysis',
params = list(
complete = TRUE,
children = TRUE,
recent_created = 'this-week' # today, yesterday this-week, this-month, this-year; recent_modified
)
)
length(analysis3)
sapply(analysis3, `[[`, 'title')
names(analysis3[[1]])
# Example 2
catalogs <- get_senaite_data(BASE_URL,
endpoint = 'catalogs',
params = list(complete = TRUE, children = TRUE))
sapply(catalogs, `[[`, 'id')
# Example 3
senaite_catalog_analysis <- get_senaite_data(
BASE_URL,
endpoint = 'search',
params = list(
catalog = 'senaite_catalog_analysis',
review_state = 'published',
sort_on = 'getDatePublished',
# or 'getDateSampled'
sort_order = 'desc',
complete = TRUE,
children = TRUE
)
)
length(senaite_catalog_analysis)
names(senaite_catalog_analysis[[1]])
sapply(senaite_catalog_analysis, `[[`, 'title')
sapply(senaite_catalog_analysis, `[[`, 'Result')
# Example 4
arreport <- get_senaite_data(BASE_URL,
endpoint = 'arreport',
params = list(complete = TRUE, children = TRUE))
length(arreport)
sapply(arreport, `[[`, 'Pdf') # all lab reports
view_senaite_output(arreport)
# Example 5
download_senaite_report(
'https://senaite.example.come/yourlab/clients/client-9/plasma-0001/arreport-1/at_download/Pdf',
'plasma-0001.pdf'
)
# Example 6
patient <- get_senaite_data(BASE_URL,
endpoint = 'patient',
params = list(complete = TRUE, children = TRUE))
patient_output <- view_senaite_output(patient, new_tab = FALSE)
View(patient_output[patient_output$name %in% c('path', 'birthdate'), ])
flatten_senaite_output(patient)You can download all your metadata with:
# download_metadata.R
source('R/functions.R')
BASE_URL <- 'https://senaite.example.com/yourlab'
get_it_all <- function(x)
get_senaite_data(BASE_URL,
endpoint = x,
params = list(complete = TRUE, children = TRUE))
metadata <- list(
analysiscategory = get_it_all('analysiscategory'),
analysisprofile = get_it_all('analysisprofile'),
analysisservice = get_it_all('analysisservice'),
analysisspec = get_it_all('analysisspec'),
antibiotic = get_it_all('antibiotic'),
antibioticclass = get_it_all('antibioticclass'),
artemplate = get_it_all('artemplate'),
bikasetup = get_it_all('bikasetup'),
containertype = get_it_all('containertype'),
department = get_it_all('department'),
interpretationtemplate = get_it_all('interpretationtemplate'),
labcontact = get_it_all('labcontact'),
method = get_it_all('method'),
microorganism = get_it_all('microorganism'),
preservation = get_it_all('preservation'),
report = get_it_all('report'),
samplecondition = get_it_all('samplecondition'),
samplecontainer = get_it_all('samplecontainer'),
samplematrix = get_it_all('samplematrix'),
sampletype = get_it_all('sampletype')
)
metadata <- lapply(metadata, flatten_senaite_output)
metadata_output <-
openxlsx::createWorkbook(creator = 'Me',
title = 'SENAITE metadata',
category = 'Laboratory surveillance')
for (i in names(metadata)) {
if (nrow(metadata[[i]] > 0)) {
openxlsx::addWorksheet(metadata_output, i)
openxlsx::writeDataTable(
metadata_output,
sheet = i,
x = metadata[[i]],
headerStyle = openxlsx::createStyle(wrapText = TRUE, halign = 'left'),
tableStyle = 'TableStyleLight9'
)
openxlsx::setColWidths(
metadata_output,
sheet = i,
cols = 1:ncol(metadata[[i]]),
widths = 'auto'
)
openxlsx::freezePane(metadata_output, i, firstRow = TRUE)
}
}
openxlsx::saveWorkbook(metadata_output, file = 'metadata.xlsx', overwrite = TRUE)