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>
3.6 KiB
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
.docxmeeting transcripts fromdocs/Programme Pulse transcripts/and uses them as supporting context. - Generates two reports on demand: a Manager Summary and a Full Report. Both saved as
.docxinreports/. - Saves your stated preferences to
data/preferences.mdso future sessions remember them.
Requirements
- Python 3.11 or newer
- An Anthropic API key
- Two Airtable bases with personal access tokens
Setup
-
Create and activate a virtual environment.
python3 -m venv .venv source .venv/bin/activate -
Install dependencies.
pip install -r requirements.txt -
Copy the example env file and fill in your own keys.
cp .env.example .envOpen
.envand add:ANTHROPIC_API_KEYPULSE_AIRTABLE_API_KEY,PULSE_AIRTABLE_BASE_ID,PULSE_AIRTABLE_TABLE_ID— the tasks basePULSE_RESOURCE_API_KEY,PULSE_RESOURCE_BASE_ID,PULSE_RESOURCE_TABLE_ID— the resource booking baseFLASK_SECRET_KEY— any random string
-
Run the app.
python web_app.pyOr with gunicorn (recommended — streaming works better):
gunicorn web_app:app --bind 0.0.0.0:5051 --worker-class gevent --workers 1 --timeout 300 -
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.