Skip to content

Commit

Permalink
Merge pull request #191 from rte-antares-rpackage/add/ant1895
Browse files Browse the repository at this point in the history
API ENDPOINT FOR STORAGE
  • Loading branch information
vargastat authored Sep 26, 2024
2 parents b3fe199 + 1d7986e commit 92f5927
Show file tree
Hide file tree
Showing 13 changed files with 304 additions and 394 deletions.
4 changes: 3 additions & 1 deletion NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ S3method(print,antares.api.logs)
export(activateRES)
export(activateST)
export(adequacyOptions)
export(api_patch)
export(backupStudy)
export(checkRemovedArea)
export(cleanUpOutput)
Expand Down Expand Up @@ -106,7 +107,6 @@ importFrom(antaresRead,getAreas)
importFrom(antaresRead,getLinks)
importFrom(antaresRead,readBindingConstraints)
importFrom(antaresRead,readClusterDesc)
importFrom(antaresRead,readClusterSTDesc)
importFrom(antaresRead,readIni)
importFrom(antaresRead,readIniFile)
importFrom(antaresRead,readInputTS)
Expand Down Expand Up @@ -134,6 +134,7 @@ importFrom(data.table,year)
importFrom(doFuture,registerDoFuture)
importFrom(future,plan)
importFrom(grDevices,col2rgb)
importFrom(httr,PATCH)
importFrom(httr,POST)
importFrom(httr,accept_json)
importFrom(httr,add_headers)
Expand All @@ -153,6 +154,7 @@ importFrom(plyr,llply)
importFrom(stats,as.formula)
importFrom(stats,sd)
importFrom(stats,setNames)
importFrom(utils,URLencode)
importFrom(utils,getFromNamespace)
importFrom(utils,head)
importFrom(utils,modifyList)
Expand Down
6 changes: 4 additions & 2 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,22 @@
* `removeArea()` : send a warning instead of a stop if an area is referenced in a binding constraint coefficient
* `removeLink()` : send a warning instead of a stop if a link is referenced in a binding constraint coefficient
* `removeCluster()` : send a warning instead of a stop if a cluster is referenced in a binding constraint coefficient
* `createClusterST()` : updated with new endpoint API (POST + PUT)
* `editClusterST()` : updated with new endpoint API (PATCH + PUT)
* `removeCluster()`/`removeClusterRES()`/`removeClusterST()` updated with new endpoint API (DELETE)

NEW FEATURES (Antares v8.8) :

* `updateOptimizationSettings()` allows the user to update solver.log property
* `createClusterST()` / `editClusterST()` use new parameters and default values
* Add new function `api_patch()` to put PATCH (httr) request to API


BUGFIXES :

* `createBindingConstraint()` in API mode (for study <v870) created with "hourly" timeStep all the time
* `createBindingConstraint()` / `editBindingConstraint()` in TEXT mode, bad values in time series
* `createBindingConstraintBulk()` with no VALUES and with a mix
* side effects with `readClusterDesc()` / `readClusterResDesc()` / `readClusterSTDesc()`
* Fix bug for data.table to ensure that the variable name is not a column name in `check_cluster_name()` (API + DISK) and `createClusterST()`(API)
* Enable control of matrix dimension in `.check_bulk_object_dim()` even if the values are not in first position in the list
* `editLink()` : avoid *NULL* value (default) for arguments *filter_synthesis* and *filter_year_by_year* to write an empty string
* `updateOutputSettings()` : in API mode, allow the user to edit the desired property
Expand Down
76 changes: 76 additions & 0 deletions R/API-utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -251,3 +251,79 @@ transform_name_to_id <- function(name, lower = TRUE, id_dash = FALSE) {
return(valid_id)
}


#' API methods
#'
#' @param opts Antares simulation options or a `list` with an `host = ` slot.
#' @param endpoint API endpoint to interrogate, it will be added after `default_endpoint`.
#' Can be a full URL (by wrapping ìn [I()]), in that case `default_endpoint` is ignored.
#' @param ... Additional arguments passed to API method ([httr::PATCH()]).
#' @param default_endpoint Default endpoint to use.
#'
#' @return Response from the API.
#' @export
#'
#' @importFrom httr PATCH accept_json stop_for_status content add_headers
#' @importFrom utils URLencode
#'
#' @examples
#' \dontrun{
#' # Simple example to update st-storages properties
#'
#' # read existing study
#' opts <- setSimulationPath("path_to_the_study", "input")
#'
#' # make list of properties
#' prop <- list(efficiency = 0.5,
#' reservoircapacity = 350,
#' initialleveloptim = TRUE)
#'
#' # convert to JSON
#' body <- jsonlite::toJSON(prop,
#' auto_unbox = TRUE)
#'
#' # send to server (see /apidoc)
#' api_patch(opts = opts,
#' endpoint = file.path(opts$study_id,
#' "areas",
#' area,
#' "storages",
#' cluster_name),
#' body = body,
#' encode = "raw")
#'
#' }
api_patch <- function(opts, endpoint, ..., default_endpoint = "v1/studies") {
if (inherits(endpoint, "AsIs")) {
opts$host <- endpoint
endpoint <- NULL
default_endpoint <- NULL
}

if (is.null(opts$host))
stop("No host provided in `opts`: use a valid simulation options object or explicitly provide a host with opts = list(host = ...)")

if (!is.null(opts$token) && opts$token != "")
config <- add_headers(Authorization = paste("Bearer ",
opts$token),
Accept = "application/json")
else
config <- add_headers(Accept = "application/json")

# send request
result <- PATCH(
url = URLencode(paste(c(opts$host, default_endpoint, endpoint), collapse = "/")),
config = config,
...
)

# manage response
api_content <- content(result)
if(!is.null(names(api_content)))
api_content <- paste0("\n[Description] : ", api_content$description,
"\n[Exception] : ", api_content$exception)
else
api_content <- NULL
stop_for_status(result, task = api_content)
content(result)
}
117 changes: 66 additions & 51 deletions R/createClusterST.R
Original file line number Diff line number Diff line change
Expand Up @@ -175,63 +175,78 @@ createClusterST <- function(area,
# format name for API
cluster_name <- transform_name_to_id(cluster_name)

# /!\ temporary solution /!\
# as the endpoint does not return an error if the cluster already exist
if(!is_api_mocked(opts)){
exists <- FALSE
suppressWarnings(
clusters <- readClusterSTDesc(opts = opts)
)
if (nrow(clusters) > 0) {
area_filter <- area
clusters_filtered <- clusters[clusters$area == tolower(area_filter) &
clusters$cluster == cluster_name,]
exists <- nrow(clusters_filtered) > 0
}
if(exists)
stop("Cluster already exists. Edit it with editClusterST().")
}
##
# POST only for properties (creation with default TS values)
##

params_cluster$name <- cluster_name
# adapt parameter names
list_properties <- list("group" = params_cluster[["group"]],
"name" = cluster_name,
"injectionNominalCapacity" = params_cluster[["injectionnominalcapacity"]],
"withdrawalNominalCapacity" = params_cluster[["withdrawalnominalcapacity"]],
"reservoirCapacity" = params_cluster[["reservoircapacity"]],
"efficiency" = params_cluster[["efficiency"]],
"initialLevel" = params_cluster[["initiallevel"]],
"initialLevelOptim" = params_cluster[["initialleveloptim"]],
"enabled" = params_cluster[["enabled"]])

cmd <- api_command_generate(
action = "create_st_storage",
area_id = area,
parameters = params_cluster
)
list_properties <- dropNulls(list_properties)

api_command_register(cmd, opts = opts)
`if`(
should_command_be_executed(opts),
api_command_execute(cmd, opts = opts, text_alert = "{.emph create_st_storage}: {msg_api}"),
cli_command_registered("create_st_storage")
)
# make json file
body <- jsonlite::toJSON(list_properties,
auto_unbox = TRUE)

# send request (without coeffs/term)
result <- api_post(opts = opts,
endpoint = file.path(opts$study_id,
"areas",
area,
"storages"),
body = body,
encode = "raw")

cli::cli_alert_success("Endpoint {.emph {'Create ST-storage (properties)'}} {.emph
{.strong {cluster_name}}} success")

for (i in names(storage_value)){
if (!is.null(get(i))) {
# format name for API
data_param_name <- transform_name_to_id(storage_value[[i]]$string,
id_dash = TRUE)
##
# PUT api call for each TS value
##

# adapt list name TS
list_value_ts <- list(pmax_injection = PMAX_injection,
pmax_withdrawal = PMAX_withdrawal,
inflows = inflows,
lower_rule_curve = lower_rule_curve,
upper_rule_curve = upper_rule_curve)

list_value_ts <- dropNulls(list_value_ts)

if(length(list_value_ts)!=0){
lapply(names(list_value_ts), function(x){
body = jsonlite::toJSON(list(data=list_value_ts[[x]],
index=0,
columns=0),
auto_unbox = FALSE)

currPath <- paste0("input/st-storage/series/%s/%s/",data_param_name)
cmd <- api_command_generate(
action = "replace_matrix",
target = sprintf(currPath, area, cluster_name),
matrix = get(i)
)
api_command_register(cmd, opts = opts)
`if`(
should_command_be_executed(opts),
api_command_execute(cmd,
opts = opts,
text_alert = paste0("Writing ",
i,
" cluster's series: {msg_api}")),
cli_command_registered("replace_matrix")
)
}
endpoint <- file.path(opts$study_id,
"areas",
area,
"storages",
cluster_name,
"series",
x)

# update
api_put(opts = opts,
endpoint = endpoint,
body = body,
encode = "raw")

cli::cli_alert_success("Endpoint {.emph {'Create ST-storage (TS value)'}} {.emph
{.strong {x}}} success")
})

}

return(invisible(opts))
}
########################## -
Expand Down
119 changes: 66 additions & 53 deletions R/editClusterST.R
Original file line number Diff line number Diff line change
Expand Up @@ -91,65 +91,78 @@ editClusterST <- function(area,
# format name for API
cluster_name <- transform_name_to_id(cluster_name)

# /!\ temporary solution /!\
# as the endpoint does not return an error if the cluster does not exist
if(!is_api_mocked(opts)){
exists <- FALSE
suppressWarnings(
clusters <- readClusterSTDesc(opts = opts)
)
if (nrow(clusters) > 0) {
clusters_filtered <- clusters[clusters$area == tolower(area) &
clusters$cluster == cluster_name,]
exists <- nrow(clusters_filtered) > 0
}
assertthat::assert_that(exists,
msg = paste0("Cluster '",
cluster_name,
"' does not exist. It can not be edited."))
}
##
# PATCH for properties
##
# adapt parameter names
list_properties <- list("group" = params_cluster[["group"]],
"name" = cluster_name,
"injectionNominalCapacity" = params_cluster[["injectionnominalcapacity"]],
"withdrawalNominalCapacity" = params_cluster[["withdrawalnominalcapacity"]],
"reservoirCapacity" = params_cluster[["reservoircapacity"]],
"efficiency" = params_cluster[["efficiency"]],
"initialLevel" = params_cluster[["initiallevel"]],
"initialLevelOptim" = params_cluster[["initialleveloptim"]],
"enabled" = params_cluster[["enabled"]])

# update parameters if something else than name
if (length(params_cluster) > 1) {
currPath <- "input/st-storage/clusters/%s/list/%s"
writeIni(
listData = params_cluster,
pathIni = sprintf(currPath, area, cluster_name),
opts = opts
)
list_properties <- dropNulls(list_properties)

if(length(list_properties)>1){
# make json file
body <- jsonlite::toJSON(list_properties,
auto_unbox = TRUE)

# send request (without coeffs/term)
result <- api_patch(opts = opts,
endpoint = file.path(opts$study_id,
"areas",
area,
"storages",
cluster_name),
body = body,
encode = "raw")

cli::cli_alert_success("Endpoint {.emph {'Edit ST-storage (properties)'}} {.emph
{.strong {cluster_name}}} success")
}

# update data
names_data_params <- c("PMAX_injection",
"PMAX_withdrawal",
"inflows",
"lower_rule_curve",
"upper_rule_curve")
##
# PUT for TS values
##
# adapt list name TS
list_value_ts <- list(pmax_injection = PMAX_injection,
pmax_withdrawal = PMAX_withdrawal,
inflows = inflows,
lower_rule_curve = lower_rule_curve,
upper_rule_curve = upper_rule_curve)

for (i in names_data_params){
if (!is.null(get(i))) {
# format name for API
data_param_name <- transform_name_to_id(i, id_dash = TRUE)
list_value_ts <- dropNulls(list_value_ts)

if(length(list_value_ts)!=0){
lapply(names(list_value_ts), function(x){
body = jsonlite::toJSON(list(data=list_value_ts[[x]],
index=0,
columns=0),
auto_unbox = FALSE)

endpoint <- file.path(opts$study_id,
"areas",
area,
"storages",
cluster_name,
"series",
x)

currPath <- paste0("input/st-storage/series/%s/%s/",data_param_name)
cmd <- api_command_generate(
action = "replace_matrix",
target = sprintf(currPath, area, cluster_name),
matrix = get(i)
)
api_command_register(cmd, opts = opts)
`if`(
should_command_be_executed(opts),
api_command_execute(cmd,
opts = opts,
text_alert = paste0("Update ",
i,
" cluster's series: {msg_api}")),
cli_command_registered("replace_matrix")
)
}
# update
api_put(opts = opts,
endpoint = endpoint,
body = body,
encode = "raw")

cli::cli_alert_success("Endpoint {.emph {'Edit ST-storage (TS value)'}} {.emph
{.strong {x}}} success")
})
}

return(invisible(opts))
}
#####-
Expand Down
Loading

0 comments on commit 92f5927

Please sign in to comment.