Add project documentation under Documentation/.

Covers architecture, setup, database schema, API reference, and image pipeline; link from README.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Danila Khodjaef
2026-06-19 09:46:44 +03:00
co-authored by Cursor
parent 40db99d734
commit 44d3d4359a
6 changed files with 577 additions and 0 deletions
+147
View File
@@ -0,0 +1,147 @@
# Art Gallery — database structure
PostgreSQL schema for the virtual gallery. Canonical DDL is intended to live in `db/schema.sql` when checked in; this document describes the logical model either way.
Connection settings come from `.env` (see [setup.md](setup.md)).
## Overview
| Item | Typical value |
|------|----------------|
| Engine | PostgreSQL 14+ |
| Database | `Gallery` |
| App user | `gallery` |
| Time fields | Integer years (negative = BCE) |
## Entity relationship
```mermaid
erDiagram
historical_eras ||--o{ art_movements : contains
art_movements ||--o{ artists : groups
artists ||--o{ artist_periods : has
artists ||--o{ paintings : created
artist_periods ||--o{ paintings : groups
paintings ||--o{ painting_influences : influenced_by
paintings ||--o{ painting_influences : influences
```
## Tables
### `historical_eras`
Broad chronological buckets (Ancient, Medieval, Renaissance, …).
| Column | Type | Notes |
|--------|------|-------|
| `id` | SERIAL PK | |
| `name` | VARCHAR(100) | Display label |
| `start_year`, `end_year` | INTEGER | Inclusive range |
| `start_definite`, `end_definite` | BOOLEAN | `false` → render as gradient edge on timeline |
| `description` | TEXT | Tooltip / sidebar copy |
| `sort_order` | INTEGER | Display order |
### `art_movements`
Finer-grained styles (Impressionism, Cubism, Suprematism, …).
| Column | Type | Notes |
|--------|------|-------|
| `id` | SERIAL PK | |
| `name` | VARCHAR(150) | |
| `start_year`, `end_year` | INTEGER | |
| `start_definite`, `end_definite` | BOOLEAN | Same visual semantics as eras |
| `era_id` | FK → `historical_eras` | Optional parent era |
| `description` | TEXT | |
| `color` | VARCHAR(20) | Hex colour for movement band |
### `artists`
| Column | Type | Notes |
|--------|------|-------|
| `id` | SERIAL PK | |
| `name` | VARCHAR(200) | |
| `birth_year`, `death_year` | INTEGER | Nullable; used for timeline portrait placement |
| `movement_id` | FK → `art_movements` | Primary movement |
| `portrait_path` | VARCHAR(500) | Relative to `data/images/` |
| `bio_short`, `bio_full` | TEXT | From Wikipedia extracts |
| `wikipedia_title` | VARCHAR(300) | Source page title |
| `century` | INTEGER | Rounded century bucket for seeding limits |
### `artist_periods`
Phases within an artists career (e.g. “Blue Period”, “Roman Period”).
| Column | Type | Notes |
|--------|------|-------|
| `id` | SERIAL PK | |
| `artist_id` | FK → `artists` | ON DELETE CASCADE |
| `name` | VARCHAR(200) | |
| `start_year`, `end_year` | INTEGER | |
| `description` | TEXT | |
| `sort_order` | INTEGER | Wall order in 3D gallery |
### `paintings`
| Column | Type | Notes |
|--------|------|-------|
| `id` | SERIAL PK | |
| `artist_id` | FK → `artists` | ON DELETE CASCADE |
| `period_id` | FK → `artist_periods` | Optional grouping |
| `title` | VARCHAR(300) | |
| `year`, `year_end` | INTEGER | Creation date(s) |
| `description` | TEXT | |
| `image_path` | VARCHAR(500) | Full-size local file |
| `thumbnail_path` | VARCHAR(500) | Smaller variant for lists / 3D |
| `wikipedia_title` | VARCHAR(300) | Used by image fetcher |
| `sort_order` | INTEGER | |
### `painting_influences`
Directed edges: *this painting* was influenced by *that painting*.
| Column | Type | Notes |
|--------|------|-------|
| `id` | SERIAL PK | |
| `painting_id` | FK → `paintings` | The work being explained |
| `influenced_by_painting_id` | FK → `paintings` | The earlier / source work |
| `notes` | TEXT | Curator summary |
| `source` | VARCHAR(500) | General attribution |
| `aspects` | TEXT | What was borrowed (composition, colour, …) |
| `quote` | TEXT | Short citation |
| `source_author` | VARCHAR(200) | e.g. Gombrich, Janson |
| `source_url` | VARCHAR(500) | Reference link |
Unique constraint on `(painting_id, influenced_by_painting_id)`.
## Indexes
- `artists(movement_id)`, `artists(century)`
- `paintings(artist_id)`, `paintings(period_id)`
- `art_movements(era_id)`, `art_movements(start_year, end_year)`
- `painting_influences(painting_id)`, `painting_influences(influenced_by_painting_id)`
## First-time setup
The `gallery` database user needs `CREATE` on schema `public` for migrations. If tables cannot be created, run admin grants as PostgreSQL superuser before `npm run migrate`:
```sql
GRANT CREATE ON SCHEMA public TO gallery;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO gallery;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO gallery;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO gallery;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO gallery;
```
Then:
```bash
npm run migrate
npm run seed
```
## Data conventions
- **Year zero** is not used; BCE years are negative integers.
- **Image paths** are relative to `IMAGE_DIR` (default `./data/images`), e.g. `portraits/Claude_Monet.jpg`, `paintings/thumbs/Raphael_The_School_of_Athens_thumb.jpg`.
- **Seeding cap**: curated ingest targets at most ~100 artists per century to keep the catalog manageable.