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

Attachments #195

Merged
merged 2 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 48 additions & 4 deletions R/attachments.R
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
query_layer_attachments <- function(
x,
definition_expression = "1=1",
attachments_definition_expression = NULL,
object_ids = NULL,
global_ids = NULL,
attachment_types = NULL,
Expand All @@ -50,6 +51,11 @@ query_layer_attachments <- function(
# Ignored arguments for now:
# returnMetadata, size,
) {
check_string(definition_expression, allow_null = TRUE)
check_string(attachments_definition_expression, allow_null = TRUE)
check_character(global_ids, allow_null = TRUE)
# TODO validate that object_ids is a vector

# ensure that attachments are available.
if (!x[["hasAttachments"]]) {
cli::cli_abort("{.arg layer} does not support attachments.")
Expand All @@ -75,12 +81,21 @@ query_layer_attachments <- function(

req <- httr2::req_body_form(
b_req,
objectIds = object_ids,
globalIds = global_ids,
# TODO test these why aren't these working?!!
objectIds = paste(object_ids, collapse = ","),
# TODO create a test for this
globalIds = paste(global_ids, collapse = ","),
attachmentTypes = attachment_types,
# TODO document common examples:
# filter based on
# - date
# - common prefix
# - numeric value
definitionExpression = definition_expression,
attachmentsDefinitionExpression = attachments_definition_expression,
keywords = keywords,
returnUrl = TRUE,
returnMetadata = TRUE,
f = "json"
)

Expand All @@ -96,6 +111,11 @@ query_layer_attachments <- function(
#' @param x the attachmentGroups column from the query results
#' @noRd
unnest_attachment_groups <- function(x) {
if (is.null(x[["attachmentInfos"]])) {
cli::cli_alert_info("No attachments found.")
return(NULL)
}

n_elem <- vapply(x[["attachmentInfos"]], nrow, integer(1))
res <- cbind(
x[rep.int(1:nrow(x), n_elem), c("parentGlobalId", "parentObjectId")],
Expand Down Expand Up @@ -128,6 +148,7 @@ download_attachments <- function(
attachments,
out_dir,
...,
overwrite = FALSE,
.progress = TRUE,
token = arc_token()) {
# check that the input is a data frame with the appropriate types
Expand Down Expand Up @@ -195,9 +216,32 @@ download_attachments <- function(

# create the output path names
out_fps <- file.path(out_dir, attachments[["name"]])
already_exist <- file.exists(out_fps)

# required fields
urls <- attachments[["url"]]
content_types <- attachments[["contentType"]]

if (!overwrite && any(already_exist)) {
# Files with the same name found.
cli::cli_inform(
c(
"Files with the same name found in {.path {out_dir}}",
"i" = "Existing files: {.file {out_fps[already_exist]}}",
">" = "set {.arg overwrite = TRUE} to overwrite these files"
)
)
# subset to the files that dont already exist
out_fps <- out_fps[!already_exist]
urls <- urls[!already_exist]
content_types <- content_types[!already_exist]
}
# TODO check if files exist, if they do provide a message
# saying we're skipping downloading.
# To download use `overwrite = TRUE`

# create the requests
attachment_reqs <- lapply(attachments[["url"]], arc_base_req, token = token)
attachment_reqs <- lapply(urls, arc_base_req, token = token)

# perform the requests
resps <- httr2::req_perform_parallel(
Expand All @@ -207,7 +251,7 @@ download_attachments <- function(
)


Map(.download_attachment, resps, attachments[["contentType"]], out_fps)
Map(.download_attachment, resps, content_types, out_fps)
}

.download_attachment <- function(.resp, .content_type, .fp) {
Expand Down
61 changes: 61 additions & 0 deletions dev/attachments-demo.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# install the development version
# install.packages("pak")
# pak::pak("r-arcgis/arcgislayers")

# for authorizing to your portal
library(arcgisutils)

# for accessing feature service content
library(arcgislayers)

# authorize
set_arc_token(auth_user())

# URL to the Survey123 feature service with attachments
furl <- "https://services1.arcgis.com/hLJbHVT9ZrDIzK0I/arcgis/rest/services/v8_Wide_Area_Search_Form_Feature_Layer___a2fe9c/FeatureServer/0"

# create a reference to it in R
layer <- arc_open(furl)

# There are two parts to working with attachments:
# 1. Finding the attachments associated with a feature service
# 2. After finding the attachments, we can download them to our machine

# Query the attachments of a layer using query_layer_attachments()
# By default this returns metadata about every attachment in your
# feature service
att <- query_layer_attachments(layer)
att

# We can limit this by providing a SQL where clause to the
# definition_expression argument.
# For example to find all of the attachments that have the
# followup_status field set to `needs_followup` we can provide the
# following definition_expression
query_layer_attachments(
layer, "followup_status = 'needs_followup'"
)

# To find all of the attachments from after a point in time
query_layer_attachments(
layer, "start_time >= '2023-01-01'"
)

# To find attachments with a name that starts with `image0`
query_layer_attachments(
layer,
attachments_definition_expression = "att_name like 'image0%'"
)

# Find attachments that contain "20221005"
query_layer_attachments(
layer,
attachments_definition_expression = "att_name like '%20221005%'"
)


# to download the attachments:
# res <- download_attachments(
# att,
# "dev/field_images"
# )
Loading