programme-pulse-chat/README.md
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

137 lines
3.6 KiB
Markdown

# 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.
```sh
python3 -m venv .venv
source .venv/bin/activate
```
2. Install dependencies.
```sh
pip install -r requirements.txt
```
3. Copy the example env file and fill in your own keys.
```sh
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.
```sh
python web_app.py
```
Or with gunicorn (recommended — streaming works better):
```sh
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
```sh
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.