Art Gallery Platform Documentation

This is a work in progress

The backend should now treat the four YAML files as first-class data sources:

  • art.yaml
  • gallery.yaml
  • scoring.yaml
  • show.yaml

What the backend should become

The backend should do four things cleanly:

  1. load and save each YAML file independently
  2. expose focused API routes for each file
  3. expose one combined route for algorithm use
  4. normalize relationships across files without duplicating data

The key idea is this:

  • art.yaml owns artworks and vocabularies
  • gallery.yaml owns spaces
  • scoring.yaml owns evaluator settings
  • show.yaml owns arrangements and placements

Backend structure

Inside server/src/, I would restructure to this:

server/src/
  app.js
  server.js

  routes/
    art.js
    gallery.js
    scoring.js
    show.js
    data.js
    uploads.js

  services/
    yamlFileService.js
    artService.js
    galleryService.js
    scoringService.js
    showService.js
    dataService.js
    uploadService.js

  utils/
    validation.js
    filePaths.js
    yamlDefaults.js

File responsibilities

yamlFileService.js

Low-level shared YAML read/write functions only.

Responsibilities:

  • read a YAML file
  • write a YAML file
  • create backups
  • validate file existence
  • return parsed JS object

This file should know nothing about artworks, spaces, or arrangements.

artService.js

Owns all artwork catalog logic.

Responsibilities:

  • load art.yaml
  • get all artworks
  • get one artwork by ID
  • create artwork
  • update artwork
  • delete artwork if you want
  • return vocabularies from art.yaml
  • validate uniqueness of artwork IDs

galleryService.js

Owns all gallery space logic.

Responsibilities:

  • load gallery.yaml
  • get all spaces
  • get one space by ID
  • create/update spaces
  • validate geometry fields

scoringService.js

Owns scoring profile logic.

Responsibilities:

  • load scoring.yaml
  • get scoring profile
  • update scoring profile
  • validate criteria structure

showService.js

Owns arrangements and placements.

Responsibilities:

  • load show.yaml
  • get all arrangements
  • get arrangement by ID
  • create arrangement
  • update arrangement
  • add/update/remove placements
  • validate references to artwork IDs and space IDs

dataService.js

The integration layer.

Responsibilities:

  • load all four files together
  • resolve references as needed
  • build combined algorithm-friendly data
  • provide getAllData() and maybe getResolvedArrangement(arrangementId)

This is the file Python will eventually depend on through the API.

Data files

In server/data/, I would now have:

server/data/
  art.yaml
  gallery.yaml
  scoring.yaml
  show.yaml
  backups/

And I would move backups into backups/, not spray .bak files into the main data directory.

API routes

Here is the contract I would use.

Art routes

GET    /api/art
GET    /api/art/:id
POST   /api/art
PUT    /api/art/:id
DELETE /api/art/:id
GET    /api/art-vocabularies

Gallery routes

GET    /api/gallery
GET    /api/gallery/spaces
GET    /api/gallery/spaces/:id
POST   /api/gallery/spaces
PUT    /api/gallery/spaces/:id

Scoring routes

GET    /api/scoring
PUT    /api/scoring

For Version 1 of scoring editing, a single profile is fine.

Show routes

GET    /api/show
GET    /api/show/arrangements
GET    /api/show/arrangements/:id
POST   /api/show/arrangements
PUT    /api/show/arrangements/:id
POST   /api/show/arrangements/:id/placements
PUT    /api/show/arrangements/:id/placements/:artworkId
DELETE /api/show/arrangements/:id/placements/:artworkId

Combined data routes

GET    /api/data/all
GET    /api/data/resolved-arrangements/:id

/api/data/all should return something like:

{
  "art": { ... },
  "gallery": { ... },
  "scoring": { ... },
  "show": { ... }
}

That is the future Python bridge.

What should change in the current artwork page

The current artwork page should no longer read show.yaml. It should read art.yaml only.

So the art UI should use:

  • GET /api/art
  • GET /api/art-vocabularies
  • POST /api/art
  • PUT /api/art/:id

That should be your first working milestone.

Important validation rules

This refactor is where you should tighten validation.

In artService

Validate:

  • required fields
  • numeric conversion for year, width_ft, height_ft
  • arrays are arrays
  • artwork ID uniqueness
  • primary_theme exists in vocabularies
  • theme_tags, palette_tags, mood_tags are known values if strict mode is on

In galleryService

Validate:

  • unique space IDs
  • positive dimensions
  • usable ranges inside wall bounds
  • centerline_ft makes sense

In showService

Validate:

  • arrangement IDs unique
  • space_id exists in gallery.yaml
  • every artwork_id exists in art.yaml
  • placements have numeric x_ft, y_ft

In scoringService

Validate:

  • criteria object exists
  • required fields per criterion exist
  • weights/tolerances are numeric
  • pairwise tables are correctly shaped

The biggest architectural rule

Do not let routes directly manipulate raw YAML objects.

Bad pattern:

router.post('/', async (req, res) => {
  const data = readYaml(...);
  data.art.artworks.push(req.body);
  writeYaml(...);
});

That will rot quickly.

Instead:

router.post('/', async (req, res) => {
  const artwork = await artService.createArtwork(req.body);
  res.status(201).json(artwork);
});

Routes should stay thin. Services should own logic.

Migration strategy

Do this in order.

Step 1

Create the new YAML files:

  • art.yaml
  • gallery.yaml
  • scoring.yaml
  • show.yaml

Step 2

Build the low-level YAML service.

Step 3

Build artService and switch the existing artwork UI to use it.

Step 4

Add galleryService, scoringService, and showService.

Step 5

Add the combined /api/data/all route.

Only after that should you build new UI pages.

That order matters because once the data contract is stable, the UI becomes much easier.

The Python bridge

Later, Python should not read YAML directly from arbitrary files if it can avoid it. It can, but the cleaner path for the class is:

  • backend reads and validates YAML
  • backend exposes normalized JSON
  • Python consumes JSON as dicts/lists

So the eventual Python workflow is:

  1. React calls backend
  2. backend returns combined JSON
  3. Python generator/scorer consumes that
  4. Python returns candidate arrangements and scores
  5. backend stores or forwards results
  6. React visualizes them

That is much cleaner than letting every student reinvent YAML parsing and validation.

What I would do right now

The immediate backend milestone should be:

  • current artwork page still works
  • but now it reads from art.yaml
  • and the backend already knows about the other three files

That gives you a stable base without trying to build walls, scoring, placements, and Python in one leap.

Suggested first implementation target

I would start by creating these files first:

server/src/services/yamlFileService.js
server/src/services/artService.js
server/src/services/dataService.js
server/src/routes/art.js
server/src/routes/data.js

and wire them into app.js.

That is the minimum clean cut.

If you want, next I’ll write the actual contents of those backend files in a form you can drop into the project.

Scroll to Top