# vuhnger/autofag architecture

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

- Interactive diagram: https://gitdiagram.com/vuhnger/autofag
- Repository: https://github.com/vuhnger/autofag

## Overview

Autofag watches UiO Studentweb courses, reports newly available places, and can enroll automatically. The user initializes a watchlist and notification channels, then runs or resumes a watcher; it checks course status, attempts enrollment when enabled, records outcomes, and dispatches notifications. The map includes browser authentication, Studentweb parsing, pacing, persistence, and notification delivery as supporting layers. Several runtime connections are only partially sampled, so those edges are marked unverified rather than asserted as directly evidenced.

Main entry points: [`src/autofag/app.py`](https://github.com/vuhnger/autofag/blob/main/src/autofag/app.py) (Service assembly), [`src/autofag/watch/watcher.py`](https://github.com/vuhnger/autofag/blob/main/src/autofag/watch/watcher.py) (Course watcher), [`src/autofag/studentweb/session.py`](https://github.com/vuhnger/autofag/blob/main/src/autofag/studentweb/session.py) (Studentweb session), [`src/autofag/watch/enroller.py`](https://github.com/vuhnger/autofag/blob/main/src/autofag/watch/enroller.py) (Auto-enrollment)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_setup["Setup and control"]
  node_cli["CLI commands<br/>[cli.py]"]
  node_init["Watchlist setup<br/>[init_flow.py]"]
  node_app["Service assembly<br/>[app.py]"]
  node_config["Configuration<br/>[config.py]"]
end

subgraph group_studentweb["Studentweb access"]
  node_auth["Browser login<br/>[browser.py]"]
  node_page["Browser page<br/>[page.py]"]
  node_session["Studentweb session<br/>[session.py]"]
  node_parsing["Result parsing<br/>[parsing.py]"]
  node_status["Status classifier<br/>[status.py]"]
  node_studentweb_service(("UiO Studentweb"))
end

subgraph group_watch["Watch and enroll"]
  node_watcher["Course watcher<br/>[watcher.py]"]
  node_enroller["Auto-enrollment<br/>[enroller.py]"]
  node_tempo["Check scheduling<br/>[tempo.py]"]
  node_models["Domain models<br/>[models.py]"]
end

subgraph group_state["State and pacing"]
  node_db[("SQLite storage<br/>[db.py]")]
  node_repos["State repositories<br/>[repos.py]"]
  node_pace["Request pacing<br/>[pace.py]"]
end

subgraph group_notify["Notifications"]
  node_dispatcher["Notification dispatcher<br/>[dispatcher.py]"]
  node_channels["Delivery channels<br/>[channels.py]"]
  node_notify_service(("Notification services"))
end

node_student(("Student"))

node_student -->|"runs commands"| node_cli
node_cli -->|"starts setup"| node_init
node_cli -->|"resumes watch"| node_watcher
node_init -.->|"signs in"| node_auth
node_app -->|"uses settings"| node_config
node_app -->|"assembles"| node_session
node_app -->|"wraps page"| node_page
node_app -->|"creates stores"| node_repos
node_auth -.->|"drives browser"| node_page
node_page -.->|"requests pages"| node_studentweb_service
node_session -->|"searches and enrolls"| node_page
node_session -.->|"parses responses"| node_parsing
node_session -.->|"classifies rows"| node_status
node_watcher -.->|"checks courses"| node_session
node_watcher -->|"attempts enrollment"| node_enroller
node_watcher -.->|"loads watchlist"| node_repos
node_watcher -.->|"schedules checks"| node_tempo
node_enroller -.->|"submits enrollment"| node_session
node_enroller -.->|"checks ledger"| node_repos
node_watcher -->|"dispatches alerts"| node_dispatcher
node_enroller -->|"reports outcome"| node_dispatcher
node_app -.->|"builds delivery"| node_dispatcher
node_dispatcher -->|"fans out"| node_channels
node_channels -.->|"sends notifications"| node_notify_service
node_repos -.->|"reads and writes"| node_db
node_app -->|"wraps requests"| node_pace

click node_cli "https://github.com/vuhnger/autofag/blob/main/src/autofag/cli.py"
click node_init "https://github.com/vuhnger/autofag/blob/main/src/autofag/init_flow.py"
click node_app "https://github.com/vuhnger/autofag/blob/main/src/autofag/app.py"
click node_config "https://github.com/vuhnger/autofag/blob/main/src/autofag/config.py"
click node_auth "https://github.com/vuhnger/autofag/blob/main/src/autofag/auth/browser.py"
click node_page "https://github.com/vuhnger/autofag/blob/main/src/autofag/studentweb/page.py"
click node_session "https://github.com/vuhnger/autofag/blob/main/src/autofag/studentweb/session.py"
click node_parsing "https://github.com/vuhnger/autofag/blob/main/src/autofag/studentweb/parsing.py"
click node_status "https://github.com/vuhnger/autofag/blob/main/src/autofag/studentweb/status.py"
click node_watcher "https://github.com/vuhnger/autofag/blob/main/src/autofag/watch/watcher.py"
click node_enroller "https://github.com/vuhnger/autofag/blob/main/src/autofag/watch/enroller.py"
click node_tempo "https://github.com/vuhnger/autofag/blob/main/src/autofag/watch/tempo.py"
click node_models "https://github.com/vuhnger/autofag/blob/main/src/autofag/models.py"
click node_db "https://github.com/vuhnger/autofag/blob/main/src/autofag/storage/db.py"
click node_repos "https://github.com/vuhnger/autofag/blob/main/src/autofag/storage/repos.py"
click node_pace "https://github.com/vuhnger/autofag/blob/main/src/autofag/transport/pace.py"
click node_dispatcher "https://github.com/vuhnger/autofag/blob/main/src/autofag/notify/dispatcher.py"
click node_channels "https://github.com/vuhnger/autofag/blob/main/src/autofag/notify/channels.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_cli,node_init,node_app,node_config toneBlue
class node_auth,node_page,node_session,node_parsing,node_status,node_studentweb_service toneAmber
class node_watcher,node_enroller,node_tempo,node_models toneMint
class node_db,node_repos,node_pace toneRose
class node_dispatcher,node_channels,node_notify_service,node_student toneIndigo
```

## Components

### Setup and control

- [CLI commands](https://github.com/vuhnger/autofag/blob/main/src/autofag/cli.py): `src/autofag/cli.py`
- [Watchlist setup](https://github.com/vuhnger/autofag/blob/main/src/autofag/init_flow.py): `src/autofag/init_flow.py`
- [Service assembly](https://github.com/vuhnger/autofag/blob/main/src/autofag/app.py): `src/autofag/app.py`
- [Configuration](https://github.com/vuhnger/autofag/blob/main/src/autofag/config.py): `src/autofag/config.py`

### Studentweb access

- [Browser login](https://github.com/vuhnger/autofag/blob/main/src/autofag/auth/browser.py): `src/autofag/auth/browser.py`
- [Browser page](https://github.com/vuhnger/autofag/blob/main/src/autofag/studentweb/page.py): `src/autofag/studentweb/page.py`
- [Studentweb session](https://github.com/vuhnger/autofag/blob/main/src/autofag/studentweb/session.py): `src/autofag/studentweb/session.py`
- [Result parsing](https://github.com/vuhnger/autofag/blob/main/src/autofag/studentweb/parsing.py): `src/autofag/studentweb/parsing.py`
- [Status classifier](https://github.com/vuhnger/autofag/blob/main/src/autofag/studentweb/status.py): `src/autofag/studentweb/status.py`
- **UiO Studentweb**

### Watch and enroll

- [Course watcher](https://github.com/vuhnger/autofag/blob/main/src/autofag/watch/watcher.py): `src/autofag/watch/watcher.py`
- [Auto-enrollment](https://github.com/vuhnger/autofag/blob/main/src/autofag/watch/enroller.py): `src/autofag/watch/enroller.py`
- [Check scheduling](https://github.com/vuhnger/autofag/blob/main/src/autofag/watch/tempo.py): `src/autofag/watch/tempo.py`
- [Domain models](https://github.com/vuhnger/autofag/blob/main/src/autofag/models.py): `src/autofag/models.py`

### State and pacing

- [SQLite storage](https://github.com/vuhnger/autofag/blob/main/src/autofag/storage/db.py): `src/autofag/storage/db.py`
- [State repositories](https://github.com/vuhnger/autofag/blob/main/src/autofag/storage/repos.py): `src/autofag/storage/repos.py`
- [Request pacing](https://github.com/vuhnger/autofag/blob/main/src/autofag/transport/pace.py): `src/autofag/transport/pace.py`

### Notifications

- [Notification dispatcher](https://github.com/vuhnger/autofag/blob/main/src/autofag/notify/dispatcher.py): `src/autofag/notify/dispatcher.py`
- [Delivery channels](https://github.com/vuhnger/autofag/blob/main/src/autofag/notify/channels.py): `src/autofag/notify/channels.py`
- **Notification services**

### Other components

- **Student**

## Connections

- Student → CLI commands: runs commands (evidence: `README.md`)
- CLI commands → Watchlist setup: starts setup (evidence: `README.md`)
- CLI commands → Course watcher: resumes watch (evidence: `README.md`)
- Watchlist setup → Browser login: signs in
- Service assembly → Configuration: uses settings (evidence: `src/autofag/app.py`)
- Service assembly → Studentweb session: assembles (evidence: `src/autofag/app.py`)
- Service assembly → Browser page: wraps page (evidence: `src/autofag/app.py`)
- Service assembly → State repositories: creates stores (evidence: `src/autofag/app.py`)
- Browser login → Browser page: drives browser (evidence: `src/autofag/auth/browser.py`)
- Browser page → UiO Studentweb: requests pages
- Studentweb session → Browser page: searches and enrolls (evidence: `src/autofag/studentweb/session.py`)
- Studentweb session → Result parsing: parses responses (evidence: `src/autofag/studentweb/session.py`)
- Studentweb session → Status classifier: classifies rows (evidence: `src/autofag/studentweb/session.py`)
- Course watcher → Studentweb session: checks courses (evidence: `src/autofag/watch/watcher.py`)
- Course watcher → Auto-enrollment: attempts enrollment (evidence: `src/autofag/watch/watcher.py`)
- Course watcher → State repositories: loads watchlist (evidence: `src/autofag/watch/watcher.py`)
- Course watcher → Check scheduling: schedules checks (evidence: `src/autofag/watch/watcher.py`)
- Auto-enrollment → Studentweb session: submits enrollment (evidence: `src/autofag/watch/enroller.py`)
- Auto-enrollment → State repositories: checks ledger (evidence: `src/autofag/watch/enroller.py`)
- Course watcher → Notification dispatcher: dispatches alerts (evidence: `src/autofag/watch/watcher.py`)
- Auto-enrollment → Notification dispatcher: reports outcome (evidence: `src/autofag/watch/enroller.py`)
- Service assembly → Notification dispatcher: builds delivery (evidence: `src/autofag/app.py`)
- Notification dispatcher → Delivery channels: fans out (evidence: `src/autofag/notify/dispatcher.py`)
- Delivery channels → Notification services: sends notifications
- State repositories → SQLite storage: reads and writes (evidence: `src/autofag/storage/repos.py`)
- Service assembly → Request pacing: wraps requests (evidence: `src/autofag/app.py`)
