Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Office Task Tracker

An office task tracker that scrapes email, passes each message to an LLM agent that classifies it into a Person, Task, and Summary, stores the result, and displays it in a web frontend.

Architecture

Layer Tech
Frontend Astro 5 + Vite + pnpm
Backend Python / FastAPI, managed with uv
Storage PostgreSQL 16
Scheduler APScheduler (background mail polling)
Agent Provider-agnostic LLM via LiteLLM
         ┌────────────┐   /api    ┌────────────┐   poll    ┌──────────┐
 Email ─►│  frontend  │ ◄───────► │  backend   │ ◄────────► │ postgres │
  IMAP   └────────────┘           │ (FastAPI)  │            └──────────┘
                                  │  └─ scrape → agent → store │
                                  └────────────┘

Repository layout

.
├── .env.example          # single source of truth for ALL env vars
├── docker-compose.yml    # dev + prod profiles
├── backend/              # Python (uv) FastAPI app
│   ├── pyproject.toml    # dependencies + tool config
│   ├── Dockerfile        # dev / prod targets
│   └── app/
│       ├── main.py       # app entrypoint, lifespan, router mounting
│       ├── config.py     # pydantic-settings reading .env
│       ├── db.py         # async SQLAlchemy engine / session
│       ├── models.py     # Person, Task, Summary ORM models
│       ├── schemas.py    # Pydantic API schemas
│       ├── jobs.py       # APScheduler mail-polling job
│       ├── routers/tasks.py
│       └── services/
│           ├── mail.py   # IMAP scraping (stub)
│           └── agent.py  # LLM classification (stub)
└── frontend/             # Astro (pnpm + Vite)
    ├── astro.config.mjs  # node adapter + /api dev proxy
    ├── Dockerfile        # dev / build / prod targets
    └── src/
        ├── pages/index.astro
        ├── layouts/Layout.astro
        └── components/TaskList.astro

Configuration

Everything is driven by a single root .env file. Docker Compose passes it to every service via env_file, so there is no per-service duplication.

cp .env.example .env

Open .env and fill in at least:

  • POSTGRES_PASSWORD — database password
  • IMAP_HOST, IMAP_USER, IMAP_PASSWORD — mailbox for scraping
  • LITELLM_MODEL + a provider key (OPENAI_API_KEY, ANTHROPIC_API_KEY, …) for the agent

PUBLIC_API_URL is inlined into the client bundle at build time. In dev it defaults to /api and the Vite server proxies to the backend automatically. In prod set it to the externally reachable backend URL.

Prerequisites

  • Docker with Compose v2
  • uv (local dev / lockfile generation)
  • Node.js ≥ 22 and pnpm (via corepack enable)

Run it

Option A — Docker Compose (recommended)

Development profile — hot reload, ports exposed:

docker compose --profile dev up --build

Build networking note: compose builds use network: host so uv/pnpm can download dependencies when the default bridge network has no outbound internet. If your Docker daemon already has build-network access, you can remove these network: host lines.

Production profile — built images, production servers:

docker compose --profile prod up -d --build

Stop / clean up:

docker compose down          # stop containers (keeps DB data)
docker compose down -v       # stop + delete the postgres volume

Option B — Local (no Docker, only Postgres in Docker)

Run the database, then the app from your host:

# terminal 1: postgres
docker compose --profile dev up postgres

# terminal 2: backend
cd backend
uv sync
uv run uvicorn app.main:app --reload --port 8000

# terminal 3: frontend
cd frontend
corepack enable && pnpm install
pnpm dev

Mail ingestion modes

The MAIL_INGESTION_MODE env var controls how emails are scraped:

Value Behaviour
background APScheduler polls the inbox every MAIL_POLL_SECONDS seconds
manual Nothing runs automatically; call POST /api/tasks/ingest

Manual trigger example:

curl -X POST http://localhost:8000/api/tasks/ingest -H 'Content-Type: application/json' \
  -d '{"limit": 25}'

API

Method Endpoint Description
GET /api/tasks List classified tasks (newest first)
GET /api/tasks/{id} Fetch a single task
POST /api/tasks/ingest Manually scrape + classify emails

Current status

This is an initial boilerplate commit. The end-to-end pipeline (app, models, API, Docker, env wiring) is in place, but the two core pieces are intentionally stubbed and live under backend/app/services/:

  • mail.pyscrape_emails() — add the real IMAP fetch logic.
  • agent.pyclassify_email() — add the LiteLLM call that extracts Person / Task / Summary.

Once implemented, the background job (or manual trigger) will persist classified tasks, and the frontend TaskList will display them.

About

Office task tracker which reads mails and classifies them, and provides summaries.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages