The Problem

Photographers and image‑rich applications need an automated way to rank large libraries by aesthetic, compositional, facial and technical quality. Manual curation is slow, error‑prone and does not scale across thousands of files.

What This Does

facet bundles a FastAPI back‑end (api/) with an Angular front‑end (client/) and a set of Python vision models (models/, processing/). The back‑end loads image‑level scores via TOPIQ, SAMP‑Net, CLIP/Qwen and technical metrics, stores the results in a SQLite DB (db/connection.py), and exposes REST endpoints (e.g. api/routers/gallery.py, api/routers/comparison.py). The front‑end consumes those endpoints to render a dark‑themed gallery with infinite scroll, filter drawers, and person‑management UI (client/src/app/...).

Key files:

  • api/config.py – loads secret‑derived config, controls multi‑user mode.
  • api/db_helpers.py – builds SQL filters used by many routers.
  • processing/scorer.py – orchestrates model loading and per‑image scoring.
  • client/src/app/core/services/api.service.ts – central HTTP client used by 17 other modules.

How It Is Wired

Execution starts at run_api.py (line 22) which calls main -> get_connection. get_connection (defined in db/connection.py) opens the SQLite DB and is the most‑called function (57 call sites). From there the flow is:

  1. Database init – db/connection.py → init_database (via api/database.py) creates tables, runs migrations, and registers pragmas.
  2. Request handling – FastAPI routes in api/routers/*.py invoke helpers in api/db_helpers.py. Functions like add_range_filter and _add_tag_filter are called from many routers (e.g., gallery, stats, comparison).
  3. Scoring pipeline – processing/scorer.py loads models (models/model_manager.py, models/samp_net.py) and calls compute which eventually invokes get_db_connection (again from db/connection.py) to write per‑image scores. The scorer also calls external commands (tqdm for progress, GPU libraries) and performs a single outbound network call when loading CLIP/Qwen weights.
  4. Front‑end interaction – The Angular service client/src/app/core/services/api.service.ts is the hub (Ca = 17) for all UI components; it forwards HTTP calls to the FastAPI back‑end. The i18n service and auth service are similarly central but have no internal imports, making them low‑risk to change.

The most blast‑radius modules are:

  • db/__init__.py – imported by 17 modules, touches the DB in 7 places.
  • api/config.py – secret‑derived config accessed by 15 modules, performs cryptographic hashing.
  • client/src/app/core/services/api.service.ts – consumed by 17 UI components, any change ripples through the UI.

No circular import cycles were detected, but deep nesting (up to 6 levels) and duplicated 6‑line blocks across 29 files raise maintenance risk.

How To Use It

# Clone the repo
git clone https://github.com/moses-y/facet
cd facet

# Python environment
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

# Front‑end
cd client
npm ci          # uses package-lock.json
npm run build   # builds Angular app (output in dist/)

# Initialise the DB (first run only)
python run_api.py --init-db   # creates SQLite file via db/connection.py

# Start the server
python run_api.py            # FastAPI runs on http://127.0.0.1:8000

Configuration lives in api/config.py; it expects a secret value (derived from SHARE_SECRET env var) and optional USER_CONFIG JSON files. No .env template is provided, so the caller must set those variables before launch.

The UI is served from client/dist/ by FastAPI’s static mount (api/__init__.py). Access the gallery at http://localhost:8000/.

Real‑World Use

A photo‑management service can point its ingestion pipeline at processing/scorer.py::compute to batch‑process new uploads. After scores are persisted, the Angular front‑end presents sortable, filterable galleries, while the api/routers/comparison.py endpoints enable human‑in‑the‑loop pairwise comparisons for active learning.

from processing.scorer import compute
compute(image_paths=["/data/img1.jpg", "/data/img2.jpg"])

Code Health & Issues

  • HIGH – No CI pipeline – 127 source files, no .github/workflows.
  • MEDIUM – Dependabot missing – only requirements.txt and client/package.json present.
  • MEDIUM – Broad exception handling – generic except: in api/config.py, api/db_helpers.py, api/types.py.
  • HIGH – Deep nesting – up to 6 indentation levels in api/db_helpers.py, faces/processor.py, models/model_manager.py.
  • HIGH – Duplicated code blocks – identical 6‑line snippets across 29 files (e.g., filter construction).
  • MEDIUM – File opens without context manager – models/samp_net.py, api/routers/thumbnails.py, db/maintenance.py.
  • HIGH – Oversized files – processing/scorer.py (1 837 lines) and related config modules are hard to maintain.

No Dockerfile, license present, lockfiles exist, and no secrets were found in the history.

The Bottom Line

facet provides a complete end‑to‑end photo scoring stack with a functional UI and a rich set of vision models. It is usable out of the box for small‑to‑medium libraries, but the codebase suffers from deep nesting, duplicated logic and a lack of automated testing/CI. Teams that need a customizable scoring pipeline and are prepared to refactor the hub modules will find it valuable; otherwise the maintenance overhead may outweigh the benefits.