feat(dates): true_instant + wall_clock on /photos/exif (#113)
/photos/exif is Apollo's photo-to-track matching feed, but unlike /image/metadata and /photos/gps-summary it only carried the raw new-contract triple. date_utc is NULL for unconverted rows and, by design, forever for the drain's edited/exported floor (#120), so a client reading date_utc alone would have to re-implement the per-source legacy conventions to cover those rows: the re-anchoring ADR-0001 says consumers delete, not migrate. ExifSummary now carries: - true_instant: date_view::row_true_instant (date_utc when converted, else the per-source fallback), for instant-to-instant matching. - wall_clock: date_view::row_wall_clock, rendered verbatim. The row → wire mapping moves to ExifSummary::from_row so the tests cover the real projection, including an unconverted row falling back to the same instant and wall as its converted twin. Additive: legacy fields are unchanged, so current clients see the same values. The date_from/date_to filter still runs on the legacy column; that's #152, which now explicitly covers /photos/exif too. Unblocks Apollo #37. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
991622af2a
commit
f1a7fb4ecc
2 files changed
+84
-31
No files matched your search
+8
-16
@@ -1216,6 +1216,11 @@ pub async fn list_exif_summary(
|
||||
.map(|lib| (lib.id, lib.name.clone()))
|
||||
.collect();
|
||||
|
||||
// Resolved before taking the DAO mutex so the prefs lock isn't held
|
||||
// nested inside it.
|
||||
let display_zone = crate::date_view::owner_display_zone(&app_state.user_ai_prefs_dao);
|
||||
let server_zone = crate::camera_tz::server_zone();
|
||||
|
||||
let mut exif_dao_guard = exif_dao.lock().expect("Unable to get ExifDao");
|
||||
match exif_dao_guard.query_by_exif(
|
||||
library_filter,
|
||||
@@ -1229,22 +1234,9 @@ pub async fn list_exif_summary(
|
||||
Ok(rows) => {
|
||||
let photos: Vec<ExifSummary> = rows
|
||||
.into_iter()
|
||||
.map(|r| ExifSummary {
|
||||
library_name: library_names.get(&r.library_id).cloned(),
|
||||
file_path: r.file_path,
|
||||
library_id: r.library_id,
|
||||
camera_model: r.camera_model,
|
||||
width: r.width,
|
||||
height: r.height,
|
||||
// image_exif stores GPS as f32 to keep row size small;
|
||||
// widen for the JSON shape so clients don't need to
|
||||
// know about the on-disk precision.
|
||||
gps_latitude: r.gps_latitude.map(f64::from),
|
||||
gps_longitude: r.gps_longitude.map(f64::from),
|
||||
date_taken: r.date_taken,
|
||||
date_utc: r.date_utc,
|
||||
camera_offset_minutes: r.camera_offset_minutes,
|
||||
date_taken_tz_source: r.date_taken_tz_source,
|
||||
.map(|r| {
|
||||
let name = library_names.get(&r.library_id).cloned();
|
||||
ExifSummary::from_row(r, name, display_zone, server_zone)
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
||||
Reference in new issue
Block a user