Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

API ENDPOINT #191

Merged
merged 9 commits into from
Sep 26, 2024
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
Loading