Add PainterPalette integration, canonical DB schema, and influence UI fixes.
Integrate Inputs/PainterPalette.csv for artist metadata and influence links, add db/schema.sql with server/migrate.js, letterbox influence panel thumbnails, and refresh Titian/Pontormo images. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
4eead54062
commit
b2cae284ac
@@ -0,0 +1,6 @@
|
||||
-- PainterPalette.csv enrichment fields on artists
|
||||
ALTER TABLE artists
|
||||
ADD COLUMN IF NOT EXISTS palette_metadata JSONB;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS artists_palette_metadata_idx
|
||||
ON artists USING gin (palette_metadata);
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
-- Virtual Art Gallery — canonical PostgreSQL schema
|
||||
-- Applied by: npm run migrate (server/migrate.js)
|
||||
-- Incremental ALTER migrations in db/migrate-*.sql run after this on existing databases.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS historical_eras (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
start_year INTEGER NOT NULL,
|
||||
end_year INTEGER NOT NULL,
|
||||
start_definite BOOLEAN DEFAULT true,
|
||||
end_definite BOOLEAN DEFAULT true,
|
||||
description TEXT,
|
||||
sort_order INTEGER DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS art_movements (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(150) NOT NULL,
|
||||
start_year INTEGER NOT NULL,
|
||||
end_year INTEGER NOT NULL,
|
||||
start_definite BOOLEAN DEFAULT true,
|
||||
end_definite BOOLEAN DEFAULT true,
|
||||
era_id INTEGER REFERENCES historical_eras(id),
|
||||
description TEXT,
|
||||
color VARCHAR(20) DEFAULT '#8B7355'
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS artists (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(200) NOT NULL,
|
||||
birth_year INTEGER,
|
||||
death_year INTEGER,
|
||||
movement_id INTEGER REFERENCES art_movements(id),
|
||||
portrait_path VARCHAR(500),
|
||||
bio_short TEXT,
|
||||
bio_full TEXT,
|
||||
wikipedia_title VARCHAR(300),
|
||||
century INTEGER,
|
||||
checkup_checked BOOLEAN NOT NULL DEFAULT false,
|
||||
checkup_fixed BOOLEAN NOT NULL DEFAULT false,
|
||||
palette_metadata JSONB
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS artist_periods (
|
||||
id SERIAL PRIMARY KEY,
|
||||
artist_id INTEGER NOT NULL REFERENCES artists(id) ON DELETE CASCADE,
|
||||
name VARCHAR(200) NOT NULL,
|
||||
start_year INTEGER,
|
||||
end_year INTEGER,
|
||||
description TEXT,
|
||||
sort_order INTEGER DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS paintings (
|
||||
id SERIAL PRIMARY KEY,
|
||||
artist_id INTEGER NOT NULL REFERENCES artists(id) ON DELETE CASCADE,
|
||||
period_id INTEGER REFERENCES artist_periods(id),
|
||||
title VARCHAR(300) NOT NULL,
|
||||
year INTEGER,
|
||||
year_end INTEGER,
|
||||
description TEXT,
|
||||
image_path VARCHAR(500),
|
||||
thumbnail_path VARCHAR(500),
|
||||
wikipedia_title VARCHAR(300),
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
checkup_checked BOOLEAN NOT NULL DEFAULT false,
|
||||
checkup_fixed BOOLEAN NOT NULL DEFAULT false
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS painting_influences (
|
||||
id SERIAL PRIMARY KEY,
|
||||
painting_id INTEGER NOT NULL REFERENCES paintings(id) ON DELETE CASCADE,
|
||||
influenced_by_painting_id INTEGER NOT NULL REFERENCES paintings(id) ON DELETE CASCADE,
|
||||
notes TEXT,
|
||||
source VARCHAR(500),
|
||||
aspects TEXT,
|
||||
quote TEXT,
|
||||
source_author VARCHAR(200),
|
||||
source_url VARCHAR(500),
|
||||
UNIQUE(painting_id, influenced_by_painting_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS painting_influence_sources (
|
||||
id SERIAL PRIMARY KEY,
|
||||
painting_id INTEGER NOT NULL REFERENCES paintings(id) ON DELETE CASCADE,
|
||||
source_type VARCHAR(20) NOT NULL CHECK (source_type IN ('painting', 'artist', 'movement')),
|
||||
source_painting_id INTEGER REFERENCES paintings(id) ON DELETE CASCADE,
|
||||
source_artist_id INTEGER REFERENCES artists(id) ON DELETE CASCADE,
|
||||
source_movement_id INTEGER REFERENCES art_movements(id) ON DELETE CASCADE,
|
||||
period_note VARCHAR(240),
|
||||
period_start_year INTEGER,
|
||||
period_end_year INTEGER,
|
||||
notes TEXT,
|
||||
source VARCHAR(500),
|
||||
aspects TEXT,
|
||||
quote TEXT,
|
||||
source_author VARCHAR(200),
|
||||
source_url VARCHAR(500),
|
||||
discovered_via VARCHAR(120),
|
||||
confidence VARCHAR(20) NOT NULL DEFAULT 'curated',
|
||||
CONSTRAINT painting_influence_sources_source_shape CHECK (
|
||||
(source_type = 'painting' AND source_painting_id IS NOT NULL AND source_artist_id IS NULL AND source_movement_id IS NULL)
|
||||
OR (source_type = 'artist' AND source_artist_id IS NOT NULL AND source_painting_id IS NULL AND source_movement_id IS NULL)
|
||||
OR (source_type = 'movement' AND source_movement_id IS NOT NULL AND source_painting_id IS NULL AND source_artist_id IS NULL)
|
||||
)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS painting_annotations (
|
||||
id SERIAL PRIMARY KEY,
|
||||
painting_id INTEGER NOT NULL REFERENCES paintings(id) ON DELETE CASCADE,
|
||||
label VARCHAR(80),
|
||||
body TEXT NOT NULL,
|
||||
category VARCHAR(30) NOT NULL DEFAULT 'subject',
|
||||
pos_x NUMERIC(5, 2),
|
||||
pos_y NUMERIC(5, 2),
|
||||
source_author VARCHAR(200),
|
||||
source VARCHAR(500),
|
||||
source_url VARCHAR(500),
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
confidence VARCHAR(20) NOT NULL DEFAULT 'curated'
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_artists_movement ON artists(movement_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_artists_century ON artists(century);
|
||||
CREATE INDEX IF NOT EXISTS artists_checkup_checked_idx ON artists (checkup_checked);
|
||||
CREATE INDEX IF NOT EXISTS artists_checkup_fixed_idx ON artists (checkup_fixed);
|
||||
CREATE INDEX IF NOT EXISTS artists_palette_metadata_idx ON artists USING gin (palette_metadata);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_paintings_artist ON paintings(artist_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_paintings_period ON paintings(period_id);
|
||||
CREATE INDEX IF NOT EXISTS paintings_checkup_checked_idx ON paintings (checkup_checked);
|
||||
CREATE INDEX IF NOT EXISTS paintings_checkup_fixed_idx ON paintings (checkup_fixed);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_movements_era ON art_movements(era_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_movements_years ON art_movements(start_year, end_year);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_influences_painting ON painting_influences(painting_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_influences_by ON painting_influences(influenced_by_painting_id);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS painting_influence_sources_unique
|
||||
ON painting_influence_sources (
|
||||
painting_id,
|
||||
source_type,
|
||||
COALESCE(source_painting_id, 0),
|
||||
COALESCE(source_artist_id, 0),
|
||||
COALESCE(source_movement_id, 0)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS painting_influence_sources_painting_idx
|
||||
ON painting_influence_sources (painting_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS painting_influence_sources_artist_idx
|
||||
ON painting_influence_sources (source_artist_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS painting_influence_sources_movement_idx
|
||||
ON painting_influence_sources (source_movement_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS painting_annotations_painting_idx
|
||||
ON painting_annotations (painting_id);
|
||||
@@ -0,0 +1,8 @@
|
||||
-- Run as PostgreSQL superuser before first use if the app user cannot CREATE tables:
|
||||
-- psql -h HOST -U postgres -d Gallery -f db/setup-admin.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;
|
||||
Reference in New Issue
Block a user