No description
Find a file
DJP b70d148b94 Productionise Programme Pulse
Backend
- Routes moved under /api/, JWT bearer auth via @before_request
- DEV_AUTH_BYPASS escape hatch for local dev
- In-memory chat history and report state replaced with Postgres tables
  (preferences, chat_messages, reports, feedback_events) keyed on user
- SQLAlchemy 2.x + Alembic migrations run on container start
- Graceful Airtable failure handling — bad creds no longer 500 the API
- Per-user data isolation via g.user_email from validated token

Frontend
- React + Vite + TypeScript SPA at /programme-pulse/
- MSAL.js (PKCE, sessionStorage, ID token to backend)
- VITE_DEV_AUTH_BYPASS mirrors backend bypass for local dev
- Streaming chat via fetch ReadableStream + SSE parsing
- Charts via chart.js, markdown via react-markdown + remark-gfm
- Full UI parity with the original templates/index.html

Deploy (optical-dev split-build pattern)
- Dockerfile + docker-compose.yml (name: programme-pulse pinned;
  app + Postgres; 127.0.0.1 binding only)
- deploy/apache-programme-pulse.conf.tmpl with flushpackets=on for SSE
- deploy/deploy.sh mirrors OSOP — port auto-pick (5051..5099),
  apache conf render, frontend build in throwaway node container,
  rsync to /var/www/html/programme-pulse, /api/health poll

Tests
- 49 passing; new tests for DB-backed preferences and JWT auth helpers
- SQLite-backed test fixture in tests/conftest.py

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:08:28 -04:00
alembic Productionise Programme Pulse 2026-05-07 11:08:28 -04:00
data Productionise Programme Pulse 2026-05-07 11:08:28 -04:00
deploy Productionise Programme Pulse 2026-05-07 11:08:28 -04:00
design Productionise Programme Pulse 2026-05-07 11:08:28 -04:00
docs/Programme Pulse transcripts Productionise Programme Pulse 2026-05-07 11:08:28 -04:00
frontend Productionise Programme Pulse 2026-05-07 11:08:28 -04:00
reports Productionise Programme Pulse 2026-05-07 11:08:28 -04:00
src Productionise Programme Pulse 2026-05-07 11:08:28 -04:00
templates Productionise Programme Pulse 2026-05-07 11:08:28 -04:00
tests Productionise Programme Pulse 2026-05-07 11:08:28 -04:00
.dockerignore Productionise Programme Pulse 2026-05-07 11:08:28 -04:00
.env.example Productionise Programme Pulse 2026-05-07 11:08:28 -04:00
.gitignore Productionise Programme Pulse 2026-05-07 11:08:28 -04:00
alembic.ini Productionise Programme Pulse 2026-05-07 11:08:28 -04:00
docker-compose.yml Productionise Programme Pulse 2026-05-07 11:08:28 -04:00
Dockerfile Productionise Programme Pulse 2026-05-07 11:08:28 -04:00
Procfile Productionise Programme Pulse 2026-05-07 11:08:28 -04:00
pytest.ini Productionise Programme Pulse 2026-05-07 11:08:28 -04:00
README.md Productionise Programme Pulse 2026-05-07 11:08:28 -04:00
requirements.txt Productionise Programme Pulse 2026-05-07 11:08:28 -04:00
web_app.py Productionise Programme Pulse 2026-05-07 11:08:28 -04:00

Programme Pulse

A small Flask app for chatting with live Airtable programme data and generating two-tier status reports (Manager Summary + Full Report) as Word documents.

Runs locally on port 5051 by default.


What it does

  • Loads tasks from one Airtable base (the Master tracker) and resource bookings from a second Airtable base.
  • Lets you chat with the data through Claude. Streaming response, stop button, thumbs up/down feedback.
  • Reads .docx meeting transcripts from docs/Programme Pulse transcripts/ and uses them as supporting context.
  • Generates two reports on demand: a Manager Summary and a Full Report. Both saved as .docx in reports/.
  • Saves your stated preferences to data/preferences.md so future sessions remember them.

Requirements

  • Python 3.11 or newer
  • An Anthropic API key
  • Two Airtable bases with personal access tokens

Setup

  1. Create and activate a virtual environment.

    python3 -m venv .venv
    source .venv/bin/activate
    
  2. Install dependencies.

    pip install -r requirements.txt
    
  3. Copy the example env file and fill in your own keys.

    cp .env.example .env
    

    Open .env and add:

    • ANTHROPIC_API_KEY
    • PULSE_AIRTABLE_API_KEY, PULSE_AIRTABLE_BASE_ID, PULSE_AIRTABLE_TABLE_ID — the tasks base
    • PULSE_RESOURCE_API_KEY, PULSE_RESOURCE_BASE_ID, PULSE_RESOURCE_TABLE_ID — the resource booking base
    • FLASK_SECRET_KEY — any random string
  4. Run the app.

    python web_app.py
    

    Or with gunicorn (recommended — streaming works better):

    gunicorn web_app:app --bind 0.0.0.0:5051 --worker-class gevent --workers 1 --timeout 300
    
  5. Open http://localhost:5051 in your browser.


Adding meeting transcripts

Drop Teams .docx transcript exports into docs/Programme Pulse transcripts/. The app picks them up on restart and feeds them to the chat and the reports as supporting narrative.

Each .docx is parsed paragraph by paragraph. One speaker turn per paragraph.


Folder layout

web_app.py                 Flask entry point
src/
  airtable_client.py       Pulls tasks and bookings from Airtable
  analyzer.py              Health signal logic
  claude_client.py         Anthropic SDK wrapper, streaming
  preferences.py           Saves liked/disliked patterns
  prompts.py               System prompts and snapshot builders
  reporter.py              Word document generation
  transcripts.py           Parses .docx meeting transcripts
templates/index.html       The single-page UI
design/                    Fonts and design tokens
data/                      preferences.md lives here
docs/Programme Pulse transcripts/   Drop .docx files here
reports/                   Generated Word files land here
logs/                      Runtime logs
tests/                     Pytest suite

Airtable schema notes

The tasks base must include these fields (rename the constants in src/airtable_client.py if your column names differ):

  • Title / task name
  • Owner (collaborator)
  • Status / Progress
  • Priority
  • Due date
  • Notes
  • Last modified

The resource base needs:

  • Resource name
  • Project
  • Division
  • Start date, end date
  • Hours
  • Status
  • Conflict flag

Open src/airtable_client.py to map field names to your own base.


Running tests

pytest

Deploying elsewhere

The app is stateless apart from data/preferences.md, reports/, and logs/. Mount those as volumes if you containerise it. The Procfile is set up for any platform that respects it (Heroku, Render, Fly, etc).

Set PORT via env var. Everything else is read from .env or the platform's secret store.