Jul 21 - Update uploaded photos location/timestamp

This commit is contained in:
2026-07-21 14:06:26 -04:00
parent 507d9b5c64
commit 5a30abbea5
4 changed files with 413 additions and 5 deletions
+40 -4
View File
@@ -47,13 +47,26 @@ def upload_photo():
---------------------
file — binary image data (jpg / png / gif)
entity_type — "inspection" | "issue" | "issue_result" (controls subfolder)
captured_at — OPTIONAL ISO-8601 capture time (e.g. 2026-07-20T09:14:22-04:00)
latitude — OPTIONAL decimal degrees at capture
longitude — OPTIONAL decimal degrees at capture
A capture-time + geo overlay is burned into the image before it is stored
(see app/utils/photo_stamp.py). Metadata is taken from the client fields
above, falling back to the image's EXIF, then to server receipt time.
Sending captured_at/latitude/longitude is strongly preferred for an
offline-first client: a photo taken at 09:14 but synced at 16:00 would
otherwise be stamped with the sync time.
Response 200
------------
{
"ok": true,
"data": {
"server_path": "uploads/inspection_photos/abc123.jpg"
"server_path": "uploads/inspection_photos/abc123.jpg",
"stamped": true,
"captured_at": "2026-07-20T09:14:22",
"capture_source": "client"
}
}
"""
@@ -85,12 +98,35 @@ def upload_photo():
else:
subfolder = 'inspection_photos'
# Burn the capture-time + geo overlay before the bytes are ever stored, so
# exactly one (already-stamped) object is written and nothing has to be
# read back out of R2. Any stamping failure returns the original bytes.
meta = {'stamped': False, 'captured_at': None, 'source': None}
if current_app.config.get('PHOTO_STAMP_ENABLED', True):
from app.utils.photo_stamp import stamp_file_storage
file_obj, meta = stamp_file_storage(
file_obj,
captured_at = request.form.get('captured_at'),
latitude = request.form.get('latitude'),
longitude = request.form.get('longitude'),
)
# Write via the active storage backend (local disk or R2). Key format
# 'uploads/<subfolder>/<uuid>.<ext>' is unchanged across backends.
from app.utils import storage
server_path = storage.save(file_obj, subfolder)
logger.info('API PHOTOS | uploaded | entity_type=%s | path=%s | user=%s',
entity_type, server_path, user.username)
logger.info(
'API PHOTOS | uploaded | entity_type=%s | path=%s | user=%s | '
'stamped=%s | capture_source=%s',
entity_type, server_path, user.username,
meta.get('stamped'), meta.get('source'),
)
return api_ok({'server_path': server_path})
captured_at = meta.get('captured_at')
return api_ok({
'server_path': server_path,
'stamped': bool(meta.get('stamped')),
'captured_at': captured_at.isoformat() if captured_at else None,
'capture_source': meta.get('source'),
})