From ba53612f105b014c5bd5c2e9cd260576b89011a2 Mon Sep 17 00:00:00 2001 From: eblondel Date: Mon, 16 Oct 2023 09:31:46 +0200 Subject: [PATCH 01/25] support #129 - communities endpoint with Zenodo InvenioRDM --- R/ZenodoManager.R | 92 +++++++++++++++++++++++++++++++++----------- man/ZenodoManager.Rd | 8 +++- 2 files changed, 76 insertions(+), 24 deletions(-) diff --git a/R/ZenodoManager.R b/R/ZenodoManager.R index a4894d1..eae8a40 100644 --- a/R/ZenodoManager.R +++ b/R/ZenodoManager.R @@ -184,34 +184,82 @@ ZenodoManager <- R6Class("ZenodoManager", #' @param pretty Prettify the output. By default the argument \code{pretty} is set to #' \code{TRUE} which will returns the list of communities as \code{data.frame}. #' Set \code{pretty = FALSE} to get the raw list of communities - #' @return list of communities as \code{data.frame} or \code{list} - getCommunities = function(pretty = TRUE){ - zenReq <- ZenodoRequest$new(private$url, "GET", "communities/?q=&size=10000", - token= self$getToken(), + #' @param q an ElasticSearch compliant query, object of class \code{character}. Default is emtpy. + #' Note that the Zenodo API restrains a maximum number of 10,000 records to be retrieved. Consequently, + #' not all grants can be listed from Zenodo, a query has to be specified. + #' @param size number of grants to be returned. By default equal to 1000. + #' @return list of grants as \code{data.frame} or \code{list} + getCommunities = function(pretty = TRUE, q = "", size = 100){ + page <- 1 + lastPage <- FALSE + zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("communities?q=%s&size=%s&page=%s", URLencode(q), size, page), + token = self$getToken(), logger = self$loggerType) zenReq$execute() - out <- zenReq$getResponse() + out <- NULL if(zenReq$getStatus() == 200){ - out <- out$hits$hits - if(pretty){ - out = do.call("rbind", lapply(out,function(x){ - rec = data.frame( - id = x$id, - title = x$title, - description = x$description, - curation_policy = x$curation_policy, - url = x$links$html, - created = x$created, - updated = x$updated, - stringsAsFactors = FALSE - ) - return(rec) - })) + resp <- zenReq$getResponse() + communities <- resp$hits$hits + total <- resp$hits$total + if(total > 10000){ + self$WARN(sprintf("Total of %s records found: the Zenodo API limits to a maximum of 10,000 records!", total)) + } + total_remaining <- total + hasCommunities <- length(communities)>0 + while(hasCommunities){ + out <- c(out, communities) + if(!is.null(communities)){ + self$INFO(sprintf("Successfully fetched list of communities - page %s", page)) + page <- page+1 #next + total_remaining <- total_remaining-length(communities) + }else{ + lastPage <- TRUE + } + zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("communities?q=%s&size=%s&page=%s", URLencode(q), size, page), + token = self$getToken(), + logger = self$loggerType) + zenReq$execute() + if(zenReq$getStatus() == 200){ + resp <- zenReq$getResponse() + communities <- resp$hits$hits + hasCommunities <- length(communities)>0 + if(lastPage) break; + }else{ + self$WARN(sprintf("Maximum allowed size for list of communities - page %s - attempt to decrease size", page)) + size <- size-1 + hasCommunities <- TRUE + communities <- NULL + } } - self$INFO("Successfully fetched list of communities") + self$INFO("Successfully fetched list of communities!") }else{ + out <- zenReq$getResponse() self$ERROR(sprintf("Error while fetching communities: %s", out$message)) + for(error in out$errors){ + self$ERROR(sprintf("Error: %s - %s", error$field, error$message)) + } } + + if(pretty){ + out = do.call("rbind", lapply(out,function(x){ + rec = data.frame( + id = x$id, + title = x$metadata$title, + description = if(!is.null(x$metadata$description)) x$metadata$description else NA, + website = if(!is.null(x$metadata$website)) x$metadata$website else NA, + visibility = x$access$visibility, + member_policy = x$access$member_policy, + record_policy = x$access$record_policy, + review_policy = x$access$review_policy, + url = x$links$self_html, + created = x$created, + updated = x$updated, + stringsAsFactors = FALSE + ) + return(rec) + })) + } + return(out) }, @@ -241,7 +289,7 @@ ZenodoManager <- R6Class("ZenodoManager", #' \code{TRUE} which will returns the list of grants as \code{data.frame}. #' Set \code{pretty = FALSE} to get the raw list of grants #' @param q an ElasticSearch compliant query, object of class \code{character}. Default is emtpy. - #' Note that the Zenodo API restrains a maximum number of 10,000 grants to be retrieved. Consequently, + #' Note that the Zenodo API restrains a maximum number of 10,000 records to be retrieved. Consequently, #' not all grants can be listed from Zenodo, a query has to be specified. #' @param size number of grants to be returned. By default equal to 1000. #' @return list of grants as \code{data.frame} or \code{list} diff --git a/man/ZenodoManager.Rd b/man/ZenodoManager.Rd index 6aa39ef..3ad7c54 100644 --- a/man/ZenodoManager.Rd +++ b/man/ZenodoManager.Rd @@ -234,7 +234,7 @@ the license \subsection{Method \code{getCommunities()}}{ Get Communities supported by Zenodo. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ZenodoManager$getCommunities(pretty = TRUE)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ZenodoManager$getCommunities(pretty = TRUE, q = "", size = 100)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -243,11 +243,15 @@ Get Communities supported by Zenodo. \item{\code{pretty}}{Prettify the output. By default the argument \code{pretty} is set to \code{TRUE} which will returns the list of communities as \code{data.frame}. Set \code{pretty = FALSE} to get the raw list of communities} + +\item{\code{q}}{an ElasticSearch compliant query, object of class \code{character}. Default is emtpy.} + +\item{\code{size}}{number of grants to be returned. By default equal to 1000.} } \if{html}{\out{}} } \subsection{Returns}{ -list of communities as \code{data.frame} or \code{list} +list of grants as \code{data.frame} or \code{list} } } \if{html}{\out{
}} From e7e12b132311f2ccf3a67240feb2ef12ecfb4fc3 Mon Sep 17 00:00:00 2001 From: eblondel Date: Mon, 16 Oct 2023 09:35:42 +0200 Subject: [PATCH 02/25] #129 change default size --- R/ZenodoManager.R | 4 ++-- man/ZenodoManager.Rd | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/R/ZenodoManager.R b/R/ZenodoManager.R index eae8a40..70eb3ab 100644 --- a/R/ZenodoManager.R +++ b/R/ZenodoManager.R @@ -187,9 +187,9 @@ ZenodoManager <- R6Class("ZenodoManager", #' @param q an ElasticSearch compliant query, object of class \code{character}. Default is emtpy. #' Note that the Zenodo API restrains a maximum number of 10,000 records to be retrieved. Consequently, #' not all grants can be listed from Zenodo, a query has to be specified. - #' @param size number of grants to be returned. By default equal to 1000. + #' @param size number of grants to be returned. By default equal to 500 #' @return list of grants as \code{data.frame} or \code{list} - getCommunities = function(pretty = TRUE, q = "", size = 100){ + getCommunities = function(pretty = TRUE, q = "", size = 500){ page <- 1 lastPage <- FALSE zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("communities?q=%s&size=%s&page=%s", URLencode(q), size, page), diff --git a/man/ZenodoManager.Rd b/man/ZenodoManager.Rd index 3ad7c54..12d01d9 100644 --- a/man/ZenodoManager.Rd +++ b/man/ZenodoManager.Rd @@ -234,7 +234,7 @@ the license \subsection{Method \code{getCommunities()}}{ Get Communities supported by Zenodo. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ZenodoManager$getCommunities(pretty = TRUE, q = "", size = 100)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ZenodoManager$getCommunities(pretty = TRUE, q = "", size = 500)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -244,9 +244,11 @@ Get Communities supported by Zenodo. \code{TRUE} which will returns the list of communities as \code{data.frame}. Set \code{pretty = FALSE} to get the raw list of communities} -\item{\code{q}}{an ElasticSearch compliant query, object of class \code{character}. Default is emtpy.} +\item{\code{q}}{an ElasticSearch compliant query, object of class \code{character}. Default is emtpy. +Note that the Zenodo API restrains a maximum number of 10,000 records to be retrieved. Consequently, +not all grants can be listed from Zenodo, a query has to be specified.} -\item{\code{size}}{number of grants to be returned. By default equal to 1000.} +\item{\code{size}}{number of grants to be returned. By default equal to 500} } \if{html}{\out{}} } @@ -287,7 +289,7 @@ Get Grants supported by Zenodo. \if{html}{\out{
}} \describe{ \item{\code{q}}{an ElasticSearch compliant query, object of class \code{character}. Default is emtpy. -Note that the Zenodo API restrains a maximum number of 10,000 grants to be retrieved. Consequently, +Note that the Zenodo API restrains a maximum number of 10,000 records to be retrieved. Consequently, not all grants can be listed from Zenodo, a query has to be specified.} \item{\code{pretty}}{Prettify the output. By default the argument \code{pretty} is set to From 5a9f81f2cb30403707c2c2d2fc673f56f002d9d7 Mon Sep 17 00:00:00 2001 From: eblondel Date: Mon, 16 Oct 2023 15:16:57 +0200 Subject: [PATCH 03/25] support #130 --- DESCRIPTION | 2 +- NAMESPACE | 1 + R/ZenodoManager.R | 63 +++++++++++++++++++++----------------------- R/zen4R.R | 1 + man/ZenodoManager.Rd | 10 +++---- 5 files changed, 38 insertions(+), 39 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 7efba39..c0cbe37 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -11,7 +11,7 @@ Authors@R: c( person("Jemma", "Stachelek", role = c("ctb"), comment = c(ORCID = "0000-0002-5924-2464"))) Maintainer: Emmanuel Blondel Depends: R (>= 3.3.0), methods -Imports: R6, httr, jsonlite, XML, xml2, keyring, tools, atom4R, utf8 +Imports: R6, httr, jsonlite, XML, xml2, keyring, tools, atom4R, utf8, plyr Suggests: testthat, parallel, knitr, markdown Description: Provides an Interface to 'Zenodo' () REST API, including management of depositions, attribution of DOIs by 'Zenodo' and diff --git a/NAMESPACE b/NAMESPACE index 31ac534..bbd4de9 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -17,6 +17,7 @@ import(keyring) import(methods) import(xml2) importFrom(R6,R6Class) +importFrom(plyr,rbind.fill) importFrom(tools,file_path_as_absolute) importFrom(tools,md5sum) importFrom(utf8,utf8_encode) diff --git a/R/ZenodoManager.R b/R/ZenodoManager.R index 70eb3ab..c7b7f2c 100644 --- a/R/ZenodoManager.R +++ b/R/ZenodoManager.R @@ -291,13 +291,12 @@ ZenodoManager <- R6Class("ZenodoManager", #' @param q an ElasticSearch compliant query, object of class \code{character}. Default is emtpy. #' Note that the Zenodo API restrains a maximum number of 10,000 records to be retrieved. Consequently, #' not all grants can be listed from Zenodo, a query has to be specified. - #' @param size number of grants to be returned. By default equal to 1000. + #' @param size number of grants to be returned. By default equal to 500. #' @return list of grants as \code{data.frame} or \code{list} - getGrants = function(q = "", pretty = TRUE, size = 1000){ - if(q=="") size = 10000 + getGrants = function(q = "", pretty = TRUE, size = 500){ page <- 1 lastPage <- FALSE - zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("grants/?q=%s&size=%s&page=%s", URLencode(q), size, page), + zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("grants?q=%s&size=%s&page=%s", URLencode(q), size, page), token = self$getToken(), logger = self$loggerType) zenReq$execute() @@ -415,15 +414,13 @@ ZenodoManager <- R6Class("ZenodoManager", #' \code{TRUE} which will returns the list of funders as \code{data.frame}. #' Set \code{pretty = FALSE} to get the raw list of funders #' @param q an ElasticSearch compliant query, object of class \code{character}. Default is emtpy. - #' Note that the Zenodo API restrains a maximum number of 10,000 funders to be retrieved. Consequently, + #' Note that the Zenodo API restrains a maximum number of 10,000 records to be retrieved. Consequently, #' not all funders can be listed from Zenodo, a query has to be specified. - #' @param size number of funders to be returned. By default equal to 1000. + #' @param size number of funders to be returned. By default equal to 500 #' @return list of funders as \code{data.frame} or \code{list} - getFunders = function(q = "", pretty = TRUE, size = 1000){ - if(q=="") size = 10000 + getFunders = function(q = "", pretty = TRUE, size = 500){ page <- 1 - lastPage <- FALSE - zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("funders/?q=%s&size=%s&page=%s", URLencode(q), size, page), + zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("funders?q=%s&size=%s&page=%s", URLencode(q), size, page), token = self$getToken(), logger = self$loggerType) zenReq$execute() @@ -439,18 +436,16 @@ ZenodoManager <- R6Class("ZenodoManager", hasFunders <- length(funders)>0 while(hasFunders){ out <- c(out, funders) - if(!is.null(funders)){ - self$INFO(sprintf("Successfully fetched list of funders - page %s", page)) - if(q != ""){ - page <- page+1 #next - total_remaining <- total_remaining-length(funders) - }else{ - break; - } - }else{ - lastPage <- TRUE + self$INFO(sprintf("Successfully fetched list of funders - page %s", page)) + total_remaining <- total_remaining-length(funders) + if(total_remaining <= size) size = total_remaining + if(total_remaining == 0){ + break } - zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("funders/?q=%s&size=%s&page=%s", URLencode(q), size, page), + + #next page + page <- page+1 + zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("funders?q=%s&size=%s&page=%s", URLencode(q), size, page), token = self$getToken(), logger = self$loggerType) zenReq$execute() @@ -458,12 +453,9 @@ ZenodoManager <- R6Class("ZenodoManager", resp <- zenReq$getResponse() funders <- resp$hits$hits hasFunders <- length(funders)>0 - if(lastPage) break; }else{ - self$WARN(sprintf("Maximum allowed size for list of funders - page %s - attempt to decrease size", page)) - size <- size-1 - hasFunders <- TRUE - funders <- NULL + self$WARN(sprintf("Maximum allowed size for list of communities reached at page %s", page)) + break } } self$INFO("Successfully fetched list of funders!") @@ -476,14 +468,19 @@ ZenodoManager <- R6Class("ZenodoManager", } if(pretty){ - out = do.call("rbind", lapply(out,function(x){ + out = do.call("rbind.fill", lapply(out,function(x){ + + identifiers = do.call("cbind", lapply(x$identifiers, function(identifier){ + out_id = data.frame(scheme = identifier$identifier, stringsAsFactors = F) + names(out_id) = identifier$scheme + return(out_id) + })) + rec = data.frame( - id = x$metadata$doi, - doi = x$metadata$doi, - country = x$metadata$country, - name = x$metadata$name, - type = x$metadata$type, - subtype = x$metadata$subtype, + id = x$id, + country = x$country, + name = x$name, + identifiers, created = x$created, updated = x$updated, stringsAsFactors = FALSE diff --git a/R/zen4R.R b/R/zen4R.R index f7c39e3..f592f22 100644 --- a/R/zen4R.R +++ b/R/zen4R.R @@ -14,6 +14,7 @@ #' @importFrom tools md5sum #' @import atom4R #' @importFrom utf8 utf8_encode +#' @importFrom plyr rbind.fill #' #' @title Interface to 'Zenodo' REST API #' @description Provides an Interface to 'Zenodo' () REST API, diff --git a/man/ZenodoManager.Rd b/man/ZenodoManager.Rd index 12d01d9..d5e7c65 100644 --- a/man/ZenodoManager.Rd +++ b/man/ZenodoManager.Rd @@ -282,7 +282,7 @@ the community \subsection{Method \code{getGrants()}}{ Get Grants supported by Zenodo. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ZenodoManager$getGrants(q = "", pretty = TRUE, size = 1000)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ZenodoManager$getGrants(q = "", pretty = TRUE, size = 500)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -296,7 +296,7 @@ not all grants can be listed from Zenodo, a query has to be specified.} \code{TRUE} which will returns the list of grants as \code{data.frame}. Set \code{pretty = FALSE} to get the raw list of grants} -\item{\code{size}}{number of grants to be returned. By default equal to 1000.} +\item{\code{size}}{number of grants to be returned. By default equal to 500.} } \if{html}{\out{
}} } @@ -354,21 +354,21 @@ the grant \subsection{Method \code{getFunders()}}{ Get Funders supported by Zenodo based on a query. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ZenodoManager$getFunders(q = "", pretty = TRUE, size = 1000)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ZenodoManager$getFunders(q = "", pretty = TRUE, size = 500)}\if{html}{\out{
}} } \subsection{Arguments}{ \if{html}{\out{
}} \describe{ \item{\code{q}}{an ElasticSearch compliant query, object of class \code{character}. Default is emtpy. -Note that the Zenodo API restrains a maximum number of 10,000 funders to be retrieved. Consequently, +Note that the Zenodo API restrains a maximum number of 10,000 records to be retrieved. Consequently, not all funders can be listed from Zenodo, a query has to be specified.} \item{\code{pretty}}{Prettify the output. By default the argument \code{pretty} is set to \code{TRUE} which will returns the list of funders as \code{data.frame}. Set \code{pretty = FALSE} to get the raw list of funders} -\item{\code{size}}{number of funders to be returned. By default equal to 1000.} +\item{\code{size}}{number of funders to be returned. By default equal to 500} } \if{html}{\out{
}} } From 30f88aaa8c5d1f4956c145c531769f6356a2590b Mon Sep 17 00:00:00 2001 From: eblondel Date: Mon, 16 Oct 2023 15:27:26 +0200 Subject: [PATCH 04/25] #129 improve pagination support --- R/ZenodoManager.R | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/R/ZenodoManager.R b/R/ZenodoManager.R index c7b7f2c..2201f05 100644 --- a/R/ZenodoManager.R +++ b/R/ZenodoManager.R @@ -191,7 +191,6 @@ ZenodoManager <- R6Class("ZenodoManager", #' @return list of grants as \code{data.frame} or \code{list} getCommunities = function(pretty = TRUE, q = "", size = 500){ page <- 1 - lastPage <- FALSE zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("communities?q=%s&size=%s&page=%s", URLencode(q), size, page), token = self$getToken(), logger = self$loggerType) @@ -208,13 +207,15 @@ ZenodoManager <- R6Class("ZenodoManager", hasCommunities <- length(communities)>0 while(hasCommunities){ out <- c(out, communities) - if(!is.null(communities)){ - self$INFO(sprintf("Successfully fetched list of communities - page %s", page)) - page <- page+1 #next - total_remaining <- total_remaining-length(communities) - }else{ - lastPage <- TRUE + self$INFO(sprintf("Successfully fetched list of communities - page %s", page)) + total_remaining <- total_remaining-length(communities) + if(total_remaining <= size) size = total_remaining + if(total_remaining == 0){ + break } + + #next page + page <- page+1 zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("communities?q=%s&size=%s&page=%s", URLencode(q), size, page), token = self$getToken(), logger = self$loggerType) @@ -223,12 +224,9 @@ ZenodoManager <- R6Class("ZenodoManager", resp <- zenReq$getResponse() communities <- resp$hits$hits hasCommunities <- length(communities)>0 - if(lastPage) break; }else{ - self$WARN(sprintf("Maximum allowed size for list of communities - page %s - attempt to decrease size", page)) - size <- size-1 - hasCommunities <- TRUE - communities <- NULL + self$WARN(sprintf("Maximum allowed size for list of communities at page %s", page)) + break } } self$INFO("Successfully fetched list of communities!") From 1d702a13aa1b786f3b4f853bd3d7d385ebae6caa Mon Sep 17 00:00:00 2001 From: eblondel Date: Mon, 16 Oct 2023 22:39:27 +0200 Subject: [PATCH 05/25] support #131 --- R/ZenodoManager.R | 24 ++++++++++++++---------- tests/testthat/test_licenses.R | 4 ++-- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/R/ZenodoManager.R b/R/ZenodoManager.R index 2201f05..237a9b0 100644 --- a/R/ZenodoManager.R +++ b/R/ZenodoManager.R @@ -134,22 +134,26 @@ ZenodoManager <- R6Class("ZenodoManager", #' Set \code{pretty = FALSE} to get the raw list of licenses. #' @return list of licenses as \code{data.frame} or \code{list} getLicenses = function(pretty = TRUE){ - zenReq <- ZenodoRequest$new(private$url, "GET", "licenses/?q=&size=1000", + zenReq <- ZenodoRequest$new(private$url, "GET", "vocabularies/licenses?q=&size=1000", token= self$getToken(), logger = self$loggerType) zenReq$execute() out <- zenReq$getResponse() if(zenReq$getStatus() == 200){ - out <- out$hits$hits + out = out$hits$hits if(pretty){ out = do.call("rbind", lapply(out,function(x){ - rec = x$metadata - rec$`$schema` <- NULL - rec$is_generic <- NULL - rec$suggest <- NULL - rec <- as.data.frame(rec) - rec <- rec[,c("id", "title", "url", "domain_content", "domain_data", "domain_software", "family", - "maintainer", "od_conformance", "osd_conformance", "status")] + rec = data.frame( + id = x$id, + title = x$title[[1]], + description = if(!is.null(x$description)) x$description[[1]] else NA, + url = x$props$url, + schema = x$props$scheme, + osi_approved = x$props$osi_approved, + revision_id = x$revision_id, + created = x$created, + updated = x$updated + ) return(rec) })) } @@ -164,7 +168,7 @@ ZenodoManager <- R6Class("ZenodoManager", #' @param id license id #' @return the license getLicenseById = function(id){ - zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("licenses/%s",id), + zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("vocabularies/licenses/%s",id), token= self$getToken(), logger = self$loggerType) zenReq$execute() diff --git a/tests/testthat/test_licenses.R b/tests/testthat/test_licenses.R index 31888de..162b31c 100644 --- a/tests/testthat/test_licenses.R +++ b/tests/testthat/test_licenses.R @@ -21,7 +21,7 @@ test_that("licenses are retrieved",{ test_that("license is retrieved by id",{ zenodo <- ZenodoManager$new(logger = "INFO") zen_license <- zenodo$getLicenseById("mit") - expect_equal(zen_license$metadata$id, "MIT") - expect_equal(zen_license$metadata$title, "MIT License") + expect_equal(zen_license$id, "mit") + expect_equal(zen_license$title[[1]], "MIT License") Sys.sleep(2) }) \ No newline at end of file From 0550fd7d6471971783086a73b6bdd8a3111f3eca Mon Sep 17 00:00:00 2001 From: eblondel Date: Mon, 16 Oct 2023 22:48:34 +0200 Subject: [PATCH 06/25] support #132 --- R/ZenodoManager.R | 54 ++++++++++++++++++++++++++++++++- man/ZenodoManager.Rd | 44 +++++++++++++++++++++++++++ tests/testthat/test_languages.R | 27 +++++++++++++++++ 3 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 tests/testthat/test_languages.R diff --git a/R/ZenodoManager.R b/R/ZenodoManager.R index 237a9b0..b9f28d1 100644 --- a/R/ZenodoManager.R +++ b/R/ZenodoManager.R @@ -125,7 +125,59 @@ ZenodoManager <- R6Class("ZenodoManager", return(token) }, - #Licenses + #Vocabulary/Languages + #------------------------------------------------------------------------------------------ + + #' @description Get Languages supported by Zenodo. + #' @param pretty Prettify the output. By default the argument \code{pretty} is set to + #' \code{TRUE} which will returns the list of languages as \code{data.frame}. + #' Set \code{pretty = FALSE} to get the raw list of languages + #' @return list of languages as \code{data.frame} or \code{list} + getLanguages = function(pretty = TRUE){ + zenReq <- ZenodoRequest$new(private$url, "GET", "vocabularies/languages?q=&size=1000", + token= self$getToken(), + logger = self$loggerType) + zenReq$execute() + out <- zenReq$getResponse() + if(zenReq$getStatus() == 200){ + out = out$hits$hits + if(pretty){ + out = do.call("rbind", lapply(out,function(x){ + rec = data.frame( + id = x$id, + title = x$title[[1]], + revision_id = x$revision_id, + created = x$created, + updated = x$updated + ) + return(rec) + })) + } + self$INFO("Successfully fetched list of languages") + }else{ + self$ERROR(sprintf("Error while fetching languages: %s", out$message)) + } + return(out) + }, + + #' @description Get language by Id. + #' @param id license id + #' @return the license + getLanguageById = function(id){ + zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("vocabularies/languages/%s",id), + token= self$getToken(), + logger = self$loggerType) + zenReq$execute() + out <- zenReq$getResponse() + if(zenReq$getStatus() == 200){ + self$INFO(sprintf("Successfully fetched language '%s'",id)) + }else{ + self$ERROR(sprintf("Error while fetching language '%s': %s", id, out$message)) + } + return(out) + }, + + #Vocabulary/Licenses #------------------------------------------------------------------------------------------ #' @description Get Licenses supported by Zenodo. diff --git a/man/ZenodoManager.Rd b/man/ZenodoManager.Rd index d5e7c65..85247e2 100644 --- a/man/ZenodoManager.Rd +++ b/man/ZenodoManager.Rd @@ -92,6 +92,8 @@ Emmanuel Blondel \itemize{ \item \href{#method-ZenodoManager-new}{\code{ZenodoManager$new()}} \item \href{#method-ZenodoManager-getToken}{\code{ZenodoManager$getToken()}} +\item \href{#method-ZenodoManager-getLanguages}{\code{ZenodoManager$getLanguages()}} +\item \href{#method-ZenodoManager-getLanguageById}{\code{ZenodoManager$getLanguageById()}} \item \href{#method-ZenodoManager-getLicenses}{\code{ZenodoManager$getLicenses()}} \item \href{#method-ZenodoManager-getLicenseById}{\code{ZenodoManager$getLicenseById()}} \item \href{#method-ZenodoManager-getCommunities}{\code{ZenodoManager$getCommunities()}} @@ -187,6 +189,48 @@ the token, object of class \code{character} } } \if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ZenodoManager-getLanguages}{}}} +\subsection{Method \code{getLanguages()}}{ +Get Languages supported by Zenodo. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ZenodoManager$getLanguages(pretty = TRUE)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{pretty}}{Prettify the output. By default the argument \code{pretty} is set to +\code{TRUE} which will returns the list of languages as \code{data.frame}. +Set \code{pretty = FALSE} to get the raw list of languages} +} +\if{html}{\out{
}} +} +\subsection{Returns}{ +list of languages as \code{data.frame} or \code{list} +} +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ZenodoManager-getLanguageById}{}}} +\subsection{Method \code{getLanguageById()}}{ +Get language by Id. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ZenodoManager$getLanguageById(id)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{id}}{license id} +} +\if{html}{\out{
}} +} +\subsection{Returns}{ +the license +} +} +\if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ZenodoManager-getLicenses}{}}} \subsection{Method \code{getLicenses()}}{ diff --git a/tests/testthat/test_languages.R b/tests/testthat/test_languages.R new file mode 100644 index 0000000..5417c03 --- /dev/null +++ b/tests/testthat/test_languages.R @@ -0,0 +1,27 @@ +# test_languages.R +# Author: Emmanuel Blondel +# +# Description: Unit tests for Zenodo supported languages +#======================= +require(zen4R, quietly = TRUE) +require(testthat) + +context("languages") + +test_that("languages are retrieved",{ + zenodo <- ZenodoManager$new(logger = "INFO") + zen_languages_df <- zenodo$getLanguages() + Sys.sleep(2) + expect_is(zen_languages_df, "data.frame") + zen_languages_raw <- zenodo$getLanguages(pretty = FALSE) + expect_is(zen_languages_raw, "list") + Sys.sleep(2) +}) + +test_that("language is retrieved by id",{ + zenodo <- ZenodoManager$new(logger = "INFO") + zen_license <- zenodo$getLanguageById("eng") + expect_equal(zen_license$id, "eng") + expect_equal(zen_license$title[[1]], "English") + Sys.sleep(2) +}) \ No newline at end of file From 809b1221784c99697f5b68012cd52e2bc5a8f848 Mon Sep 17 00:00:00 2001 From: eblondel Date: Mon, 16 Oct 2023 23:01:05 +0200 Subject: [PATCH 07/25] #129 fix Rdoc --- R/ZenodoManager.R | 6 +++--- man/ZenodoManager.Rd | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/R/ZenodoManager.R b/R/ZenodoManager.R index b9f28d1..543c3c4 100644 --- a/R/ZenodoManager.R +++ b/R/ZenodoManager.R @@ -242,9 +242,9 @@ ZenodoManager <- R6Class("ZenodoManager", #' Set \code{pretty = FALSE} to get the raw list of communities #' @param q an ElasticSearch compliant query, object of class \code{character}. Default is emtpy. #' Note that the Zenodo API restrains a maximum number of 10,000 records to be retrieved. Consequently, - #' not all grants can be listed from Zenodo, a query has to be specified. - #' @param size number of grants to be returned. By default equal to 500 - #' @return list of grants as \code{data.frame} or \code{list} + #' not all communities can be listed from Zenodo, a query has to be specified. + #' @param size number of communities to be returned. By default equal to 500 + #' @return list of communities as \code{data.frame} or \code{list} getCommunities = function(pretty = TRUE, q = "", size = 500){ page <- 1 zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("communities?q=%s&size=%s&page=%s", URLencode(q), size, page), diff --git a/man/ZenodoManager.Rd b/man/ZenodoManager.Rd index 85247e2..0bb8ccb 100644 --- a/man/ZenodoManager.Rd +++ b/man/ZenodoManager.Rd @@ -290,14 +290,14 @@ Set \code{pretty = FALSE} to get the raw list of communities} \item{\code{q}}{an ElasticSearch compliant query, object of class \code{character}. Default is emtpy. Note that the Zenodo API restrains a maximum number of 10,000 records to be retrieved. Consequently, -not all grants can be listed from Zenodo, a query has to be specified.} +not all communities can be listed from Zenodo, a query has to be specified.} -\item{\code{size}}{number of grants to be returned. By default equal to 500} +\item{\code{size}}{number of communities to be returned. By default equal to 500} } \if{html}{\out{}} } \subsection{Returns}{ -list of grants as \code{data.frame} or \code{list} +list of communities as \code{data.frame} or \code{list} } } \if{html}{\out{
}} From 4319d10895a9041afc3c6fc31d473d9e35e3d3c8 Mon Sep 17 00:00:00 2001 From: eblondel Date: Tue, 17 Oct 2023 00:46:46 +0200 Subject: [PATCH 08/25] support #133 --- R/ZenodoManager.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/ZenodoManager.R b/R/ZenodoManager.R index 543c3c4..de382c1 100644 --- a/R/ZenodoManager.R +++ b/R/ZenodoManager.R @@ -756,8 +756,8 @@ ZenodoManager <- R6Class("ZenodoManager", depositRecord = function(record, publish = FALSE){ data <- record type <- ifelse(is.null(record$id), "POST", "PUT") - request <- ifelse(is.null(record$id), "deposit/depositions", - sprintf("deposit/depositions/%s", record$id)) + request <- ifelse(is.null(record$id), "records", + sprintf("records/%s/draft", record$id)) zenReq <- ZenodoRequest$new(private$url, type, request, data = data, token = self$getToken(), logger = self$loggerType) From 4c276e0fbac0343da43c4b3ab9603f53382c7db3 Mon Sep 17 00:00:00 2001 From: eblondel Date: Tue, 17 Oct 2023 01:23:22 +0200 Subject: [PATCH 09/25] support #134 --- R/ZenodoManager.R | 114 ++++++++++++++++++++--------------- man/ZenodoManager.Rd | 81 ++++++++++++++++++++++++- tests/testthat/test_awards.R | 24 ++++++++ tests/testthat/test_grants.R | 25 -------- 4 files changed, 167 insertions(+), 77 deletions(-) create mode 100644 tests/testthat/test_awards.R delete mode 100644 tests/testthat/test_grants.R diff --git a/R/ZenodoManager.R b/R/ZenodoManager.R index de382c1..a1bd194 100644 --- a/R/ZenodoManager.R +++ b/R/ZenodoManager.R @@ -335,10 +335,10 @@ ZenodoManager <- R6Class("ZenodoManager", return(out) }, - #Grants + #Special vocabulary/Awards (former Grants) #------------------------------------------------------------------------------------------ - #' @description Get Grants supported by Zenodo. + #' @description Get Grants supported by Zenodo. DEPRECATED: replaced by \code{getAwards} #' @param pretty Prettify the output. By default the argument \code{pretty} is set to #' \code{TRUE} which will returns the list of grants as \code{data.frame}. #' Set \code{pretty = FALSE} to get the raw list of grants @@ -348,49 +348,56 @@ ZenodoManager <- R6Class("ZenodoManager", #' @param size number of grants to be returned. By default equal to 500. #' @return list of grants as \code{data.frame} or \code{list} getGrants = function(q = "", pretty = TRUE, size = 500){ + self$WARN("Method 'getGrants' is deprecated, please use 'getAwards' instead!") + return(self$getAwards(q = q, pretty = pretty, size = size)) + }, + + #' @description Get Awards supported by Zenodo. + #' @param pretty Prettify the output. By default the argument \code{pretty} is set to + #' \code{TRUE} which will returns the list of awards as \code{data.frame}. + #' Set \code{pretty = FALSE} to get the raw list of awards + #' @param q an ElasticSearch compliant query, object of class \code{character}. Default is emtpy. + #' Note that the Zenodo API restrains a maximum number of 10,000 records to be retrieved. Consequently, + #' not all awards can be listed from Zenodo, a query has to be specified. + #' @param size number of awards to be returned. By default equal to 500. + #' @return list of awards as \code{data.frame} or \code{list} + getAwards = function(q = "", pretty = TRUE, size = 500){ page <- 1 - lastPage <- FALSE - zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("grants?q=%s&size=%s&page=%s", URLencode(q), size, page), + zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("awards?q=%s&size=%s&page=%s", URLencode(q), size, page), token = self$getToken(), logger = self$loggerType) zenReq$execute() out <- NULL if(zenReq$getStatus() == 200){ resp <- zenReq$getResponse() - grants <- resp$hits$hits + awards <- resp$hits$hits total <- resp$hits$total if(total > 10000){ self$WARN(sprintf("Total of %s records found: the Zenodo API limits to a maximum of 10,000 records!", total)) } total_remaining <- total - hasGrants <- length(grants)>0 - while(hasGrants){ - out <- c(out, grants) - if(!is.null(grants)){ - self$INFO(sprintf("Successfully fetched list of grants - page %s", page)) - if(q!=""){ - page <- page+1 #next - total_remaining <- total_remaining-length(grants) - }else{ - break; - } - }else{ - lastPage <- TRUE + hasAwards <- length(awards)>0 + while(hasAwards){ + out <- c(out, awards) + total_remaining <- total_remaining-length(awards) + if(total_remaining <= size) size = total_remaining + if(total_remaining == 0){ + break } - zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("grants/?q=%s&size=%s&page=%s", URLencode(q), size, page), + + #next page + page = page+1 + zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("awards?q=%s&size=%s&page=%s", URLencode(q), size, page), token = self$getToken(), logger = self$loggerType) zenReq$execute() if(zenReq$getStatus() == 200){ resp <- zenReq$getResponse() - grants <- resp$hits$hits - hasGrants <- length(grants)>0 - if(lastPage) break; + awards <- resp$hits$hits + hasAwards <- length(awards)>0 }else{ - self$WARN(sprintf("Maximum allowed size for list of grants - page %s - attempt to decrease size", page)) - size <- size-1 - hasGrants <- TRUE - grants <- NULL + self$WARN(sprintf("Maximum allowed size for list of awars at page %s", page)) + break } } self$INFO("Successfully fetched list of grants!") @@ -405,24 +412,14 @@ ZenodoManager <- R6Class("ZenodoManager", if(pretty){ out = do.call("rbind", lapply(out,function(x){ rec = data.frame( - id = x$metadata$internal_id, - code = x$metadata$code, - title = x$metadata$title, - startdate = x$metadata$startdate, - enddate = x$metadata$enddate, - url = x$metadata$url, + id = x$id, + number = x$number, + title = x$title[[1]], created = x$created, updated = x$updated, - funder_country = x$metadata$funder$country, - funder_doi = x$metadata$funder$doi, - funder_name = x$metadata$funder$name, - funder_type = x$metadata$funder$type, - funder_subtype = x$metadata$funder$subtype, - funder_parent_country = if(length(x$metadata$funder$parent)>0) x$metadata$funder$parent$country else NA, - funder_parent_doi = if(length(x$metadata$funder$parent)>0) x$metadata$funder$parent$doi else NA, - funder_parent_name = if(length(x$metadata$funder$parent)>0) x$metadata$funder$parent$name else NA, - funder_parent_type = if(length(x$metadata$funder$parent)>0) x$metadata$funder$parent$type else NA, - funder_parent_subtype = if(length(x$metadata$funder$parent)>0) x$metadata$funder$parent$subtype else NA, + funder_id = x$funder$id, + funder_name = x$funder$name, + program = if(!is.null(x$program)) x$program else NA, stringsAsFactors = FALSE ) return(rec) @@ -431,30 +428,49 @@ ZenodoManager <- R6Class("ZenodoManager", return(out) }, - #' @description Get grants by name. + #' @description Get grants by name. DEPRECATED: replaced by \code{getAwardByName} #' @param name name #' @param pretty Prettify the output. By default the argument \code{pretty} is set to #' \code{TRUE} which will returns the list of grants as \code{data.frame}. #' Set \code{pretty = FALSE} to get the raw list of grants #' @return list of grants as \code{data.frame} or \code{list} getGrantsByName = function(name, pretty = TRUE){ - query = sprintf("title:%s", URLencode(paste0("\"",name,"\""))) - self$getGrants(q = query, pretty = pretty) + self$WARN("Method 'getGrantsByName' is deprecated, please use 'getAwardsByName' instead!") + return(self$getAwardsByName(name = name, pretty = pretty)) }, - #' @description Get grant by Id. + #' @description Get awards by name. + #' @param name name + #' @param pretty Prettify the output. By default the argument \code{pretty} is set to + #' \code{TRUE} which will returns the list of awards as \code{data.frame}. + #' Set \code{pretty = FALSE} to get the raw list of awards + #' @return list of awards as \code{data.frame} or \code{list} + getAwardsByName = function(name, pretty = TRUE){ + query = sprintf("title.en:%s", URLencode(paste0("\"",name,"\""))) + self$getAwards(q = query, pretty = pretty) + }, + + #' @description Get grant by Id.DEPRECATED: replaced by \code{getAwardById} #' @param id grant id #' @return the grant getGrantById = function(id){ - zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("grants/%s",id), + self$WARN("Method 'getGrantById' is deprecated, please use 'getAwardById' instead!") + return(self$getAwardById(id)) + }, + + #' @description Get award by Id. + #' @param id award id + #' @return the award + getAwardById = function(id){ + zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("awards/%s",id), token= self$getToken(), logger = self$loggerType) zenReq$execute() out <- zenReq$getResponse() if(zenReq$getStatus() == 200){ - self$INFO(sprintf("Successfully fetched grant '%s'",id)) + self$INFO(sprintf("Successfully fetched award '%s'",id)) }else{ - self$ERROR(sprintf("Error while fetching grant '%s': %s", id, out$message)) + self$ERROR(sprintf("Error while fetching award '%s': %s", id, out$message)) out <- NULL } return(out) diff --git a/man/ZenodoManager.Rd b/man/ZenodoManager.Rd index 0bb8ccb..81c4841 100644 --- a/man/ZenodoManager.Rd +++ b/man/ZenodoManager.Rd @@ -99,8 +99,11 @@ Emmanuel Blondel \item \href{#method-ZenodoManager-getCommunities}{\code{ZenodoManager$getCommunities()}} \item \href{#method-ZenodoManager-getCommunityById}{\code{ZenodoManager$getCommunityById()}} \item \href{#method-ZenodoManager-getGrants}{\code{ZenodoManager$getGrants()}} +\item \href{#method-ZenodoManager-getAwards}{\code{ZenodoManager$getAwards()}} \item \href{#method-ZenodoManager-getGrantsByName}{\code{ZenodoManager$getGrantsByName()}} +\item \href{#method-ZenodoManager-getAwardsByName}{\code{ZenodoManager$getAwardsByName()}} \item \href{#method-ZenodoManager-getGrantById}{\code{ZenodoManager$getGrantById()}} +\item \href{#method-ZenodoManager-getAwardById}{\code{ZenodoManager$getAwardById()}} \item \href{#method-ZenodoManager-getFunders}{\code{ZenodoManager$getFunders()}} \item \href{#method-ZenodoManager-getFundersByName}{\code{ZenodoManager$getFundersByName()}} \item \href{#method-ZenodoManager-getFunderById}{\code{ZenodoManager$getFunderById()}} @@ -324,7 +327,7 @@ the community \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ZenodoManager-getGrants}{}}} \subsection{Method \code{getGrants()}}{ -Get Grants supported by Zenodo. +Get Grants supported by Zenodo. DEPRECATED: replaced by \code{getAwards} \subsection{Usage}{ \if{html}{\out{
}}\preformatted{ZenodoManager$getGrants(q = "", pretty = TRUE, size = 500)}\if{html}{\out{
}} } @@ -349,10 +352,38 @@ list of grants as \code{data.frame} or \code{list} } } \if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ZenodoManager-getAwards}{}}} +\subsection{Method \code{getAwards()}}{ +Get Awards supported by Zenodo. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ZenodoManager$getAwards(q = "", pretty = TRUE, size = 500)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{q}}{an ElasticSearch compliant query, object of class \code{character}. Default is emtpy. +Note that the Zenodo API restrains a maximum number of 10,000 records to be retrieved. Consequently, +not all awards can be listed from Zenodo, a query has to be specified.} + +\item{\code{pretty}}{Prettify the output. By default the argument \code{pretty} is set to +\code{TRUE} which will returns the list of awards as \code{data.frame}. +Set \code{pretty = FALSE} to get the raw list of awards} + +\item{\code{size}}{number of awards to be returned. By default equal to 500.} +} +\if{html}{\out{
}} +} +\subsection{Returns}{ +list of awards as \code{data.frame} or \code{list} +} +} +\if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ZenodoManager-getGrantsByName}{}}} \subsection{Method \code{getGrantsByName()}}{ -Get grants by name. +Get grants by name. DEPRECATED: replaced by \code{getAwardByName} \subsection{Usage}{ \if{html}{\out{
}}\preformatted{ZenodoManager$getGrantsByName(name, pretty = TRUE)}\if{html}{\out{
}} } @@ -373,10 +404,34 @@ list of grants as \code{data.frame} or \code{list} } } \if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ZenodoManager-getAwardsByName}{}}} +\subsection{Method \code{getAwardsByName()}}{ +Get awards by name. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ZenodoManager$getAwardsByName(name, pretty = TRUE)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{name}}{name} + +\item{\code{pretty}}{Prettify the output. By default the argument \code{pretty} is set to +\code{TRUE} which will returns the list of awards as \code{data.frame}. +Set \code{pretty = FALSE} to get the raw list of awards} +} +\if{html}{\out{
}} +} +\subsection{Returns}{ +list of awards as \code{data.frame} or \code{list} +} +} +\if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ZenodoManager-getGrantById}{}}} \subsection{Method \code{getGrantById()}}{ -Get grant by Id. +Get grant by Id.DEPRECATED: replaced by \code{getAwardById} \subsection{Usage}{ \if{html}{\out{
}}\preformatted{ZenodoManager$getGrantById(id)}\if{html}{\out{
}} } @@ -393,6 +448,26 @@ the grant } } \if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ZenodoManager-getAwardById}{}}} +\subsection{Method \code{getAwardById()}}{ +Get award by Id. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ZenodoManager$getAwardById(id)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{id}}{award id} +} +\if{html}{\out{
}} +} +\subsection{Returns}{ +the award +} +} +\if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ZenodoManager-getFunders}{}}} \subsection{Method \code{getFunders()}}{ diff --git a/tests/testthat/test_awards.R b/tests/testthat/test_awards.R new file mode 100644 index 0000000..3229108 --- /dev/null +++ b/tests/testthat/test_awards.R @@ -0,0 +1,24 @@ +# test_awards.R +# Author: Emmanuel Blondel +# +# Description: Unit tests for Zenodo awards operations +#======================= +require(zen4R, quietly = TRUE) +require(testthat) + +context("awards") + +test_that("awards are retrieved",{ + zenodo <- ZenodoManager$new(logger = "INFO") + zen_awards_df <- zenodo$getAwards() + expect_is(zen_awards_df, "data.frame") + zen_awards_raw <- zenodo$getAwards(pretty = FALSE) + expect_is(zen_awards_raw, "list") +}) + +test_that("grant is retrieved by id",{ + zenodo <- ZenodoManager$new(logger = "INFO") + zen_award <- zenodo$getAwardById("001aqnf71::2444954") + expect_equal(zen_award$id, "001aqnf71::2444954") + Sys.sleep(2) +}) \ No newline at end of file diff --git a/tests/testthat/test_grants.R b/tests/testthat/test_grants.R deleted file mode 100644 index d6b9ce8..0000000 --- a/tests/testthat/test_grants.R +++ /dev/null @@ -1,25 +0,0 @@ -# test_grants.R -# Author: Emmanuel Blondel -# -# Description: Unit tests for Zenodo grants operations -#======================= -require(zen4R, quietly = TRUE) -require(testthat) - -context("grants") - -#as of 2020-04-14 this method doesn't respond, to liaise with Zenodo team if it persists -test_that("grants are retrieved",{ - #zenodo <- ZenodoManager$new(logger = "INFO") - #zen_grants_df <- zenodo$getGrants() - #expect_is(zen_grants_df, "data.frame") - #zen_grants_raw <- zenodo$getGrants(pretty = FALSE) - #expect_is(zen_grants_raw, "list") -}) - -test_that("grant is retrieved by id",{ - zenodo <- ZenodoManager$new(logger = "INFO") - zen_grant <- zenodo$getGrantById("10.13039/100000002::5R01DK049482-03") - expect_equal(zen_grant$metadata$internal_id, "10.13039/100000002::5R01DK049482-03") - Sys.sleep(2) -}) \ No newline at end of file From 9a08f76163b733e96c0932a6ca2e4e5246b3b654 Mon Sep 17 00:00:00 2001 From: eblondel Date: Wed, 18 Oct 2023 10:50:00 +0200 Subject: [PATCH 10/25] #135 getFiles --- R/ZenodoManager.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/ZenodoManager.R b/R/ZenodoManager.R index a1bd194..dbbd16a 100644 --- a/R/ZenodoManager.R +++ b/R/ZenodoManager.R @@ -989,7 +989,7 @@ ZenodoManager <- R6Class("ZenodoManager", #' @param recordId the ID of the record. #' @return list of files getFiles = function(recordId){ - zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("deposit/depositions/%s/files", recordId), + zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("records/%s/draft/files", recordId), token = self$getToken(), logger = self$loggerType) zenReq$execute() From 77272fa5ac8f5e6fde722e3bd6b08ed23c41cee6 Mon Sep 17 00:00:00 2001 From: eblondel Date: Wed, 18 Oct 2023 10:55:54 +0200 Subject: [PATCH 11/25] #135 deleteFile --- R/ZenodoManager.R | 8 ++++---- man/ZenodoManager.Rd | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/R/ZenodoManager.R b/R/ZenodoManager.R index dbbd16a..432539a 100644 --- a/R/ZenodoManager.R +++ b/R/ZenodoManager.R @@ -1056,10 +1056,10 @@ ZenodoManager <- R6Class("ZenodoManager", #' @description Deletes a file for a record #' @param recordId ID of the record - #' @param fileId ID of the file to delete - deleteFile = function(recordId, fileId){ - zenReq <- ZenodoRequest$new(private$url, "DELETE", sprintf("deposit/depositions/%s/files", recordId), - data = fileId, token = self$getToken(), + #' @param filename name of the file to be deleted + deleteFile = function(recordId, filename){ + zenReq <- ZenodoRequest$new(private$url, "DELETE", sprintf("records/%s/draft/files", recordId), + data = filename, token = self$getToken(), logger = self$loggerType) zenReq$execute() out <- FALSE diff --git a/man/ZenodoManager.Rd b/man/ZenodoManager.Rd index 81c4841..9e11e0b 100644 --- a/man/ZenodoManager.Rd +++ b/man/ZenodoManager.Rd @@ -903,7 +903,7 @@ Uploads a file to a Zenodo record \subsection{Method \code{deleteFile()}}{ Deletes a file for a record \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ZenodoManager$deleteFile(recordId, fileId)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ZenodoManager$deleteFile(recordId, filename)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -911,7 +911,7 @@ Deletes a file for a record \describe{ \item{\code{recordId}}{ID of the record} -\item{\code{fileId}}{ID of the file to delete} +\item{\code{filename}}{name of the file to be deleted} } \if{html}{\out{}} } From 5bab9607dd9bab6efc54fdca17fda4a7fbf80d04 Mon Sep 17 00:00:00 2001 From: eblondel Date: Wed, 18 Oct 2023 12:01:02 +0200 Subject: [PATCH 12/25] #135 uploadFile --- R/ZenodoManager.R | 9 ++++++--- R/ZenodoRequest.R | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/R/ZenodoManager.R b/R/ZenodoManager.R index 432539a..5682af1 100644 --- a/R/ZenodoManager.R +++ b/R/ZenodoManager.R @@ -1023,8 +1023,9 @@ ZenodoManager <- R6Class("ZenodoManager", newapi <- FALSE } method <- if(newapi) "PUT" else "POST" - if(newapi) self$INFO(sprintf("Using new file upload API with bucket: %s", record$links$bucket)) - method_url <- if(newapi) sprintf("%s/%s", unlist(strsplit(record$links$bucket, "api/"))[2], URLencode(filename)) else sprintf("deposit/depositions/%s/files", recordId) + if(newapi) self$INFO("Using new file upload API with bucket") + method_url <- if(newapi) sprintf("records/%s/draft/files/%s/content", recordId, URLencode(filename)) else sprintf("deposit/depositions/%s/files", recordId) + print(method_url) zenReq <- if(newapi){ ZenodoRequest$new( private$url, method, method_url, @@ -1045,8 +1046,10 @@ ZenodoManager <- R6Class("ZenodoManager", zenReq$execute() out <- NULL if(zenReq$getStatus() == 201){ - out <- ZenodoRecord$new(obj = zenReq$getResponse()) self$INFO(sprintf("Successful uploaded file to record '%s'", recordId)) + rec_files = self$getFiles(recordId = recordId) + out = rec_files$entries[sapply(rec_files$entries, function(x){x$key == filename})][[1]] + return(out) }else{ out <- zenReq$getResponse() self$ERROR(sprintf("Error while uploading file to record '%s': %s", recordId, out$message)) diff --git a/R/ZenodoRequest.R b/R/ZenodoRequest.R index 5dc8b8b..fd0d12e 100644 --- a/R/ZenodoRequest.R +++ b/R/ZenodoRequest.R @@ -134,12 +134,12 @@ ZenodoRequest <- R6Class("ZenodoRequest", PUT = function(url, request, data, progress){ req <- paste(url, request, sep="/") - if(regexpr("api/files", req)<0) data <- private$prepareData(data) + if(regexpr("draft/files", req)<0) data <- private$prepareData(data) #headers headers <- c( "User-Agent" = private$agent, - "Content-Type" = if(regexpr("api/files", req)>0) "application/octet-stream" else "application/json", + "Content-Type" = if(regexpr("draft/files", req)>0) "application/octet-stream" else "application/json", "Authorization" = paste("Bearer",private$token) ) From cafc08952c69e7ad40cb5cd4e5310d8aca8c2661 Mon Sep 17 00:00:00 2001 From: eblondel Date: Wed, 18 Oct 2023 13:49:24 +0200 Subject: [PATCH 13/25] #135 advanced uploadFile, with startFileUpload and completeFileUpload support --- R/ZenodoManager.R | 89 ++++++++++++++++++++++++++++++++++++++++++-- R/ZenodoRequest.R | 6 ++- man/ZenodoManager.Rd | 60 ++++++++++++++++++++++++++--- 3 files changed, 144 insertions(+), 11 deletions(-) diff --git a/R/ZenodoManager.R b/R/ZenodoManager.R index 5682af1..ceac922 100644 --- a/R/ZenodoManager.R +++ b/R/ZenodoManager.R @@ -985,6 +985,9 @@ ZenodoManager <- R6Class("ZenodoManager", return(out) }, + #File management + #------------------------------------------------------------------------------------------ + #' @description Get list of files attached to a Zenodo record. #' @param recordId the ID of the record. #' @return list of files @@ -1004,11 +1007,65 @@ ZenodoManager <- R6Class("ZenodoManager", return(out) }, - #' @description Uploads a file to a Zenodo record + #' @description Start a file upload. The method will create a key for the file to be uploaded + #' This method is essentially for internal purpose, and is called directly in \code{uploadFile} + #' for user convenience and for backward compatibility with the legacy Zenodo API. + #' @param path Local path of the file + #' @param recordId ID of the record + startFileUpload = function(path, recordId){ + self$INFO(sprintf("Start upload procedure for file '%s'", path)) + fileparts <- unlist(strsplit(path,"/")) + filename <- fileparts[length(fileparts)] + + zenReq <- ZenodoRequest$new(private$url, "POST", sprintf("records/%s/draft/files", recordId), + data = list(list(key = filename)), + token = self$getToken(), + logger = self$loggerType) + zenReq$execute() + out <- FALSE + if(zenReq$getStatus() == 201){ + self$INFO(sprintf("Successfully started upload procedure for file '%s'", path)) + out <- TRUE + }else{ + self$ERROR(sprintf("Error while starting upload procedure for file '%s' in record %s: %s", + path, recordId, out$message)) + } + return(out) + }, + + #' @description Completes a file upload. The method will complete a file upload through a commit operation + #' This method is essentially for internal purpose, and is called directly in \code{uploadFile} + #' for user convenience and for backward compatibility with the legacy Zenodo API. + #' @param path Local path of the file + #' @param recordId ID of the record + completeFileUpload = function(path, recordId){ + self$INFO(sprintf("Complete upload procedure for file '%s'", path)) + fileparts <- unlist(strsplit(path,"/")) + filename <- fileparts[length(fileparts)] + + zenReq <- ZenodoRequest$new(private$url, "POST", sprintf("records/%s/draft/files/%s/commit", recordId, filename), + token = self$getToken(), + logger = self$loggerType) + zenReq$execute() + out <- FALSE + if(zenReq$getStatus() == 200){ + self$INFO(sprintf("Successfully completed upload procedure for file '%s'", path)) + out <- TRUE + }else{ + self$ERROR(sprintf("Error while completing upload procedure for file '%s' in record %s: %s", + path, recordId, out$message)) + } + return(out) + }, + + #' @description Uploads a file to a Zenodo record. With the new Zenodo Invenio RDM API, this method + #' internally calls \code{startFileUpload} to create a file record (with a filename key) at start, followed + #' by the actual file content upload. At this stage, the file upload is in "pending" status. At the end, + #' the function calls \code{completeFileUpload} to commit the file which status becomes "completed". #' @param path Local path of the file - #' @param record object of class \code{ZenodoRecord} #' @param recordId ID of the record. Deprecated, use \code{record} instead to take advantage of the new Zenodo bucket upload API. - uploadFile = function(path, record = NULL, recordId = NULL){ + #' @param record object of class \code{ZenodoRecord} + uploadFile = function(path, recordId = NULL, record = NULL){ newapi = TRUE if(!is.null(recordId)){ self$WARN("'recordId' argument is deprecated, please consider using 'record' argument giving an object of class 'ZenodoRecord'") @@ -1022,6 +1079,16 @@ ZenodoManager <- R6Class("ZenodoManager", self$WARN(sprintf("No bucket link for record id = %s. Revert to old file upload API", recordId)) newapi <- FALSE } + + #start upload (needed with new Invenio RDM API) + if(newapi){ + started = self$startFileUpload(path = path, recordId = recordId) + if(!started){ + return(NULL) + } + } + + #proceed with upload method <- if(newapi) "PUT" else "POST" if(newapi) self$INFO("Using new file upload API with bucket") method_url <- if(newapi) sprintf("records/%s/draft/files/%s/content", recordId, URLencode(filename)) else sprintf("deposit/depositions/%s/files", recordId) @@ -1054,10 +1121,24 @@ ZenodoManager <- R6Class("ZenodoManager", out <- zenReq$getResponse() self$ERROR(sprintf("Error while uploading file to record '%s': %s", recordId, out$message)) } + + #complete upload (needed with new Invenio RDM API) + if(newapi){ + completed = self$completeFileUpload(path = path, recordId = recordId) + if(!completed){ + self$WARN("File upload procedure completion failed, file is uploaded but remains in 'pending' status!") + }else{ + out$status = "completed" + } + } + return(out) }, - #' @description Deletes a file for a record + #' @description Deletes a file for a record. With the new Zenodo Invenio RDM API, if a file is + #' deleted although its status was pending, only the upload content is deleted, and the file upload + #' record (identified by a filename key) is kept. If the status was completed (with a file commit), + #' the file record is deleted. #' @param recordId ID of the record #' @param filename name of the file to be deleted deleteFile = function(recordId, filename){ diff --git a/R/ZenodoRequest.R b/R/ZenodoRequest.R index fd0d12e..ba0049e 100644 --- a/R/ZenodoRequest.R +++ b/R/ZenodoRequest.R @@ -47,8 +47,10 @@ ZenodoRequest <- R6Class("ZenodoRequest", data <- data[!sapply(data, is.null)] }else if(is(data, "list")){ meta <- data$metadata - if(!is.null(meta$prereserve_doi)) meta$prereserve_doi <- NULL - data <- list(metadata = meta) + if(!is.null(meta)){ + if(!is.null(meta$prereserve_doi)) meta$prereserve_doi <- NULL + data <- list(metadata = meta) + } } data <- as(toJSON(data, pretty=T, auto_unbox=T), "character") diff --git a/man/ZenodoManager.Rd b/man/ZenodoManager.Rd index 9e11e0b..65cff1f 100644 --- a/man/ZenodoManager.Rd +++ b/man/ZenodoManager.Rd @@ -122,6 +122,8 @@ Emmanuel Blondel \item \href{#method-ZenodoManager-discardChanges}{\code{ZenodoManager$discardChanges()}} \item \href{#method-ZenodoManager-publishRecord}{\code{ZenodoManager$publishRecord()}} \item \href{#method-ZenodoManager-getFiles}{\code{ZenodoManager$getFiles()}} +\item \href{#method-ZenodoManager-startFileUpload}{\code{ZenodoManager$startFileUpload()}} +\item \href{#method-ZenodoManager-completeFileUpload}{\code{ZenodoManager$completeFileUpload()}} \item \href{#method-ZenodoManager-uploadFile}{\code{ZenodoManager$uploadFile()}} \item \href{#method-ZenodoManager-deleteFile}{\code{ZenodoManager$deleteFile()}} \item \href{#method-ZenodoManager-getRecords}{\code{ZenodoManager$getRecords()}} @@ -877,12 +879,57 @@ list of files } } \if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ZenodoManager-startFileUpload}{}}} +\subsection{Method \code{startFileUpload()}}{ +Start a file upload. The method will create a key for the file to be uploaded +This method is essentially for internal purpose, and is called directly in \code{uploadFile} +for user convenience and for backward compatibility with the legacy Zenodo API. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ZenodoManager$startFileUpload(path, recordId)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{path}}{Local path of the file} + +\item{\code{recordId}}{ID of the record} +} +\if{html}{\out{
}} +} +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ZenodoManager-completeFileUpload}{}}} +\subsection{Method \code{completeFileUpload()}}{ +Completes a file upload. The method will complete a file upload through a commit operation +This method is essentially for internal purpose, and is called directly in \code{uploadFile} +for user convenience and for backward compatibility with the legacy Zenodo API. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ZenodoManager$completeFileUpload(path, recordId)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{path}}{Local path of the file} + +\item{\code{recordId}}{ID of the record} +} +\if{html}{\out{
}} +} +} +\if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ZenodoManager-uploadFile}{}}} \subsection{Method \code{uploadFile()}}{ -Uploads a file to a Zenodo record +Uploads a file to a Zenodo record. With the new Zenodo Invenio RDM API, this method +internally calls \code{startFileUpload} to create a file record (with a filename key) at start, followed +by the actual file content upload. At this stage, the file upload is in "pending" status. At the end, +the function calls \code{completeFileUpload} to commit the file which status becomes "completed". \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ZenodoManager$uploadFile(path, record = NULL, recordId = NULL)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ZenodoManager$uploadFile(path, recordId = NULL, record = NULL)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -890,9 +937,9 @@ Uploads a file to a Zenodo record \describe{ \item{\code{path}}{Local path of the file} -\item{\code{record}}{object of class \code{ZenodoRecord}} - \item{\code{recordId}}{ID of the record. Deprecated, use \code{record} instead to take advantage of the new Zenodo bucket upload API.} + +\item{\code{record}}{object of class \code{ZenodoRecord}} } \if{html}{\out{}} } @@ -901,7 +948,10 @@ Uploads a file to a Zenodo record \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ZenodoManager-deleteFile}{}}} \subsection{Method \code{deleteFile()}}{ -Deletes a file for a record +Deletes a file for a record. With the new Zenodo Invenio RDM API, if a file is +deleted although its status was pending, only the upload content is deleted, and the file upload +record (identified by a filename key) is kept. If the status was completed (with a file commit), +the file record is deleted. \subsection{Usage}{ \if{html}{\out{
}}\preformatted{ZenodoManager$deleteFile(recordId, filename)}\if{html}{\out{
}} } From a12fe11888dfa3ee863f73e05b9182ca104f863a Mon Sep 17 00:00:00 2001 From: eblondel Date: Wed, 18 Oct 2023 14:12:51 +0200 Subject: [PATCH 14/25] #135 getFile (metadata) --- R/ZenodoManager.R | 18 ++++++++++++++++++ man/ZenodoManager.Rd | 23 +++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/R/ZenodoManager.R b/R/ZenodoManager.R index ceac922..6216e7c 100644 --- a/R/ZenodoManager.R +++ b/R/ZenodoManager.R @@ -1007,6 +1007,24 @@ ZenodoManager <- R6Class("ZenodoManager", return(out) }, + #' @description Get a file record metadata. + #' @param recordId the ID of the record. + #' @param filename filename + #' @return the file metadata + getFile = function(recordId, filename){ + zenReq <- ZenodoRequest$new(private$url, "GET", sprintf("records/%s/draft/files/%s", recordId, filename), + token = self$getToken(), + logger = self$loggerType) + zenReq$execute() + out <- zenReq$getResponse() + if(zenReq$getStatus() == 200){ + self$INFO(sprintf("Successful fetched file metadata for record '%s' - filename '%s'", recordId, filename)) + }else{ + self$ERROR(sprintf("Error while fetching file(s) for record '%s': %s", recordId, out$message)) + } + return(out) + }, + #' @description Start a file upload. The method will create a key for the file to be uploaded #' This method is essentially for internal purpose, and is called directly in \code{uploadFile} #' for user convenience and for backward compatibility with the legacy Zenodo API. diff --git a/man/ZenodoManager.Rd b/man/ZenodoManager.Rd index 65cff1f..0fcfe73 100644 --- a/man/ZenodoManager.Rd +++ b/man/ZenodoManager.Rd @@ -122,6 +122,7 @@ Emmanuel Blondel \item \href{#method-ZenodoManager-discardChanges}{\code{ZenodoManager$discardChanges()}} \item \href{#method-ZenodoManager-publishRecord}{\code{ZenodoManager$publishRecord()}} \item \href{#method-ZenodoManager-getFiles}{\code{ZenodoManager$getFiles()}} +\item \href{#method-ZenodoManager-getFile}{\code{ZenodoManager$getFile()}} \item \href{#method-ZenodoManager-startFileUpload}{\code{ZenodoManager$startFileUpload()}} \item \href{#method-ZenodoManager-completeFileUpload}{\code{ZenodoManager$completeFileUpload()}} \item \href{#method-ZenodoManager-uploadFile}{\code{ZenodoManager$uploadFile()}} @@ -879,6 +880,28 @@ list of files } } \if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ZenodoManager-getFile}{}}} +\subsection{Method \code{getFile()}}{ +Get a file record metadata. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ZenodoManager$getFile(recordId, filename)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{recordId}}{the ID of the record.} + +\item{\code{filename}}{filename} +} +\if{html}{\out{
}} +} +\subsection{Returns}{ +the file metadata +} +} +\if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ZenodoManager-startFileUpload}{}}} \subsection{Method \code{startFileUpload()}}{ From 1b0cbce555c794aad96d2790f40d887d81720d95 Mon Sep 17 00:00:00 2001 From: eblondel Date: Fri, 20 Oct 2023 10:17:34 +0200 Subject: [PATCH 15/25] #133 deleteFile --- R/ZenodoManager.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/ZenodoManager.R b/R/ZenodoManager.R index 6216e7c..5c58247 100644 --- a/R/ZenodoManager.R +++ b/R/ZenodoManager.R @@ -869,8 +869,8 @@ ZenodoManager <- R6Class("ZenodoManager", #' @param recordId the ID of the record to be deleted #' @return \code{TRUE} if deleted, \code{FALSE} otherwise deleteRecord = function(recordId){ - zenReq <- ZenodoRequest$new(private$url, "DELETE", "deposit/depositions", - data = recordId, token = self$getToken(), + zenReq <- ZenodoRequest$new(private$url, "DELETE", sprintf("records/%s/draft", recordId), + token = self$getToken(), logger = self$loggerType) zenReq$execute() out <- FALSE From 0ce8e5b2b04c093447f8af10cb33453f904bdd74 Mon Sep 17 00:00:00 2001 From: eblondel Date: Tue, 31 Oct 2023 20:52:11 +0100 Subject: [PATCH 16/25] implement #136 --- R/ZenodoRecord.R | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/R/ZenodoRecord.R b/R/ZenodoRecord.R index 3d048b9..f49f20f 100644 --- a/R/ZenodoRecord.R +++ b/R/ZenodoRecord.R @@ -108,6 +108,13 @@ ZenodoRecord <- R6Class("ZenodoRecord", if(!is.null(obj)) private$fromList(obj) }, + #Invenio RDM API new methods + #--------------------------------------------------------------------------- + + + #legacy REST API methods (to be evaluated under Zenodo Invenio RDM migration) + #---------------------------------------------------------------------------- + #' @description Set prereserve_doi if \code{TRUE}, \code{FALSE} otherwise to create a record without #' prereserved DOI by Zenodo. By default, this method will be called to prereserve a DOI assuming #' the record created doesn't yet handle a DOI. To avoid prereserving a DOI call \code{$prereserveDOI(FALSE)} @@ -971,18 +978,18 @@ ZenodoRecord <- R6Class("ZenodoRecord", #' @return the writen file name (with extension) exportAs = function(format, filename, append_format = TRUE){ zenodo_url <- self$links$record_html - if(is.null(zenodo_url)) zenodo_url <- self$links$latest_html + if(is.null(zenodo_url)) zenodo_url <- self$links$self_html if(is.null(zenodo_url)){ stop("Ups, this record seems a draft, can't export metadata until it is published!") } metadata_export_url <- switch(format, - "BibTeX" = paste0(zenodo_url,"/export/hx"), + "BibTeX" = paste0(zenodo_url,"/export/bibtex"), "CSL" = paste0(zenodo_url,"/export/csl"), - "DataCite" = paste0(zenodo_url,"/export/dcite4"), - "DublinCore" = paste0(zenodo_url,"/export/xd"), - "DCAT" = paste0(zenodo_url,"/export/dcat"), + "DataCite" = paste0(zenodo_url,"/export/datacite-xml"), + "DublinCore" = paste0(zenodo_url,"/export/dublincore"), + "DCAT" = paste0(zenodo_url,"/export/dcat-ap"), "JSON" = paste0(zenodo_url,"/export/json"), - "JSON-LD" = paste0(zenodo_url,"/export/schemaorg_jsonld"), + "JSON-LD" = paste0(zenodo_url,"/export/json-ld"), "GeoJSON" = paste0(zenodo_url,"/export/geojson"), "MARCXML" = paste0(zenodo_url,"/export/xm"), NULL @@ -992,19 +999,11 @@ ZenodoRecord <- R6Class("ZenodoRecord", } fileext <- private$getExportFormatExtension(format) - - html <- xml2::read_html(metadata_export_url) - reference <- xml2::xml_find_all(html, ".//pre") - reference <- reference[1] - reference <- gsub("","",reference) - reference <- gsub("","",reference) - if(fileext %in% c("xml", "rdf")){ - reference <- gsub("<", "<", reference) - reference <- gsub(">", ">", reference) - } - destfile <- paste(paste0(filename, ifelse(append_format,paste0("_", format),"")), fileext, sep = ".") - writeChar(reference, destfile, eos = NULL) + req <- httr::GET( + url = metadata_export_url, + httr::write_disk(path = destfile, overwrite = TRUE) + ) return(destfile) }, From 17ae1edf3994cf64ebee1a90fdeb630dc68f6f0a Mon Sep 17 00:00:00 2001 From: eblondel Date: Tue, 31 Oct 2023 23:37:49 +0100 Subject: [PATCH 17/25] change sandbox api endpoint --- R/ZenodoManager.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/ZenodoManager.R b/R/ZenodoManager.R index 5c58247..62810a7 100644 --- a/R/ZenodoManager.R +++ b/R/ZenodoManager.R @@ -87,7 +87,7 @@ ZenodoManager <- R6Class("ZenodoManager", initialize = function(url = "https://zenodo.org/api", token = zenodo_pat(), sandbox = FALSE, logger = NULL, keyring_backend = 'env'){ super$initialize(logger = logger) - if(sandbox) url = "https://sandbox.zenodo.org/api" + if(sandbox) url = "https://zenodo-rdm-qa.web.cern.ch/api" private$url = url if(url == "https://sandbox.zenodo.org/api") self$sandbox = TRUE if(!is.null(token)) if(nzchar(token)){ From 6002f35bd1a56c07cef5be26745380a4881ae78f Mon Sep 17 00:00:00 2001 From: eblondel Date: Tue, 31 Oct 2023 23:38:36 +0100 Subject: [PATCH 18/25] #133 DELETE change for deleteFile --- R/ZenodoRequest.R | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/R/ZenodoRequest.R b/R/ZenodoRequest.R index ba0049e..9f4d384 100644 --- a/R/ZenodoRequest.R +++ b/R/ZenodoRequest.R @@ -169,7 +169,8 @@ ZenodoRequest <- R6Class("ZenodoRequest", }, DELETE = function(url, request, data){ - req <- paste(url, request, data, sep="/") + req <- paste(url, request, sep="/") + if(!is.null(data)) req <- paste(req, data, sep = "/") #headers headers <- c( "User-Agent" = private$agent, From d517044a0c0b92b8a0972fca53e294ecfab034df Mon Sep 17 00:00:00 2001 From: eblondel Date: Wed, 1 Nov 2023 16:14:35 +0100 Subject: [PATCH 19/25] #133 getDepositions --- R/ZenodoManager.R | 53 +++++++++++++++++++++++++++++--------------- man/ZenodoManager.Rd | 17 +++++++------- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/R/ZenodoManager.R b/R/ZenodoManager.R index 62810a7..f01ce94 100644 --- a/R/ZenodoManager.R +++ b/R/ZenodoManager.R @@ -593,14 +593,13 @@ ZenodoManager <- R6Class("ZenodoManager", #Depositions #------------------------------------------------------------------------------------------ - #' @description Get the list of Zenodo records deposited in your Zenodo workspace. By defaut - #' the list of depositions will be returned by page with a size of 10 results per - #' page (default size of the Zenodo API). The parameter \code{q} allows to specify - #' an ElasticSearch-compliant query to filter depositions (default query is empty - #' to retrieve all records). The argument \code{all_versions}, if set to TRUE allows - #' to get all versions of records as part of the depositions list. The argument \code{exact} - #' specifies that an exact matching is wished, in which case paginated search will be - #' disabled (only the first search page will be returned). + #' @description Get the list of Zenodo records deposited in your Zenodo workspace (user records). By default + #' the list of depositions will be returned by page with a size of 10 results per page (default size of + #' the Zenodo API). The parameter \code{q} allows to specify an ElasticSearch-compliant query to filter + #' depositions (default query is empty to retrieve all records). The argument \code{all_versions}, if set + #' to TRUE allows to get all versions of records as part of the depositions list. The argument \code{exact} + #' specifies that an exact matching is wished, in which case paginated search will be disabled (only the first + #' search page will be returned). #' Examples of ElasticSearch queries for Zenodo can be found at \href{https://help.zenodo.org/guides/search/}{https://help.zenodo.org/guides/search/}. #' @param q Elastic-Search-compliant query, as object of class \code{character}. Default is "" #' @param size number of depositions to be retrieved per request (paginated). Default is 10 @@ -608,16 +607,16 @@ ZenodoManager <- R6Class("ZenodoManager", #' @param exact object of class \code{logical} indicating if exact matching has to be applied. Default is \code{TRUE} #' @param quiet object of class \code{logical} indicating if logs have to skipped. Default is \code{FALSE} #' @return a list of \code{ZenodoRecord} - getDepositions = function(q = "", size = 10, all_versions = FALSE, exact = TRUE, + getDepositions = function(q = "", size = 10, all_versions = FALSE, exact = FALSE, quiet = FALSE){ page <- 1 - baseUrl <- "deposit/depositions" + baseUrl <- "user/records" #set in #72, now re-deactivated through #76 (due to Zenodo server-side changes) #if(!private$sandbox) baseUrl <- paste0(baseUrl, "/") req <- sprintf("%s?q=%s&size=%s&page=%s", baseUrl, URLencode(q), size, page) - if(all_versions) req <- paste0(req, "&all_versions=1") + if(all_versions) req <- paste0(req, "&allversions=1") zenReq <- ZenodoRequest$new(private$url, "GET", req, token = self$getToken(), logger = if(quiet) NULL else self$loggerType) @@ -625,10 +624,21 @@ ZenodoManager <- R6Class("ZenodoManager", out <- NULL if(zenReq$getStatus() == 200){ resp <- zenReq$getResponse() - hasRecords <- length(resp)>0 + records <- resp$hits$hits + total <- resp$hits$total + if(total > 10000){ + self$WARN(sprintf("Total of %s records found: the Zenodo API limits to a maximum of 10,000 records!", total)) + } + total_remaining <- total + hasRecords <- length(records)>0 while(hasRecords){ - out <- c(out, lapply(resp, ZenodoRecord$new)) - if(!quiet) self$INFO(sprintf("Successfully fetched list of depositions - page %s", page)) + out <- c(out, lapply(records, ZenodoRecord$new)) + if(!quiet) self$INFO(sprintf("Successfully fetched list of depositions (user records) - page %s", page)) + total_remaining <- total_remaining-length(records) + if(total_remaining <= size) size = total_remaining + if(total_remaining == 0){ + break + } if(exact){ hasRecords <- FALSE @@ -636,19 +646,26 @@ ZenodoManager <- R6Class("ZenodoManager", #next page <- page+1 nextreq <- sprintf("%s?q=%s&size=%s&page=%s", baseUrl, q, size, page) - if(all_versions) nextreq <- paste0(nextreq, "&all_versions=1") + if(all_versions) nextreq <- paste0(nextreq, "&allversions=1") zenReq <- ZenodoRequest$new(private$url, "GET", nextreq, token = self$getToken(), logger = if(quiet) NULL else self$loggerType) zenReq$execute() resp <- zenReq$getResponse() - hasRecords <- length(resp)>0 + if(zenReq$getStatus() == 200){ + resp <- zenReq$getResponse() + records <- resp$hits$hits + hasRecords <- length(records)>0 + }else{ + if(!quiet) self$WARN(sprintf("Maximum allowed size for list of depositions (user records) at page %s", page)) + break + } } } - if(!quiet) self$INFO("Successfully fetched list of depositions!") + if(!quiet) self$INFO("Successfully fetched list of depositions (user records)!") }else{ out <- zenReq$getResponse() - if(!quiet) self$ERROR(sprintf("Error while fetching depositions: %s", out$message)) + if(!quiet) self$ERROR(sprintf("Error while fetching depositions (user records): %s", out$message)) for(error in out$errors){ self$ERROR(sprintf("Error: %s - %s", error$field, error$message)) } diff --git a/man/ZenodoManager.Rd b/man/ZenodoManager.Rd index 0fcfe73..f99dad2 100644 --- a/man/ZenodoManager.Rd +++ b/man/ZenodoManager.Rd @@ -546,21 +546,20 @@ the funder \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ZenodoManager-getDepositions}{}}} \subsection{Method \code{getDepositions()}}{ -Get the list of Zenodo records deposited in your Zenodo workspace. By defaut - the list of depositions will be returned by page with a size of 10 results per - page (default size of the Zenodo API). The parameter \code{q} allows to specify - an ElasticSearch-compliant query to filter depositions (default query is empty - to retrieve all records). The argument \code{all_versions}, if set to TRUE allows - to get all versions of records as part of the depositions list. The argument \code{exact} - specifies that an exact matching is wished, in which case paginated search will be - disabled (only the first search page will be returned). +Get the list of Zenodo records deposited in your Zenodo workspace (user records). By default + the list of depositions will be returned by page with a size of 10 results per page (default size of + the Zenodo API). The parameter \code{q} allows to specify an ElasticSearch-compliant query to filter + depositions (default query is empty to retrieve all records). The argument \code{all_versions}, if set + to TRUE allows to get all versions of records as part of the depositions list. The argument \code{exact} + specifies that an exact matching is wished, in which case paginated search will be disabled (only the first + search page will be returned). Examples of ElasticSearch queries for Zenodo can be found at \href{https://help.zenodo.org/guides/search/}{https://help.zenodo.org/guides/search/}. \subsection{Usage}{ \if{html}{\out{
}}\preformatted{ZenodoManager$getDepositions( q = "", size = 10, all_versions = FALSE, - exact = TRUE, + exact = FALSE, quiet = FALSE )}\if{html}{\out{
}} } From fd0eba229eb04c4617519d8c7b088535825e345d Mon Sep 17 00:00:00 2001 From: eblondel Date: Wed, 1 Nov 2023 21:31:31 +0100 Subject: [PATCH 20/25] #137 owners cardinality N, revision, status, recid --- R/ZenodoRecord.R | 17 ++++++++++------- man/ZenodoRecord.Rd | 6 ++++-- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/R/ZenodoRecord.R b/R/ZenodoRecord.R index f49f20f..fe1a555 100644 --- a/R/ZenodoRecord.R +++ b/R/ZenodoRecord.R @@ -50,15 +50,16 @@ ZenodoRecord <- R6Class("ZenodoRecord", ) }) self$id = obj$id + self$recid = obj$recid self$links = obj$links self$metadata = obj$metadata self$modified = obj$modified - self$owner = obj$owner - self$record_id = obj$record_id + self$owners = obj$owners + self$status = obj$status self$state = obj$state self$submitted = obj$submitted self$title = obj$title - self$version = obj$version + self$revision = obj$revision if(!is.null(obj$stats)) self$stats = data.frame(obj$stats) } ), @@ -83,10 +84,12 @@ ZenodoRecord <- R6Class("ZenodoRecord", metadata = list(), #' @field modified record modification date modified = NULL, - #' @field owner record owner - owner = NULL, - #' @field record_id record_id - record_id = NULL, + #' @field owners record owners + owners = NULL, + #' @field recid recid + recid = NULL, + #' @field status record status + status = NULL, #' @field state record state state = NULL, #' @field submitted record submission status diff --git a/man/ZenodoRecord.Rd b/man/ZenodoRecord.Rd index 93dedb3..f0f76b5 100644 --- a/man/ZenodoRecord.Rd +++ b/man/ZenodoRecord.Rd @@ -49,9 +49,11 @@ Emmanuel Blondel \item{\code{modified}}{record modification date} -\item{\code{owner}}{record owner} +\item{\code{owners}}{record owners} -\item{\code{record_id}}{record_id} +\item{\code{recid}}{recid} + +\item{\code{status}}{record status} \item{\code{state}}{record state} From c72319d0475c8f950d984efefe8c7ca326e2d98b Mon Sep 17 00:00:00 2001 From: eblondel Date: Wed, 1 Nov 2023 21:42:21 +0100 Subject: [PATCH 21/25] #133 getRecords --- R/ZenodoManager.R | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/R/ZenodoManager.R b/R/ZenodoManager.R index f01ce94..45155c9 100644 --- a/R/ZenodoManager.R +++ b/R/ZenodoManager.R @@ -1213,7 +1213,7 @@ ZenodoManager <- R6Class("ZenodoManager", getRecords = function(q = "", size = 10, all_versions = FALSE, exact = FALSE){ page <- 1 req <- sprintf("records/?q=%s&size=%s&page=%s", URLencode(q), size, page) - if(all_versions) req <- paste0(req, "&all_versions=1") + if(all_versions) req <- paste0(req, "&allversions=1") zenReq <- ZenodoRequest$new(private$url, "GET_WITH_CURL", req, token = self$getToken(), logger = self$loggerType) @@ -1221,10 +1221,21 @@ ZenodoManager <- R6Class("ZenodoManager", out <- NULL if(zenReq$getStatus() == 200){ resp <- zenReq$getResponse() + records <- resp$hits$hits + total <- resp$hits$total + if(total > 10000){ + self$WARN(sprintf("Total of %s records found: the Zenodo API limits to a maximum of 10,000 records!", total)) + } + total_remaining <- total hasRecords <- length(resp)>0 while(hasRecords){ - out <- c(out, lapply(resp, ZenodoRecord$new)) + out <- c(out, lapply(records, ZenodoRecord$new)) self$INFO(sprintf("Successfully fetched list of published records - page %s", page)) + total_remaining <- total_remaining-length(records) + if(total_remaining <= size) size = total_remaining + if(total_remaining == 0){ + break + } if(exact){ hasRecords <- FALSE @@ -1232,13 +1243,19 @@ ZenodoManager <- R6Class("ZenodoManager", #next page <- page+1 nextreq <- sprintf("records/?q=%s&size=%s&page=%s", URLencode(q), size, page) - if(all_versions) nextreq <- paste0(nextreq, "&all_versions=1") + if(all_versions) nextreq <- paste0(nextreq, "&allversions=1") zenReq <- ZenodoRequest$new(private$url, "GET_WITH_CURL", nextreq, token = self$getToken(), logger = self$loggerType) zenReq$execute() - resp <- zenReq$getResponse() - hasRecords <- length(resp)>0 + if(zenReq$getStatus() == 200){ + resp <- zenReq$getResponse() + records <- resp$hits$hits + hasRecords <- length(records)>0 + }else{ + self$WARN(sprintf("Maximum allowed size for list of published records at page %s", page)) + break + } } } self$INFO("Successfully fetched list of published records!") From 1c40598347a6ff1e813fdbb295197489ee91b392 Mon Sep 17 00:00:00 2001 From: eblondel Date: Wed, 1 Nov 2023 21:46:15 +0100 Subject: [PATCH 22/25] #137 minor adjustments on revision (field), remove title --- R/ZenodoRecord.R | 7 ++----- man/ZenodoRecord.Rd | 4 +--- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/R/ZenodoRecord.R b/R/ZenodoRecord.R index fe1a555..1065ddd 100644 --- a/R/ZenodoRecord.R +++ b/R/ZenodoRecord.R @@ -58,7 +58,6 @@ ZenodoRecord <- R6Class("ZenodoRecord", self$status = obj$status self$state = obj$state self$submitted = obj$submitted - self$title = obj$title self$revision = obj$revision if(!is.null(obj$stats)) self$stats = data.frame(obj$stats) } @@ -94,10 +93,8 @@ ZenodoRecord <- R6Class("ZenodoRecord", state = NULL, #' @field submitted record submission status submitted = FALSE, - #' @field title record title - title = NULL, - #' @field version record version - version = NULL, + #' @field revision record revision + revision = NULL, #' @field stats stats stats = NULL, diff --git a/man/ZenodoRecord.Rd b/man/ZenodoRecord.Rd index f0f76b5..6d3a7fb 100644 --- a/man/ZenodoRecord.Rd +++ b/man/ZenodoRecord.Rd @@ -59,9 +59,7 @@ Emmanuel Blondel \item{\code{submitted}}{record submission status} -\item{\code{title}}{record title} - -\item{\code{version}}{record version} +\item{\code{revision}}{record revision} \item{\code{stats}}{stats} } From 139d4c6c1b4c398d192814b4a9f13db7e3660743 Mon Sep 17 00:00:00 2001 From: eblondel Date: Wed, 1 Nov 2023 22:18:48 +0100 Subject: [PATCH 23/25] #133 fix getRecords --- R/ZenodoManager.R | 29 ++++++++++++++++++++--------- man/ZenodoManager.Rd | 9 ++------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/R/ZenodoManager.R b/R/ZenodoManager.R index 45155c9..c49af85 100644 --- a/R/ZenodoManager.R +++ b/R/ZenodoManager.R @@ -607,7 +607,7 @@ ZenodoManager <- R6Class("ZenodoManager", #' @param exact object of class \code{logical} indicating if exact matching has to be applied. Default is \code{TRUE} #' @param quiet object of class \code{logical} indicating if logs have to skipped. Default is \code{FALSE} #' @return a list of \code{ZenodoRecord} - getDepositions = function(q = "", size = 10, all_versions = FALSE, exact = FALSE, + getDepositions = function(q = "", size = 10, all_versions = FALSE, exact = TRUE, quiet = FALSE){ page <- 1 baseUrl <- "user/records" @@ -1210,24 +1210,35 @@ ZenodoManager <- R6Class("ZenodoManager", #' @param exact object of class \code{logical} indicating if exact matching has to be applied. Default is \code{TRUE} #' @param quiet object of class \code{logical} indicating if logs have to skipped. Default is \code{FALSE} #' @return a list of \code{ZenodoRecord} - getRecords = function(q = "", size = 10, all_versions = FALSE, exact = FALSE){ + getRecords = function(q = "", size = 10, all_versions = FALSE, exact = TRUE){ page <- 1 - req <- sprintf("records/?q=%s&size=%s&page=%s", URLencode(q), size, page) + req <- sprintf("records?q=%s&size=10page=%s", URLencode(q), page) if(all_versions) req <- paste0(req, "&allversions=1") - zenReq <- ZenodoRequest$new(private$url, "GET_WITH_CURL", req, + zenReq <- ZenodoRequest$new(private$url, "GET", req, token = self$getToken(), - logger = self$loggerType) + logger = NULL) zenReq$execute() - out <- NULL + total = 0 if(zenReq$getStatus() == 200){ resp <- zenReq$getResponse() - records <- resp$hits$hits total <- resp$hits$total if(total > 10000){ self$WARN(sprintf("Total of %s records found: the Zenodo API limits to a maximum of 10,000 records!", total)) } + } + + req <- sprintf("records?q=%s&size=%s&page=%s", URLencode(q), size, page) + if(all_versions) req <- paste0(req, "&allversions=1") + zenReq <- ZenodoRequest$new(private$url, "GET_WITH_CURL", req, + token = self$getToken(), + logger = self$loggerType) + zenReq$execute() + out <- NULL + if(zenReq$getStatus() == 200){ + resp <- zenReq$getResponse() total_remaining <- total - hasRecords <- length(resp)>0 + records = resp + hasRecords <- length(records)>0 while(hasRecords){ out <- c(out, lapply(records, ZenodoRecord$new)) self$INFO(sprintf("Successfully fetched list of published records - page %s", page)) @@ -1242,7 +1253,7 @@ ZenodoManager <- R6Class("ZenodoManager", }else{ #next page <- page+1 - nextreq <- sprintf("records/?q=%s&size=%s&page=%s", URLencode(q), size, page) + nextreq <- sprintf("records?q=%s&size=%s&page=%s", URLencode(q), size, page) if(all_versions) nextreq <- paste0(nextreq, "&allversions=1") zenReq <- ZenodoRequest$new(private$url, "GET_WITH_CURL", nextreq, token = self$getToken(), diff --git a/man/ZenodoManager.Rd b/man/ZenodoManager.Rd index f99dad2..8f20ee5 100644 --- a/man/ZenodoManager.Rd +++ b/man/ZenodoManager.Rd @@ -559,7 +559,7 @@ Get the list of Zenodo records deposited in your Zenodo workspace (user records) q = "", size = 10, all_versions = FALSE, - exact = FALSE, + exact = TRUE, quiet = FALSE )}\if{html}{\out{}} } @@ -1001,12 +1001,7 @@ Get the list of Zenodo records. By defaut the list of records will be returned b paginated search will be disabled (only the first search page will be returned). Examples of ElasticSearch queries for Zenodo can be found at \href{https://help.zenodo.org/guides/search/}{https://help.zenodo.org/guides/search/}. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{ZenodoManager$getRecords( - q = "", - size = 10, - all_versions = FALSE, - exact = FALSE -)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{ZenodoManager$getRecords(q = "", size = 10, all_versions = FALSE, exact = TRUE)}\if{html}{\out{
}} } \subsection{Arguments}{ From 7ad7fca5814bed49e576240f6f46db7e7ffac9eb Mon Sep 17 00:00:00 2001 From: eblondel Date: Thu, 2 Nov 2023 15:17:53 +0100 Subject: [PATCH 24/25] implement #138 --- R/ZenodoRecord.R | 27 +++++++++++++++++++- man/ZenodoRecord.Rd | 60 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/R/ZenodoRecord.R b/R/ZenodoRecord.R index 1065ddd..12de1fd 100644 --- a/R/ZenodoRecord.R +++ b/R/ZenodoRecord.R @@ -36,6 +36,7 @@ ZenodoRecord <- R6Class("ZenodoRecord", ) }, fromList = function(obj){ + self$access = obj$access self$conceptdoi = obj$conceptdoi self$conceptrecid = obj$conceptrecid self$created = obj$created @@ -63,6 +64,11 @@ ZenodoRecord <- R6Class("ZenodoRecord", } ), public = list( + #' @field access access policies + access = list( + record = "public", + files = "public" + ), #' @field conceptdoi record Concept DOI (common to all record versions) conceptdoi = NULL, #' @field conceptrecid record concept id @@ -104,13 +110,31 @@ ZenodoRecord <- R6Class("ZenodoRecord", #' or "DEBUG" (for complete curl http calls logs) initialize = function(obj = NULL, logger = "INFO"){ super$initialize(logger = logger) - self$prereserveDOI(TRUE) if(!is.null(obj)) private$fromList(obj) }, #Invenio RDM API new methods #--------------------------------------------------------------------------- + #'@description Set the access policy for record, among values "public" (default) or "restricted" + #'@param access access policy ('public' or 'restricted') + setAccessPolicyRecord = function(access = c("public","resticted")){ + self$access$record = access + }, + #'@description Set the access policy for files, among values "public" (default) or "restricted" + #'@param access access policy ('public' or 'restricted') + setAccessPolicyFiles = function(access = c("public","resticted")){ + self$access$files = access + }, + + #'@description Set access policy embargo options + #'@param active whether embargo is active or not. Default is \code{FALSE} + #'@param until embargo date, object of class \code{Date}. Default is \code{NULL}. Must be provided if embargo is active + #'@param reason embargo reason, object of class \code{character}. Default is an empty string + setAccessPolicyEmbargo = function(active = FALSE, until = NULL, reason = ""){ + if(!is.null(until)) if(!is(until, "Date")) stop("Argument 'until' should be of class 'Date'") + self$access$embargo = list(active = active, until = until, reason = reason) + }, #legacy REST API methods (to be evaluated under Zenodo Invenio RDM migration) #---------------------------------------------------------------------------- @@ -1000,6 +1024,7 @@ ZenodoRecord <- R6Class("ZenodoRecord", fileext <- private$getExportFormatExtension(format) destfile <- paste(paste0(filename, ifelse(append_format,paste0("_", format),"")), fileext, sep = ".") + req <- httr::GET( url = metadata_export_url, httr::write_disk(path = destfile, overwrite = TRUE) diff --git a/man/ZenodoRecord.Rd b/man/ZenodoRecord.Rd index 6d3a7fb..b33d981 100644 --- a/man/ZenodoRecord.Rd +++ b/man/ZenodoRecord.Rd @@ -29,6 +29,8 @@ Emmanuel Blondel \section{Public fields}{ \if{html}{\out{
}} \describe{ +\item{\code{access}}{access policies} + \item{\code{conceptdoi}}{record Concept DOI (common to all record versions)} \item{\code{conceptrecid}}{record concept id} @@ -69,6 +71,9 @@ Emmanuel Blondel \subsection{Public methods}{ \itemize{ \item \href{#method-ZenodoRecord-new}{\code{ZenodoRecord$new()}} +\item \href{#method-ZenodoRecord-setAccessPolicyRecord}{\code{ZenodoRecord$setAccessPolicyRecord()}} +\item \href{#method-ZenodoRecord-setAccessPolicyFiles}{\code{ZenodoRecord$setAccessPolicyFiles()}} +\item \href{#method-ZenodoRecord-setAccessPolicyEmbargo}{\code{ZenodoRecord$setAccessPolicyEmbargo()}} \item \href{#method-ZenodoRecord-prereserveDOI}{\code{ZenodoRecord$prereserveDOI()}} \item \href{#method-ZenodoRecord-setDOI}{\code{ZenodoRecord$setDOI()}} \item \href{#method-ZenodoRecord-getConceptDOI}{\code{ZenodoRecord$getConceptDOI()}} @@ -195,6 +200,61 @@ or "DEBUG" (for complete curl http calls logs)} } } \if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ZenodoRecord-setAccessPolicyRecord}{}}} +\subsection{Method \code{setAccessPolicyRecord()}}{ +Set the access policy for record, among values "public" (default) or "restricted" +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ZenodoRecord$setAccessPolicyRecord(access = c("public", "resticted"))}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{access}}{access policy ('public' or 'restricted')} +} +\if{html}{\out{
}} +} +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ZenodoRecord-setAccessPolicyFiles}{}}} +\subsection{Method \code{setAccessPolicyFiles()}}{ +Set the access policy for files, among values "public" (default) or "restricted" +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ZenodoRecord$setAccessPolicyFiles(access = c("public", "resticted"))}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{access}}{access policy ('public' or 'restricted')} +} +\if{html}{\out{
}} +} +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-ZenodoRecord-setAccessPolicyEmbargo}{}}} +\subsection{Method \code{setAccessPolicyEmbargo()}}{ +Set access policy embargo options +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ZenodoRecord$setAccessPolicyEmbargo(active = FALSE, until = NULL, reason = "")}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{active}}{whether embargo is active or not. Default is \code{FALSE}} + +\item{\code{until}}{embargo date, object of class \code{Date}. Default is \code{NULL}. Must be provided if embargo is active} + +\item{\code{reason}}{embargo reason, object of class \code{character}. Default is an empty string} +} +\if{html}{\out{
}} +} +} +\if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ZenodoRecord-prereserveDOI}{}}} \subsection{Method \code{prereserveDOI()}}{ From f91c89941a0dd7968427c8fec56aa75c5c4cb95d Mon Sep 17 00:00:00 2001 From: eblondel Date: Thu, 2 Nov 2023 17:17:09 +0100 Subject: [PATCH 25/25] change pkg version --- DESCRIPTION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index c0cbe37..e420352 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: zen4R -Version: 0.9 -Date: 2023-09-04 +Version: 0.9.9000 +Date: 2023-11-02 Title: Interface to 'Zenodo' REST API Authors@R: c( person("Emmanuel", "Blondel", role = c("aut", "cre"), email = "emmanuel.blondel1@gmail.com", comment = c(ORCID = "0000-0002-5870-5762")),