From 194354a3b42de0b59aa74ff8b5a35a1b7f8f51d6 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sun, 14 Jun 2026 16:55:08 +0300 Subject: om0 add stats aggregation module Add src/lib/stats.source.sh implementing the aggregation layer for the stats site feature. collect_photo_exif_stats() iterates the album's incoming photos, reads each one's cached identify -verbose output via album.source.sh's cache helper, and fills global STATS_* associative arrays for the camera leaderboard, per-camera photo lists, temporal, exposure, dimension, format and enum stats. Builds exactly the v1 stat set recommended by docs/stats-exif-audit.md (rendering is left to the sibling tasks pm0/rm0/um0). Wire stats.source.sh into the Justfile LIB_SOURCES list (after album, before config) and regenerate bin/shuriken. Add unit tests exercising synthetic identify fixtures, rational/bucket boundaries, DateTimeOriginal substring parsing, missing-field tolerance, and the cached-output path. Co-Authored-By: Claude Opus 4.8 --- Justfile | 2 +- bin/shuriken | 514 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lib/stats.source.sh | 512 +++++++++++++++++++++++++++++++++++++++++++++++ tests/cli.sh | 176 +++++++++++++++++ 4 files changed, 1203 insertions(+), 1 deletion(-) create mode 100644 src/lib/stats.source.sh diff --git a/Justfile b/Justfile index 841cfb8..68bc63c 100644 --- a/Justfile +++ b/Justfile @@ -7,7 +7,7 @@ PREFIX := env_var_or_default("PREFIX", "/usr") BINDIR := env_var_or_default("BINDIR", PREFIX + "/bin") DATADIR := env_var_or_default("DATADIR", PREFIX + "/share") SYSCONFDIR := env_var_or_default("SYSCONFDIR", "/etc/default") -LIB_SOURCES := "src/lib/bootstrap.source.sh src/lib/imagemagick.source.sh src/lib/process.source.sh src/lib/archive.source.sh src/lib/template.source.sh src/lib/job-pool.source.sh src/lib/image.source.sh src/lib/random.source.sh src/lib/album.source.sh src/lib/config.source.sh src/lib/config.print.source.sh src/lib/config.sync.source.sh src/lib/config.staging.source.sh src/lib/config.validate.source.sh src/lib/config.cli.source.sh src/lib/action.source.sh" +LIB_SOURCES := "src/lib/bootstrap.source.sh src/lib/imagemagick.source.sh src/lib/process.source.sh src/lib/archive.source.sh src/lib/template.source.sh src/lib/job-pool.source.sh src/lib/image.source.sh src/lib/random.source.sh src/lib/album.source.sh src/lib/stats.source.sh src/lib/config.source.sh src/lib/config.print.source.sh src/lib/config.sync.source.sh src/lib/config.staging.source.sh src/lib/config.validate.source.sh src/lib/config.cli.source.sh src/lib/action.source.sh" default: build diff --git a/bin/shuriken b/bin/shuriken index 06514b1..4fd127f 100755 --- a/bin/shuriken +++ b/bin/shuriken @@ -2726,6 +2726,520 @@ print_dry_run_plan() { fi } +# Inlined from src/lib/stats.source.sh +# Stats aggregation for the album stats page (feature plan: +# /home/paul/.pi/plans/shuriken-stats-site.md, field audit: +# docs/stats-exif-audit.md). This module turns the per-photo +# `identify -verbose` output that album.source.sh already caches into +# aggregated, photographer-friendly counters. +# +# Scope: aggregation only. Rendering (stats.tmpl / camera.tmpl) lives in the +# sibling tasks pm0/rm0/um0; this module just fills the data structures they +# read. +# +# ---------------------------------------------------------------------------- +# Public API / handoff contract +# ---------------------------------------------------------------------------- +# collect_photo_exif_stats iterates the album's photos and fills these globals +# (all declared with `declare -gA` so the render tasks can read them after the +# call without namerefs): +# +# STATS_CAMERAS[] = count (camera leaderboard) +# STATS_CAMERA_SLUGS[] = slug (camera-.html) +# STATS_CAMERA_PHOTOS[] = newline-separated photo filenames +# STATS_LENSES[] = count (sparse; may be empty) +# STATS_YEARS[] = count +# STATS_MONTHS[<01..12>] = count +# STATS_APERTURE[] = count (e.g. "f/2.8") +# STATS_SHUTTER[] = count (e.g. "1/250s") +# STATS_ISO[] = count (e.g. "400") +# STATS_FOCAL[] = count (e.g. "35-70mm") +# STATS_MEGAPIXELS[] = count (e.g. "10-20MP") +# STATS_ASPECT[] = count (e.g. "3:2") +# STATS_ORIENTATION[] = count (Landscape/Portrait/Square) +# STATS_FORMAT[] = count (JPEG/PNG/WEBP/GIF) +# STATS_EXPOSURE_PROGRAM[