# denisisaev-create/wop architecture

> Architecture diagram of the GitHub repository denisisaev-create/wop, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-06-30). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/denisisaev-create/wop
- Repository: https://github.com/denisisaev-create/wop

## Overview

1. **Overall shape**
   - This is a Python backend that replaces a Google Sheets-based workflow for the “WOP” feed.
   - The system is centered on a FastAPI service, a relational database layer, an ingestion/sync pipeline, and a small browser UI served as static assets.

2. **Primary runtime entrypoint**
   - `app/main.py` is the application entrypoint and likely wires together the API app, routes, startup hooks, and scheduler initialization.
   - The repo is intended to run under `uvicorn app.main:app`, so the API server is the main runtime boundary.

3. **HTTP/API boundary**
   - `app/views.py` defines the HTTP endpoints, while `app/schemas.py` defines the request/response contracts.
   - The documented endpoints show three major concerns: health checks, load querying, and sync operations (`/sync`, `/sync/upload`, `/sync/status`, `/loads`, `/schedule`).
   - This is the boundary where external clients interact with the backend.

4. **Data persistence layer**
   - `app/db.py` and `app/models.py` form the persistence layer.
   - `app/config.py` centralizes environment-driven settings such as database connection details and IMAP/scheduler flags.
   - The code likely uses an ORM-backed relational store; the important architectural point is that all ingestion and API reads/writes go through this database layer, not directly through the source feed.

5. **Ingestion pipeline**
   - The `app/ingest/` package is the core transformation pipeline.
   - `feed_source.py` abstracts where feed data comes from, `parser.py` reads raw feed content, `normalize.py` standardizes it, `mappers.py` converts source fields into internal models, and `upsert.py` writes the resulting records into the database.
   - This package is the main boundary between messy external feed formats and the internal canonical load representation.

6. **Sync orchestration**
   - `app/sync.py` appears to coordinate end-to-end synchronization: fetch feed data, run parsing/normalization/mapping, then apply database updates.
   - `app/clock.py` and `app/scheduler.py` suggest time-based orchestration, with the scheduler triggering periodic syncs after startup.
   - The readme indicates the scheduler runs one sync on startup and then every 15 minutes when enabled.

7. **Feed sources and manual fallback**
   - The system supports two feed acquisition modes:
     - **Live email sync** via Gmail IMAP using an app password and the `WOP FEED` label.
     - **Manual CSV upload** via `/sync/upload` for fallback or early setup.
   - This means the ingestion pipeline is source-agnostic at the top, but converges into a single normalization/upsert path downstream.

8. **Business rules and safety controls**
   - `app/rules.py` and `app/enums.py` likely encode domain-specific load status, archival, and filtering logic.
   - The readme describes a key safety boundary: if a sync would archive too large a fraction of active loads, the archive step is skipped and recorded in sync history.
   - This is important for architecture because it prevents bad upstream data from deleting too much state during a single run.

9. **UI and presentation layer**
   - `app/static/index.html`, `app/static/app.js`, and `app/static/styles.css` implement a lightweight frontend.
   - This is not a separate SPA backend; it is a thin presentation layer served by the same Python application, likely used to inspect loads and sync state.

10. **Operational/observability surface**
   - `/health` provides basic liveness checking.
   - `/sync/status` and probably a `sync_log`-style persistence path provide operational visibility into recent runs and whether the archive guard tripped.
   - This makes sync execution a first-class operational workflow, not just a background job.

11. **Utility and backfill tooling**
   - `scripts/load_csv.py` is a utility for importing CSV data outside the live HTTP path.
   - `docs/HANDOFF.md`, `PROJECT_STATUS.md`, and the WOP spec/prompts files document the domain and transition from the old Sheets workflow.

12. **Tests as architectural guardrails**
   - `tests/test_api.py`, `tests/test_ingest.py`, and `tests/test_mappers.py` show the three main boundaries being protected: HTTP behavior, ingestion behavior, and source-to-domain mapping.
   - That test split matches the architecture: API layer, transformation pipeline, and domain mapping/persistence.

Main entry points: [`app/sync.py`](https://github.com/denisisaev-create/wop/blob/main/app/sync.py) (sync), [`app/views.py`](https://github.com/denisisaev-create/wop/blob/main/app/views.py) (views), [`app/ingest/upsert.py`](https://github.com/denisisaev-create/wop/blob/main/app/ingest/upsert.py) (upsert), [`app/db.py`](https://github.com/denisisaev-create/wop/blob/main/app/db.py) (db)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_group_api["API layer"]
  node_app_main["main<br/>FastAPI entrypoint<br/>[main.py]"]
  node_app_views["views<br/>HTTP routes<br/>[views.py]"]
  node_app_schemas["schemas<br/>API contracts<br/>[schemas.py]"]
end

subgraph group_group_core["Core services"]
  node_app_sync{{"sync<br/>orchestration<br/>[sync.py]"}}
  node_app_scheduler(("scheduler<br/>background jobs<br/>[scheduler.py]"))
  node_app_clock(("clock<br/>time control<br/>[clock.py]"))
  node_app_rules["rules<br/>domain policy<br/>[rules.py]"]
  node_app_enums["enums<br/>domain types<br/>[enums.py]"]
end

subgraph group_group_ingest["Ingestion pipeline"]
  node_ingest_source{{"feed source<br/>feed acquisition<br/>[feed_source.py]"}}
  node_ingest_parser["parser<br/>raw parsing<br/>[parser.py]"]
  node_ingest_normalize["normalize<br/>standardization<br/>[normalize.py]"]
  node_ingest_mappers["mappers<br/>field mapping<br/>[mappers.py]"]
  node_ingest_upsert[("upsert<br/>write path<br/>[upsert.py]")]
end

subgraph group_group_data["Persistence"]
  node_app_db[("db<br/>database access<br/>[db.py]")]
  node_app_models[("models<br/>[models.py]")]
  node_app_config["config<br/>settings<br/>[config.py]"]
end

subgraph group_group_ui["Presentation"]
  node_app_static["static UI<br/>browser UI<br/>[index.html]"]
  node_scripts_load_csv["load_csv<br/>utility import<br/>[load_csv.py]"]
end

node_app_main -->|"mounts"| node_app_views
node_app_main -->|"starts"| node_app_scheduler
node_app_main -->|"initializes"| node_app_db
node_app_views -->|"uses"| node_app_schemas
node_app_views -->|"triggers"| node_app_sync
node_app_views -->|"reads/writes"| node_app_db
node_app_scheduler -->|"runs"| node_app_sync
node_app_scheduler -->|"times"| node_app_clock
node_app_sync -->|"fetches"| node_ingest_source
node_app_sync -->|"applies"| node_app_rules
node_app_sync -->|"parses"| node_ingest_parser
node_app_sync -->|"normalizes"| node_ingest_normalize
node_app_sync -->|"maps"| node_ingest_mappers
node_app_sync -->|"persists"| node_ingest_upsert
node_ingest_upsert -->|"uses"| node_app_db
node_ingest_upsert -->|"writes"| node_app_models
node_app_models -->|"depends on"| node_app_enums
node_app_db -->|"configured by"| node_app_config
node_app_static -->|"served by"| node_app_views
node_scripts_load_csv -->|"reuses"| node_ingest_mappers
node_scripts_load_csv -->|"loads"| node_ingest_upsert

click node_app_main "https://github.com/denisisaev-create/wop/blob/main/app/main.py"
click node_app_views "https://github.com/denisisaev-create/wop/blob/main/app/views.py"
click node_app_schemas "https://github.com/denisisaev-create/wop/blob/main/app/schemas.py"
click node_app_sync "https://github.com/denisisaev-create/wop/blob/main/app/sync.py"
click node_app_scheduler "https://github.com/denisisaev-create/wop/blob/main/app/scheduler.py"
click node_app_clock "https://github.com/denisisaev-create/wop/blob/main/app/clock.py"
click node_app_rules "https://github.com/denisisaev-create/wop/blob/main/app/rules.py"
click node_app_enums "https://github.com/denisisaev-create/wop/blob/main/app/enums.py"
click node_ingest_source "https://github.com/denisisaev-create/wop/blob/main/app/ingest/feed_source.py"
click node_ingest_parser "https://github.com/denisisaev-create/wop/blob/main/app/ingest/parser.py"
click node_ingest_normalize "https://github.com/denisisaev-create/wop/blob/main/app/ingest/normalize.py"
click node_ingest_mappers "https://github.com/denisisaev-create/wop/blob/main/app/ingest/mappers.py"
click node_ingest_upsert "https://github.com/denisisaev-create/wop/blob/main/app/ingest/upsert.py"
click node_app_db "https://github.com/denisisaev-create/wop/blob/main/app/db.py"
click node_app_models "https://github.com/denisisaev-create/wop/blob/main/app/models.py"
click node_app_config "https://github.com/denisisaev-create/wop/blob/main/app/config.py"
click node_app_static "https://github.com/denisisaev-create/wop/blob/main/app/static/index.html"
click node_scripts_load_csv "https://github.com/denisisaev-create/wop/blob/main/scripts/load_csv.py"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_app_main,node_app_views,node_app_schemas toneBlue
class node_app_sync,node_app_scheduler,node_app_clock,node_app_rules,node_app_enums toneAmber
class node_ingest_source,node_ingest_parser,node_ingest_normalize,node_ingest_mappers,node_ingest_upsert toneMint
class node_app_db,node_app_models,node_app_config toneRose
class node_app_static,node_scripts_load_csv toneIndigo
```

## Components

### API layer

HTTP boundary and app bootstrap

- [main](https://github.com/denisisaev-create/wop/blob/main/app/main.py): FastAPI entrypoint · Wires the application, routes, and startup hooks for uvicorn. · `app/main.py`
- [views](https://github.com/denisisaev-create/wop/blob/main/app/views.py): HTTP routes · Defines health, loads, sync, and schedule endpoints. · `app/views.py`
- [schemas](https://github.com/denisisaev-create/wop/blob/main/app/schemas.py): API contracts · Request and response models for the public HTTP surface. · `app/schemas.py`

### Core services

Sync orchestration, business rules, and scheduling

- [sync](https://github.com/denisisaev-create/wop/blob/main/app/sync.py): orchestration · Coordinates end-to-end feed synchronization and persistence. · `app/sync.py`
- [scheduler](https://github.com/denisisaev-create/wop/blob/main/app/scheduler.py): background jobs · Triggers periodic sync runs when enabled. · `app/scheduler.py`
- [clock](https://github.com/denisisaev-create/wop/blob/main/app/clock.py): time control · Provides scheduling time boundaries and run timing support. · `app/clock.py`
- [rules](https://github.com/denisisaev-create/wop/blob/main/app/rules.py): domain policy · Encodes archive and load-state safety rules. · `app/rules.py`
- [enums](https://github.com/denisisaev-create/wop/blob/main/app/enums.py): domain types · Defines load and sync enumerations used across the app. · `app/enums.py`

### Ingestion pipeline

Source acquisition and feed transformation

- [feed source](https://github.com/denisisaev-create/wop/blob/main/app/ingest/feed_source.py): feed acquisition · Abstracts where raw feed data comes from. · `app/ingest/feed_source.py`
- [parser](https://github.com/denisisaev-create/wop/blob/main/app/ingest/parser.py): raw parsing · Reads raw feed content into structured records. · `app/ingest/parser.py`
- [normalize](https://github.com/denisisaev-create/wop/blob/main/app/ingest/normalize.py): standardization · Cleans and standardizes feed records. · `app/ingest/normalize.py`
- [mappers](https://github.com/denisisaev-create/wop/blob/main/app/ingest/mappers.py): field mapping · Converts source fields into canonical internal models. · `app/ingest/mappers.py`
- [upsert](https://github.com/denisisaev-create/wop/blob/main/app/ingest/upsert.py): write path · Persists normalized records into the database. · `app/ingest/upsert.py`

### Persistence

Database access and domain models

- [db](https://github.com/denisisaev-create/wop/blob/main/app/db.py): database access · Database connection and session management. · `app/db.py`
- [models](https://github.com/denisisaev-create/wop/blob/main/app/models.py): ORM models · Relational domain models for loads and sync history. · `app/models.py`
- [config](https://github.com/denisisaev-create/wop/blob/main/app/config.py): settings · Environment-driven configuration for DB, email, and scheduling. · `app/config.py`

### Presentation

Static browser UI and utility tooling

- [static UI](https://github.com/denisisaev-create/wop/blob/main/app/static/index.html): browser UI · Lightweight frontend served by the backend. · `app/static/index.html`
- [load_csv](https://github.com/denisisaev-create/wop/blob/main/scripts/load_csv.py): utility import · Standalone CSV import tool for offline backfill or setup. · `scripts/load_csv.py`

## Connections

- main → views: mounts · Registers the HTTP routes with the application.
- main → scheduler: starts · Initializes periodic sync scheduling on startup.
- main → db: initializes · Brings up database access for the app lifecycle.
- views → schemas: uses · Validates and serializes request/response payloads.
- views → sync: triggers · Starts on-demand sync operations and status queries.
- views → db: reads/writes · Exposes load and sync data through the persistence layer.
- scheduler → sync: runs · Invokes scheduled synchronization runs.
- scheduler → clock: times · Uses clock boundaries for recurring execution.
- sync → feed source: fetches · Obtains feed data from live or manual sources.
- sync → rules: applies · Checks safety and archive policy before committing changes.
- sync → parser: parses · Converts raw feed content into structured items.
- sync → normalize: normalizes · Standardizes feed records before mapping.
- sync → mappers: maps · Transforms source fields into canonical load records.
- sync → upsert: persists · Writes transformed records to the database.
- upsert → db: uses · Stores records through the database layer.
- upsert → models: writes · Operates on the ORM/domain model layer.
- models → enums: depends on · Uses shared domain state and status values.
- db → config: configured by · Reads connection and runtime settings from config.
- static UI → views: served by · Browser UI is delivered from the same application.
- load_csv → mappers: reuses · Shares the mapping path for offline CSV imports.
- load_csv → upsert: loads · Writes imported rows through the normal persistence path.
