generated from john/python-template
v5.0 Introduce centralized homepage & portrait photo management
Quality Gate / gate (push) Failing after 11s
Quality Gate / gate (push) Failing after 11s
This commit is contained in:
@@ -33,7 +33,7 @@ uv run python tools/export_import_migration.py migrate --bundle-dir .migration-b
|
||||
|
||||
## What gets migrated
|
||||
|
||||
- Tables (in dependency order): `document_type`, `person_role`, `document`, `person`, `document_person`, `job`, `source`, `job_source`, `execution_attempt`.
|
||||
- Tables (in dependency order): `document_type`, `person_role`, `tag`, `document`, `person`, `photo`, `document_person`, `document_tag`, `job`, `source`, `job_source`, `execution_attempt`.
|
||||
- Media tree under `UPLOAD_DIR`.
|
||||
|
||||
The bundle contains:
|
||||
@@ -42,7 +42,12 @@ The bundle contains:
|
||||
|
||||
Path normalization during export/import:
|
||||
- `source.file_path` is normalized to `documents/...` (upload-root-relative POSIX).
|
||||
- `person.portrait_path` is normalized to `persons/...` (upload-root-relative POSIX).
|
||||
- `photo.path` is normalized to `photos/...` (upload-root-relative POSIX).
|
||||
|
||||
Legacy V4.x portrait/homepage backfill in the export step:
|
||||
- If the source DB has no `photo` table, the exporter synthesizes `photo` rows from legacy `person.portrait_path` values and from legacy homepage image files under `UPLOAD_DIR/homepage`.
|
||||
- Legacy portrait and homepage image files are copied into the unified `UPLOAD_DIR/photos/{photo_id}{suffix}` layout in the migration bundle.
|
||||
- Legacy homepage markdown is relocated from `UPLOAD_DIR/homepage/homepage.md` to `UPLOAD_DIR/homepage.md`.
|
||||
|
||||
## Cutover
|
||||
|
||||
|
||||
+19
-21
@@ -114,28 +114,26 @@ Deferred out of this release:
|
||||
|
||||
## V5.0 — Unified Photos Table (major data-model change)
|
||||
|
||||
Per your feedback, this replaces the earlier "Track B" idea with a single
|
||||
shared model: a **`photos` table** holding an image reference plus a text
|
||||
`description` field, used by **both** the homepage gallery and Person
|
||||
portraits — rather than building two separate, one-off metadata layers.
|
||||
V5.0 standardizes homepage images and Person portraits into one shared `photo`
|
||||
table and one storage layout.
|
||||
|
||||
Rough shape (subject to the further discussion you flagged before any
|
||||
implementation):
|
||||
- `photos`: id, image path/reference, description, and whatever
|
||||
owner/context linkage is needed (e.g. a polymorphic or nullable
|
||||
`person_id` plus a `context`/`kind` discriminator such as `"homepage"` vs
|
||||
`"portrait"` — exact shape is an open design question, not decided here).
|
||||
- Person gets **multiple portraits** instead of the current single
|
||||
`portrait_path` string column — needs a migration to move existing
|
||||
single-portrait data into the new table.
|
||||
- Homepage images gain **descriptions**, using the same table instead of a
|
||||
separate sidecar/caption mechanism.
|
||||
- Because this is a genuine shared-schema decision (one table, two
|
||||
consumers, plus a migration of existing single-portrait data), this is
|
||||
correctly a 5.x "major change" rather than a small add-on — **flagged for
|
||||
a follow-up design discussion before implementation starts**, specifically
|
||||
on the exact `photos` table shape and how "which photos belong to which
|
||||
person/context" should be modeled.
|
||||
Finalized shape:
|
||||
- `photo`: `id`, nullable `person_id`, `path`, `description`, `is_primary`,
|
||||
timestamps.
|
||||
- `person_id IS NULL` = homepage photos; non-null = Person photos.
|
||||
- `is_primary` is the featured/first photo for that owner (homepage or Person).
|
||||
- No separate context enum; ownership is derived from `person_id`.
|
||||
- All image files are stored under `UPLOAD_DIR/photos/{photo_id}{suffix}`.
|
||||
- `Person.portrait_path` is removed.
|
||||
- `HOMEPAGE_DIR` is retired; homepage markdown remains file-backed at
|
||||
`UPLOAD_DIR/homepage.md`.
|
||||
|
||||
Migration policy for legacy installs:
|
||||
- Export/import rebuild remains the migration mechanism.
|
||||
- Legacy `person.portrait_path` values are backfilled into `photo` rows.
|
||||
- Legacy homepage images under `UPLOAD_DIR/homepage` are backfilled into
|
||||
homepage `photo` rows.
|
||||
- Legacy homepage markdown is relocated to `UPLOAD_DIR/homepage.md`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+17
-5
@@ -7,10 +7,10 @@ This document is the field-accurate Version 4 schema contract aligned to `src/tr
|
||||
- `src/transcription/db/models.py:60-78` (status and purpose enums)
|
||||
- `src/transcription/db/models.py:80-120` (`DocumentType`, `PersonRole`)
|
||||
- `src/transcription/db/models.py:122-172` (`Tag`, `Document`)
|
||||
- `src/transcription/db/models.py:175-258` (`Person`, `DocumentPerson`, `DocumentTag`)
|
||||
- `src/transcription/db/models.py:260-322` (`Job`)
|
||||
- `src/transcription/db/models.py:325-437` (`Source`, `JobSource`)
|
||||
- `src/transcription/db/models.py:440-497` (`ExecutionAttempt`)
|
||||
- `src/transcription/db/models.py:175-281` (`Person`, `Photo`, `DocumentPerson`, `DocumentTag`)
|
||||
- `src/transcription/db/models.py:285-347` (`Job`)
|
||||
- `src/transcription/db/models.py:350-462` (`Source`, `JobSource`)
|
||||
- `src/transcription/db/models.py:465-522` (`ExecutionAttempt`)
|
||||
|
||||
## Entity Relationship Overview
|
||||
|
||||
@@ -22,6 +22,7 @@ erDiagram
|
||||
Document ||--o{ DocumentPerson : links
|
||||
Document ||--o{ DocumentTag : tagged
|
||||
Person ||--o{ DocumentPerson : links
|
||||
Person ||--o{ Photo : owns
|
||||
PersonRole ||--o{ DocumentPerson : labels
|
||||
Tag ||--o{ DocumentTag : labels
|
||||
Job ||--o{ JobSource : includes
|
||||
@@ -119,12 +120,23 @@ erDiagram
|
||||
| `death_date_raw` | `str \| None` | optional |
|
||||
| `death_place` | `str \| None` | optional |
|
||||
| `biography` | `str \| None` | optional |
|
||||
| `portrait_path` | `str \| None` | optional upload-root-relative POSIX path (`persons/...`) |
|
||||
| `family_search_id` | `str \| None` | nullable unique |
|
||||
| `metadata_` | `dict[str, JsonValue] \| None` | stored as DB column `metadata` (`JSONBCompat`) |
|
||||
| `created_at` | `datetime` | default now |
|
||||
| `updated_at` | `datetime` | default now, onupdate |
|
||||
|
||||
### `Photo`
|
||||
|
||||
| Field | Type | Notes |
|
||||
| :--- | :--- | :--- |
|
||||
| `id` | `UUID` | PK |
|
||||
| `person_id` | `UUID \| None` | nullable FK -> `person.id`, indexed (`NULL` = homepage photo) |
|
||||
| `path` | `str` | required upload-root-relative POSIX path (`photos/...`) |
|
||||
| `description` | `str \| None` | optional |
|
||||
| `is_primary` | `bool` | default `False`; owner-level "featured/primary" marker |
|
||||
| `created_at` | `datetime` | default now |
|
||||
| `updated_at` | `datetime` | default now, onupdate |
|
||||
|
||||
### `DocumentPerson`
|
||||
|
||||
| Field | Type | Notes |
|
||||
|
||||
+14
-14
@@ -2,21 +2,22 @@
|
||||
|
||||
## Purpose
|
||||
|
||||
Home provides a user-maintained landing page for the local archive. It combines one current image with Markdown text and lets the operator edit both without changing application source or prompt assets.
|
||||
Home provides a user-maintained landing page for the local archive. It combines a database-backed image gallery with Markdown text and lets the operator edit both without changing application source or prompt assets.
|
||||
|
||||
## Routes
|
||||
|
||||
| Route | Browser path | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `/homepage` | `/ui/homepage` | View current homepage image and Markdown. |
|
||||
| `/homepage/edit` | `/ui/homepage/edit` | Upload an image and edit Markdown. |
|
||||
| `/homepage` | `/ui/homepage` | View homepage gallery and Markdown. |
|
||||
| `/homepage/edit` | `/ui/homepage/edit` | Upload images, manage image metadata, and edit Markdown. |
|
||||
|
||||
The application root and `/ui` redirect to `/ui/homepage`.
|
||||
|
||||
## View Behavior
|
||||
|
||||
- The visible page heading is **Home**; the browser tab title is **VibeScribe Home**.
|
||||
- The latest homepage image appears in the shared dark-room viewer.
|
||||
- The featured homepage image (`photo.is_primary`) is shown first; remaining images are shown in random order.
|
||||
- The current image appears in the shared dark-room viewer with its description.
|
||||
- Saved Markdown is rendered in the **Home Text** card.
|
||||
- Missing text displays `No homepage text saved yet.`
|
||||
- Missing image displays the viewer's empty state.
|
||||
@@ -25,26 +26,26 @@ The application root and `/ui` redirect to `/ui/homepage`.
|
||||
|
||||
## Edit Behavior
|
||||
|
||||
- The image upload accepts JPEG, PNG, GIF, WebP, BMP, and TIFF files.
|
||||
- A successful upload immediately stores the file, updates the preview to that image, and displays a positive notification.
|
||||
- The image upload accepts JPEG, PNG, GIF, WebP, BMP, and TIFF files and supports multi-file uploads.
|
||||
- A successful upload immediately stores files in the shared `photo` table/media layout and displays a positive notification.
|
||||
- The editor supports per-image description edits, setting a featured image, and deleting the current image.
|
||||
- The Markdown textarea is initialized from the currently stored homepage text.
|
||||
- **Save** writes the textarea content, displays `Homepage saved`, and returns to Home.
|
||||
- **Cancel** returns to Home without saving textarea changes. An image already uploaded during the edit session remains stored.
|
||||
|
||||
## Storage Contract
|
||||
|
||||
- Homepage content is mutable application data under `data/homepage`.
|
||||
- Markdown is stored in `homepage.md`.
|
||||
- Uploaded images keep a sanitized basename.
|
||||
- The view selects the supported image with the most recent modification time.
|
||||
- Homepage files are not transcription prompts and are not database records.
|
||||
- Homepage markdown text is mutable application data at `UPLOAD_DIR/homepage.md`.
|
||||
- Homepage images are stored as `photo` rows (`person_id = NULL`) with files under `UPLOAD_DIR/photos/`.
|
||||
- Uploaded images are renamed to `{photo_id}{suffix}`.
|
||||
- Homepage images are database records; markdown remains file-backed.
|
||||
|
||||
## Acceptance Checklist
|
||||
|
||||
- `/`, `/ui`, and the application brand reach Home.
|
||||
- Home renders with or without stored Markdown and image content.
|
||||
- Edit loads existing Markdown.
|
||||
- A supported image upload updates the preview and becomes the latest homepage image.
|
||||
- Supported image upload stores one or more images and makes the first image featured when no featured image exists yet.
|
||||
- Save persists Markdown and returns to Home.
|
||||
- Cancel does not save changed Markdown.
|
||||
|
||||
@@ -59,6 +60,5 @@ The application root and `/ui` redirect to `/ui/homepage`.
|
||||
|
||||
## Known Limitations
|
||||
|
||||
- Homepage storage location is configured by application settings and must remain writable in the active runtime environment.
|
||||
- Homepage markdown storage location is `UPLOAD_DIR/homepage.md` and must remain writable in the active runtime environment.
|
||||
- Uploading an image is immediate and is not rolled back by Cancel.
|
||||
- The editor does not currently delete or select among previously uploaded images.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Purpose
|
||||
|
||||
People manages reusable historical-person records. A Person may appear in many Documents under different relationship roles and may optionally carry a portrait and FamilySearch identifier.
|
||||
People manages reusable historical-person records. A Person may appear in many Documents under different relationship roles and may optionally carry one or more photos plus a FamilySearch identifier.
|
||||
|
||||
## Routes
|
||||
|
||||
@@ -38,7 +38,6 @@ Optional:
|
||||
- Exact and approximate birth/death dates.
|
||||
- Birth/death places.
|
||||
- Biography.
|
||||
- Portrait path or uploaded portrait.
|
||||
- FamilySearch ID.
|
||||
|
||||
Rules:
|
||||
@@ -46,7 +45,7 @@ Rules:
|
||||
- Missing Full name blocks save with a warning.
|
||||
- Exact date inputs are native browser date inputs.
|
||||
- FamilySearch IDs are normalized and validated by `PeopleService`.
|
||||
- Portrait uploads are stored under the configured upload root in a Person-specific directory and update Portrait path.
|
||||
- Photos are managed on Person Detail (not in create/edit form fields).
|
||||
- Metadata JSON remains hidden.
|
||||
- Save success returns to Person Detail.
|
||||
|
||||
@@ -54,7 +53,8 @@ Rules:
|
||||
|
||||
- The header provides **New Document**, **Edit Person**, and **Delete**.
|
||||
- **New Document** opens Document creation with this Person requested for author preselection.
|
||||
- The portrait viewer resolves supported relative upload paths and absolute HTTP/data URLs.
|
||||
- Person Detail includes a photo gallery card with multi-file upload, per-photo description edits, set-primary, and delete.
|
||||
- Primary photo is shown first and labeled as the primary portrait.
|
||||
- Biographical Record shows names, compact birth/death dates, and places.
|
||||
- Birth and death place values are clickable links to Google Maps when present.
|
||||
- FamilySearch ID is shown as a metadata value and is clickable to the FamilySearch person details route when present.
|
||||
@@ -67,6 +67,7 @@ Rules:
|
||||
## Delete Behavior
|
||||
|
||||
- The page warns when linked Document relationships exist.
|
||||
- Delete is blocked when related Photos exist.
|
||||
- Confirmed deletion removes the Person and its relationship links; it does not delete Documents.
|
||||
- Success returns to the People list.
|
||||
- Missing or already-deleted records return to a safe list state.
|
||||
@@ -76,7 +77,7 @@ Rules:
|
||||
- List fields, alignment, date fallback, search, sorting, and navigation match this contract.
|
||||
- Full name is enforced on create and edit.
|
||||
- FamilySearch ID validation and link generation use the fixed supported identifier format.
|
||||
- Portrait upload and rendering remain constrained to supported media paths.
|
||||
- Photo upload and rendering remain constrained to supported media paths.
|
||||
- New Document carries the Person context.
|
||||
- Linked Documents show the correct role and target.
|
||||
- Delete wording distinguishes removal of relationship links from deletion of Documents.
|
||||
|
||||
Reference in New Issue
Block a user