Introduction
Imagine importing a set of photographs from a camera, smartphone, or scanner. Initially the files arrive as a flat collection with sequential filenames. During review, a person organises them into meaningful folders representing subjects, projects, or collections.
In this vignette we use nine tiny text files as stand-ins for
photographs. The example demonstrates how fscontext records
observations before and after this organisation, allowing the contextual
changes to be reconstructed
library(fscontext, quietly = TRUE)
library(dplyr, quietly = TRUE)
vignette_folder <- file.path(tempdir(), "vignette")
camera <- file.path(vignette_folder, "camera")
photos <- file.path(vignette_folder, "photos")
unlink(c(vignette_folder, camera, photos), recursive = TRUE)
dir.create(vignette_folder)
dir.create(camera)
dir.create(photos)
for (i in 1:9) {
writeLines(as.character(i),file.path(camera, paste0("P", i, ".txt")))
}
dir(camera)
#> [1] "P1.txt" "P2.txt" "P3.txt" "P4.txt" "P5.txt" "P6.txt" "P7.txt" "P8.txt"
#> [9] "P9.txt"Let’s take a snapshot as the files arrive from a camera or stored on a memory card.
before <- snapshot_storage(path = camera,
root = vignette_folder,
person_id = "photographer")
#> Starting scan_storage() on: C:/Users/DANIEL~1/AppData/Local/Temp/RtmpWgD6tF/vignette
#> Scanning 9 files.
#> Signatures computed. Detecting repositories and Git status...
#> Files scanned: 9
#> Files in Git repos: 0
#> Files tracked by Git: 0
#> Skipped approximately 0 inaccessible files
#> scan_storage completed in 0.07 seconds
#> Saved: C:\Users\DANIEL~1\AppData\Local\Temp\RtmpWgD6tF/vignette/camera/scan_local-storage_c_vignette_20260715-110646_e5d247.rdsSummarise the files:
summary_before <- readRDS(before) |>
dplyr::select(person_id, rel_path, stem, size)
print(summary_before)
#> person_id rel_path stem size
#> 1 photographer camera/P1.txt P1 3
#> 2 photographer camera/P2.txt P2 3
#> 3 photographer camera/P3.txt P3 3
#> 4 photographer camera/P4.txt P4 3
#> 5 photographer camera/P5.txt P5 3
#> 6 photographer camera/P6.txt P6 3
#> 7 photographer camera/P7.txt P7 3
#> 8 photographer camera/P8.txt P8 3
#> 9 photographer camera/P9.txt P9 3Now let’s organise the photos into three groups: photos that are made inside of the house, in the garden, and photos that are not worth keeping and designated for deleting.
dir.create(file.path(photos, "house"), recursive = TRUE)
dir.create(file.path(photos, "garden"))
dir.create(file.path(photos, "delete"))
file.rename(
file.path(camera, c("P1.txt","P2.txt","P5.txt")),
file.path(photos, "house",
c("P1.txt","P2.txt","P5.txt"))
)
#> [1] TRUE TRUE TRUE
file.rename(
file.path(camera,
c("P3.txt","P4.txt","P6.txt","P7.txt")),
file.path(photos, "garden",
c("P3.txt","P4.txt","P6.txt","P7.txt"))
)
#> [1] TRUE TRUE TRUE TRUE
file.rename(
file.path(camera,
c("P8.txt","P9.txt")),
file.path(photos, "delete",
c("P8.txt","P9.txt"))
)
#> [1] TRUE TRUEThe resulting file structure:
photos/
house/
P1.txt
P2.txt
P5.txt
garden/
P3.txt
P4.txt
P6.txt
P7.txt
delete/
P8.txt
P9.txt
The second snapshot records the reorganised collection. Although the files themselves have not changed, their contextual location within the filesystem has.
after <- snapshot_storage(path = photos,
root = vignette_folder,
person_id = "archivist")
#> Starting scan_storage() on: C:/Users/DANIEL~1/AppData/Local/Temp/RtmpWgD6tF/vignette
#> Scanning 10 files.
#> Signatures computed. Detecting repositories and Git status...
#> Files scanned: 10
#> Files in Git repos: 0
#> Files tracked by Git: 0
#> Skipped approximately 0 inaccessible files
#> scan_storage completed in 0.05 seconds
#> Saved: C:\Users\DANIEL~1\AppData\Local\Temp\RtmpWgD6tF/vignette/photos/scan_local-storage_c_vignette_20260715-110646_e5d247.rdsWe can see the new summary:
summary_after <- readRDS(after) |>
dplyr::select(person_id, rel_path, stem, size)
print(summary_after)
#> person_id rel_path
#> 1 archivist camera/scan_local-storage_c_vignette_20260715-110646_e5d247.rds
#> 2 archivist photos/delete/P8.txt
#> 3 archivist photos/delete/P9.txt
#> 4 archivist photos/garden/P3.txt
#> 5 archivist photos/garden/P4.txt
#> 6 archivist photos/garden/P6.txt
#> 7 archivist photos/garden/P7.txt
#> 8 archivist photos/house/P1.txt
#> 9 archivist photos/house/P2.txt
#> 10 archivist photos/house/P5.txt
#> stem size
#> 1 scan_local-storage_c_vignette_20260715-110646_e5d247 1119
#> 2 P8 3
#> 3 P9 3
#> 4 P3 3
#> 5 P4 3
#> 6 P6 3
#> 7 P7 3
#> 8 P1 3
#> 9 P2 3
#> 10 P5 3Now compare the two observations.
The files are matched using their filename (stem) and file size. The comparison reveals how each file moved from the original camera import into its new contextual location.
summary_path_change <- summary_before |>
rename(
rel_path_before = rel_path,
person_before = person_id
) |>
left_join(
summary_after |>
rename(
rel_path_after = rel_path,
person_after = person_id
),
by = c("stem", "size")
) |>
select(
stem, size,
person_before, rel_path_before,
rel_path_after, person_after
) |>
filter(grepl("txt$", rel_path_before))
print(summary_path_change)
#> stem size person_before rel_path_before rel_path_after person_after
#> 1 P1 3 photographer camera/P1.txt photos/house/P1.txt archivist
#> 2 P2 3 photographer camera/P2.txt photos/house/P2.txt archivist
#> 3 P3 3 photographer camera/P3.txt photos/garden/P3.txt archivist
#> 4 P4 3 photographer camera/P4.txt photos/garden/P4.txt archivist
#> 5 P5 3 photographer camera/P5.txt photos/house/P5.txt archivist
#> 6 P6 3 photographer camera/P6.txt photos/garden/P6.txt archivist
#> 7 P7 3 photographer camera/P7.txt photos/garden/P7.txt archivist
#> 8 P8 3 photographer camera/P8.txt photos/delete/P8.txt archivist
#> 9 P9 3 photographer camera/P9.txt photos/delete/P9.txt archivistFor example, photograph P1 was originally observed in the camera import and later observed in the house folder.
summary_path_change |>
filter(stem == "P1") |>
select(
person_before,
rel_path_before,
rel_path_after,
person_after
) |>
tidyr::unite(
col = "transition",
rel_path_before,
rel_path_after,
sep = " → "
)
#> person_before transition person_after
#> 1 photographer camera/P1.txt → photos/house/P1.txt archivistThe transition
camera/P1.txt → photos/house/P1.txt
records an observable change in filesystem context.
From the perspective of fscontext, this is evidence that
the file became part of the contextual group house. The package
deliberately does not infer why this happened. The folder may
represent the subject of the photograph, a temporary workspace, or
another organisational decision known only to the user.
The important point is that the filesystem itself records a human
curation activity. By comparing two observational snapshots,
fscontext reconstructs this activity as reproducible
evidence.
The resulting comparison can serve as the starting point for later
semantic review. A subsequent workflow may interpret the movement into
house, garden, or delete as
candidate semantic assertions, record the provenance of those
assertions, and subject them to human review before they become part of
a knowledge graph or archival description.
