All projects

BrainStim Centers — Clinic Operations Automation

Three-module automation: PDF questionnaires parser, Rise4→Monday→CharmHealth sync, eClinical reconciler.

The goal

BrainStim Centers was losing ~40 admin hours/week scoring PDF questionnaires, copy-pasting Rise4 bookings into Monday.com + CharmHealth, and reconciling eClinical referrals by hand. Automate every repeatable step and keep it running 24/7.

The solution

A three-module production suite on a managed VPS: (1) a SharePoint/Teams PDF pipeline parsing 60+ questionnaire templates with auto-scoring; (2) an Outlook mail monitor that syncs Rise4 bookings to Monday.com (update-or-create against the current month's group) and creates CharmHealth patients via Playwright; (3) an eClinical reconciler implementing the clinic's branching rules (Hold On CNI, Sonja Review, Case Details Needed, Scheduled). All running as systemd services with structured logs.

Highlights

  • ~40 admin hours/week saved across three workflows
  • 24/7 systemd-managed uptime on a VPS
  • 60+ medical questionnaires parsed via one template-driven pipeline
  • Outlook OTP + Playwright JWT capture for always-on Rise4 auth

The challenge

Dozens of medical questionnaire templates in English and Spanish, each with a unique layout — a per-template parser wouldn't scale.

One pdf_pipeline.py with a templates.json registry declaring each template's fingerprint + field coordinates + scoring rules. Adding a template is a config edit, not a code change.

Rise4's API requires a JWT token-id that lives only in browser localStorage and rotates frequently.

Playwright login flow that fetches the OTP from Outlook via Microsoft Graph, navigates to the contacts smart-list, intercepts the contacts/search/2 request to capture the token into .env, and a watchdog that F5s every 30 min and re-logs-in on demand.

Every booking has to land cleanly in Monday (update or create) and in CharmHealth (create if absent) without duplicates, even when emails arrive out of order.

build_monday_existing_lookup() indexes the current month's group by email+phone; find_existing_monday_item() decides update-vs-create; CharmHealth checks for the patient before the Playwright create flow.

The owner's spec is dense branching rules (Hold On CNI / Sonja Review / Case Details Needed / Scheduled). Mis-interpreting one branch would silently corrupt leads.

Each branch implemented as a named scenario function with a [sync] -> <scenario> log line and a log-then-act pattern so every state transition is reviewable; the spec notes live in the repo as ground truth.

The clinic's admin staff cannot SSH or run Python directly.

run_teams_process.bat is a double-click launcher for the PDF module; the Rise4/Monday module runs as a systemd service on the VPS; all runs are idempotent via processed_state.json.

What was delivered

  • PDF Parser module: pdf_pipeline.py, templates.json, teams_process.py, run_teams_process.bat, staff README
  • Rise4 → Monday sync (rise4_mail_monitor, sync_rise4_to_monday, rise_monday_sync/runner.py)
  • Rise4 auth (rise_login_playwright.py) with Outlook Graph OTP
  • CharmHealth integration (charm_add_patient_from_json.py, charmhealth_questionnaire.py)
  • Extract Questionnaires Data module (fetch_patients.py)
  • eClinical reconciler with named scenario branches
  • VPS systemd setup, .env templates, AUTOMATION_SETUP.md, AGENTS.md
  • Business-rule spec notes files as repo ground truth

Results

~40h/wk

Admin time saved

24/7

Uptime

60+

Questionnaire templates

Monday + CharmHealth + eClinical

Sync targets

What it taught me

  • Template-driven parsing (config + scoring) is the only scalable way to tackle a library of questionnaires
  • Playwright login + Outlook Graph OTP + network-response capture beats calling SPA auth APIs directly
  • Turning each business branch into a named scenario function with [sync] -> <name> logs makes post-mortem trivial