# Influence links import & curator tool Curator tool to **list / add / delete** influence edges, **visualize** an artist neighborhood, and **import** CSV / JSON / XLSX files through a mapping wizard. Entry: header **Influences** (curator session). Data lives in `painting_influence_sources` (legacy `painting_influences` mirrored for painting→painting). --- ## Expansion rule (artist-level files) Input workbooks such as [`Inputs/artist_influences_web_sources.xlsx`](../Inputs/artist_influences_web_sources.xlsx) are **artist-centric**. On import: - **Influenced by** tokens → attach as sources on **all paintings** of the subject artist - **Influenced** tokens that resolve to an artist → reverse link: subject artist becomes a source on **all paintings** of the influenced artist (same as PainterPalette `Influencedon`) Unresolved names (artists / movements / paintings not in the DB) are **skipped** with warnings — no auto-create stubs. --- ## Wizard column roles | Role | Meaning | |------|---------| | `subject_artist` | Artist the row is about (required) | | `subject_painting` | Optional work hint (contextual; expansion still uses all works) | | `influenced_by` | Who/what influenced the subject (`;` / `,` separated) | | `influenced` | Who the subject influenced | | `notes` / `reference` / `source_url` | Citation metadata (URLs scraped from reference text) | | `ignore` | Skip column | ### Presets | Preset | Typical headers | Example file | |--------|-----------------|--------------| | Web sources | `Artist`, `Painting`, `Influenced by`, `Influenced`, `Reference (source + link)` | [`Inputs/artist_influences_web_sources.xlsx`](../Inputs/artist_influences_web_sources.xlsx) | | Story of Art | Same Title Case (+ chapter reference column) | [`Inputs/story_of_art_influences.xlsx`](../Inputs/story_of_art_influences.xlsx) | | Gariff influential painters | `artist`, `painting`, `influenced by`, `influenced`, `reference` | [`Inputs/gariff_influential_painters_influences.xlsx`](../Inputs/gariff_influential_painters_influences.xlsx) | | Art influences | `artist`, `painting`, `influenced_by`, `influenced`, `reference` | [`Inputs/art_influences.xlsx`](../Inputs/art_influences.xlsx) | | Custom | Map any columns manually | — | Token classification order: **artist → movement → painting title** (under subject artist, then global). Committed edges use `confidence=curated`, `discovered_via=import-wizard`. ### Book extract: Gariff (2008) [`Inputs/gariff_influential_painters_influences.xlsx`](../Inputs/gariff_influential_painters_influences.xlsx) is distilled from David Gariff et al., *The World's Most Influential Painters and the Artists They Inspired* (Herbert Press / Quarto, 2008; local PDF `Inputs/1.pdf`, not committed). - Columns match the import wizard (`artist` / `painting` / `influenced by` / `influenced` / `reference`). - **`artist` and `painting`** are limited to catalog slices in `Inputs/artistslistdbdata-*.csv` and `Inputs/paintinglistdbdata-*.csv` (regenerate those from prod/dev as needed). - **`influenced by` / `influenced`** may name artists, movements, or works from the book (including entities outside those CSVs). - Multiple rows per subject when the book states several influences. - Import via curator **Influences → Import** (Art influences / custom mapping) or keep as a curated source workbook. See also [`Inputs/story_of_art_influences.xlsx`](../Inputs/story_of_art_influences.xlsx) (Gombrich) and optional local `Inputs/janson_short_history_influences.xlsx` (same column shape). ### Duplicate file / data guard Each successful commit stores SHA-256 fingerprints in `curator_audit_log` (`influence.import` details): - `contentHash` — raw file bytes - `payloadHash` — normalized mapped rows (same data under another filename still matches) On parse/preview, if either hash matches a prior import, the UI warns and **blocks commit** unless the curator checks **Import anyway (force)**. Individual edges remain unique via DB `ON CONFLICT` either way. --- ## API (curator) | Method | Path | Purpose | |--------|------|---------| | `GET` | `/api/influences` | List (`artistId`, `paintingId`, `q`, `limit`, `offset`) | | `GET` | `/api/influences/graph?artistId=` | Nodes + edges for SVG neighborhood | | `POST` | `/api/influences` | Create one edge | | `PATCH` | `/api/influences/:id` | Update metadata / remap source | | `DELETE` | `/api/influences/:id` | Delete (+ legacy mirror) | | `POST` | `/api/influences/import/parse` | `{ filename, contentBase64, sheet? }` → columns, hashes, `alreadyImported` | | `POST` | `/api/influences/import/preview` | `{ rows, mapping, contentHash?, payloadHash? }` → proposals + duplicate check | | `POST` | `/api/influences/import/commit` | `{ proposals, fileName?, contentHash?, payloadHash?, force? }` — `409 ALREADY_IMPORTED` unless `force` | Audit: `influence.create` / `update` / `delete` / `import` in `curator_audit_log`. --- ## CLI still available - `npm run dev:update-influences` — curated [`scripts/art-influences-data.js`](../scripts/art-influences-data.js) - `npm run dev:import-painter-palette` — PainterPalette CSV The wizard is the interactive path for ad-hoc spreadsheets under `Inputs/`. See also [API.md](API.md), [DB_structure.md](DB_structure.md), [Plans.md](Plans.md).