Hi @spono,
Since you mention ODK Collect, the data structure of submissions while still on the device is one folder for each submission, with attachments nested within. So you have all media attachments neatly kept together with the original submission XML.
Once you upload the submissions to ODK Central, the submissions will contain the image (or other media) filename as value of a media field. Unless there are duplicate filenames, this is all the linkage you need.
Since you mention R, I assume you use ruODK::odata_submission_get. Here, the media will be downloaded automatically for you to a destination folder, and the value (initially just the filename) will be adjusted to the absolute path to that file. The adjustment of the media filenames is an ruODK thing.
Caveat: odata_submission_get
's default download destination for media files is here::here("media")
, which depends on which user runs that script.
If you run odata_submission_get
yourself from within an RStudio project, the destination will be relative to your project's workdir.
If you run the download e.g. from a cronjob (example) inside a Docker image (example), the script will be run by user root
and the default destination will be /root/media
.
I will give you two examples of what to do with the media files.
Embed photos in a table
You can subsequently use that path to link directly to the image, e.g. embedded in a reactable
.
You can see a real-world implementation of this here.
I will annotate my example a bit, although this is out of scope of the original question:
-
basename(value)
is the filename of the actual submission. -
coldef_img
builds a clickable image thumbnail. - This example assumes a dataframe
mydata
with photos in columnsphoto1
andphoto2
. - I rebuild the image filepaths with
fs::path("media", basename(value))
as relative paths so they work in a Shiny app. - In a Shiny app, you must set the resourcePath or Shiny will not find the local images even if the path is right. Example:
add_resource_path("media", app_sys("inst/media"))
means:- Relative to the workdir, media attachment files are stored in a folder
inst/media
. Usinginst
is an R package convention to provide non-standard file resources. - Files in
inst/media
are now available to the Shiny app under the relative pathmedia
.
- Relative to the workdir, media attachment files are stored in a folder
coldef_img <- reactable::colDef(
cell = function(value) {
img_src <- fs::path("media", basename(value))
image <- tags$img(src = img_src, width = 100, height = 100)
tags$a(href = img_src, target = "_", image)
}
)
mydata %>%
reactable::reactable(
searchable = TRUE,
sortable = TRUE,
filterable = TRUE,
columns = list(
photo1 = coldef_img,
photo2 = coldef_img
# your photos here
)
)
Embed photos in a map popup
See https://docs.ropensci.org/ruODK/articles/odata-api.html#map for a working example.
You see that the work to link media attachments as images in the map popup happens in the definition of the popup.
So I agree, turning a spreadsheet or table with file paths and a bunch of media files in a folder into something visually appealing takes some effort.
Of course, all of the above is biased through my own experiences and work context.
I'd be interested to hear your story!
Do I understand you correctly that you want more natural foreign keys in the media filenames, such as submission ID?
How would a good use case look in your context?