Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions .github/workflows/deploy-downstream.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
name: "Deploy: Downstream Clusters"

# CD: push to develop -> Containers: Publish -> this workflow -> PR to cfp-sandbox-cluster.
# Live: publish release -> Containers: Publish -> this workflow -> PR to cfp-live-cluster.
# Manual: Run workflow_dispatch with tag (and optional target) to open deploy PRs.
# Requires BOT_GITHUB_TOKEN with write access to CodeForPhilly/cfp-sandbox-cluster and cfp-live-cluster.
on:
workflow_run:
workflows: ["Containers: Publish"]
Expand All @@ -8,15 +12,29 @@ on:
workflow_dispatch:
inputs:
tag:
description: 'Image tag to deploy (e.g. 1.1.0)'
description: 'Image tag to deploy (e.g. 1.1.0 or dev-abc1234)'
required: true
default: 'latest'
target:
description: 'Which cluster(s) to open deploy PRs for'
required: false
default: 'both'
type: choice
options:
- both
- sandbox
- live

permissions:
contents: read
actions: read
pull-requests: write

jobs:
update-sandbox:
name: Update Sandbox Cluster
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' || (github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'develop') }}
if: ${{ (github.event_name == 'workflow_dispatch' && (inputs.target == 'both' || inputs.target == 'sandbox')) || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'develop') }}
outputs:
tag: ${{ steps.get_tag.outputs.TAG }}
steps:
Expand Down Expand Up @@ -65,7 +83,7 @@ jobs:
update-live:
name: Update Live Cluster
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' || (github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'release') }}
if: ${{ (github.event_name == 'workflow_dispatch' && (inputs.target == 'both' || inputs.target == 'live')) || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'release') }}
steps:
- name: Checkout App
uses: actions/checkout@v4
Expand Down
35 changes: 35 additions & 0 deletions .github/workflows/frontend-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: "Frontend: Lint and Build"

on:
push:
branches: [develop]
pull_request:
branches: [develop]

jobs:
frontend:
name: Lint and Build
runs-on: ubuntu-latest
defaults:
run:
working-directory: frontend
steps:
- uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "18"
cache: "npm"
cache-dependency-path: frontend/package-lock.json

- name: Install dependencies
run: npm ci --legacy-peer-deps

- name: Lint
run: npm run lint
continue-on-error: true

- name: Build
run: npm run build
continue-on-error: true
3 changes: 1 addition & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,7 @@ Routes defined in `src/routes/routes.tsx`:

### Environment Configuration
- **Development**: `config/env/dev.env` (used by Docker Compose)
- **Frontend Production**: `frontend/.env.production`
- Contains `VITE_API_BASE_URL` for production API endpoint
- **Frontend**: Production uses relative API URLs (no `.env.production`); local dev uses `frontend/.env` (e.g. `VITE_API_BASE_URL` for proxy).
- **Never commit** actual API keys - use `.env.example` as template
- Django `SECRET_KEY` should be a long random string in production (not "foo")

Expand Down
227 changes: 160 additions & 67 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,115 +1,208 @@
# Balancer

Balancer is a website of digital tools designed to help prescribers choose the most suitable medications
for patients with bipolar disorder, helping them shorten their journey to stability and well-being

## Usage

You can view the current build of the website here: [https://balancertestsite.com](https://balancertestsite.com/)

## Contributing

### Join the Balancer community

Balancer is a [Code for Philly](https://www.codeforphilly.org/) project
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](https://choosealicense.com/licenses/agpl-3.0/)
[![Code for Philly](https://img.shields.io/badge/Code%20for%20Philly-Project-orange)](https://codeforphilly.org/projects/balancer)
[![Stack](https://img.shields.io/badge/Stack-Django%20%7C%20React%20%7C%20PostgreSQL%20%7C%20K8s-green)](https://github.com/CodeForPhilly/balancer)

**Balancer** is a digital clinical decision support tool designed to assist prescribers in selecting the most suitable medications for patients with bipolar disorder. By providing evidence-based insights, Balancer aims to shorten the patient's journey to stability and well-being.

This is an open-source project maintained by the **[Code for Philly](https://www.codeforphilly.org/)** community.

---

## 📋 Table of Contents

- [Architecture](#-architecture)
- [Prerequisites](#-prerequisites)
- [Environment Configuration](#-environment-configuration)
- [Quick Start: Local Development](#-quick-start-local-development)
- [Advanced: Local Kubernetes Deployment](#-advanced-local-kubernetes-deployment)
- [Data Layer](#-data-layer)
- [Contributing](#-contributing)
- [License](#-license)

---

## 🏗 Architecture

Balancer follows a modern containerized 3-tier architecture:

1. **Frontend**: React (Vite) application serving the user interface.
2. **Backend**: Django REST Framework API handling business logic, authentication, and AI orchestration.
3. **Data & AI**: PostgreSQL (with `pgvector` for RAG) and integrations with LLM providers (OpenAI/Anthropic).

```mermaid
graph TD
User[User / Prescriber] -->|HTTPS| Frontend[React Frontend]
Frontend -->|REST API| Backend[Django Backend]

subgraph "Data Layer"
Backend -->|Read/Write| DB[(PostgreSQL + pgvector)]
end

subgraph "External AI Services"
Backend -->|LLM Queries| OpenAI[OpenAI API]
Backend -->|LLM Queries| Anthropic[Anthropic API]
end

subgraph "Infrastructure"
Docker[Docker Compose (Local)]
K8s[Kubernetes / Kind (Dev/Prod)]
end
```

Join the [Code for Philly Slack and introduce yourself](https://codeforphilly.org/projects/balancer) in the #balancer channel
---

The project kanban board is [on GitHub here](https://github.com/orgs/CodeForPhilly/projects/2)
## 🛠 Prerequisites

### Code for Philly Code of Conduct
Before you start, ensure you have the following installed:

The Code for Philly Code of Conduct is [here](https://codeforphilly.org/pages/code_of_conduct/)
* **[Docker Desktop](https://www.docker.com/products/docker-desktop/)**: Required for running the application containers.
* **[Node.js & npm](https://nodejs.org/)**: Required if you plan to do frontend development outside of Docker.
* **[Devbox](https://www.jetify.com/devbox)** (Optional): Required only for the Local Kubernetes workflow.
* **Postman** (Optional): Useful for API testing. Ask in Slack to join the `balancer_dev` team.

### Setting up a development environment
---

Get the code using git by either forking or cloning `CodeForPhilly/balancer-main`
## 🔐 Environment Configuration

Tools used to run Balancer:
1. `OpenAI API`: Ask for an API key and add it to `config/env/env.dev`
2. `Anthropic API`: Ask for an API key and add it to `config/env/env.dev`
To run the application, you need to configure your environment variables.

Tools used for development:
1. `Docker`: Install Docker Desktop
2. `Postman`: Ask to get invited to the Balancer Postman team `balancer_dev`
3. `npm`: In the terminal run 1) 'cd frontend' 2) 'npm install' 3) 'cd ..'
1. **Backend Config**:
* Navigate to `config/env/`.
* Copy the example file: `cp dev.env.example dev.env`
* **Action Required**: Open `dev.env` and populate your API keys (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, etc.). Ask the project leads in Slack if you need shared development keys.

### Running Balancer for development
> **⚠️ SECURITY WARNING**: Never commit `config/env/dev.env` to version control. It is already ignored by `.gitignore`.

Start the Postgres, Django REST, and React services by starting Docker Desktop and running `docker compose up --build`
2. **Frontend Config**:
* The frontend uses `frontend/.env` for local dev only (e.g. `VITE_API_BASE_URL=http://localhost:8000` for the Vite proxy).
* Production builds use relative API URLs (no `.env.production` or API base URL needed); the same image works for sandbox and live.

#### Postgres
---

The application supports connecting to PostgreSQL databases via:
## 🚀 Quick Start: Local Development

1. **CloudNativePG** - Kubernetes-managed PostgreSQL cluster (for production/sandbox)
2. **AWS RDS** - External PostgreSQL database (AWS managed)
3. **Local Docker Compose** - For local development
This is the standard workflow for contributors working on features or bug fixes.

See [Database Connection Documentation](./docs/DATABASE_CONNECTION.md) for detailed configuration.
1. **Clone the Repository**
```bash
git clone https://github.com/CodeForPhilly/balancer.git
cd balancer
```

**Local Development:**
- Download a sample of papers to upload from [https://balancertestsite.com](https://balancertestsite.com/)
- The email and password of `pgAdmin` are specified in `balancer-main/docker-compose.yml`
- The first time you use `pgAdmin` after building the Docker containers you will need to register the server.
- The `Host name/address` is the Postgres server service name in the Docker Compose file
- The `Username` and `Password` are the Postgres server environment variables in the Docker Compose file
- You can use the below code snippet to query the database from a Jupyter notebook:
2. **Install Frontend Dependencies** (Optional but recommended for IDE support)
```bash
cd frontend
npm install
cd ..
```

```
from sqlalchemy import create_engine
import pandas as pd
3. **Start Services**
Run the full stack (db, backend, frontend) using Docker Compose:
```bash
docker compose up --build
```

engine = create_engine("postgresql+psycopg2://balancer:balancer@localhost:5433/balancer_dev")
4. **Access the Application**
* **Frontend**: [http://localhost:3000](http://localhost:3000)
* **Backend API**: [http://localhost:8000](http://localhost:8000)
* **Django Admin**: [http://localhost:8000/admin](http://localhost:8000/admin)

query = "SELECT * FROM api_embeddings;"
> **Default Superuser Credentials:**
> * **Email**: `admin@example.com`
> * **Password**: `adminpassword`
> * *(Defined in `server/api/management/commands/createsu.py`)*

df = pd.read_sql(query, engine)
```
---

#### Django REST
- The email and password are set in `server/api/management/commands/createsu.py`
## ☸️ Advanced: Local Kubernetes Deployment

## Local Kubernetes Deployment
Use this workflow if you are working on DevOps tasks, Helm charts, or Kubernetes manifests.

### Prereqs
### 1. Configure Hostname
We map a local domain to your machine to simulate production routing.

- Fill the configmap with the [env vars](./deploy/manifests/balancer/base/configmap.yml)
- Install [Devbox](https://www.jetify.com/devbox)
- Run the following script with admin privileges:
Run this script to update your `/etc/hosts` file (requires `sudo`):

```bash
#!/bin/bash
HOSTNAME="balancertestsite.com"
LOCAL_IP="127.0.0.1"

# Check if the correct line already exists
if grep -q "^$LOCAL_IP[[:space:]]\+$HOSTNAME" /etc/hosts; then
echo "Entry for $HOSTNAME with IP $LOCAL_IP already exists in /etc/hosts"
echo "Entry for $HOSTNAME already exists."
else
echo "Updating /etc/hosts for $HOSTNAME"
sudo sed -i "/[[:space:]]$HOSTNAME/d" /etc/hosts
echo "Updating /etc/hosts..."
echo "$LOCAL_IP $HOSTNAME" | sudo tee -a /etc/hosts
fi
```

### Steps to reproduce

Inside root dir of balancer
### 2. Deploy with Devbox
We use `devbox` to manage the local Kind cluster and deployments.

```bash
devbox shell
devbox create:cluster
devbox run deploy:balancer
```

The website should be available in [https://balancertestsite.com:30219/](https://balancertestsite.com:30219/)
The application will be available at: **[https://balancertestsite.com:30219/](https://balancertestsite.com:30219/)**

---

## 💾 Data Layer

Balancer supports multiple PostgreSQL configurations depending on the environment:

| Environment | Database Technology | Description |
| :--- | :--- | :--- |
| **Local Dev** | **Docker Compose** | Standard postgres container. Access at `localhost:5433`. |
| **Kubernetes** | **CloudNativePG** | Operator-managed HA cluster. Used in Kind and Prod. |
| **AWS** | **RDS** | Managed PostgreSQL for scalable cloud deployments. |

### Querying the Local Database
You can connect via any SQL client using:
* **Host**: `localhost`
* **Port**: `5433`
* **User/Pass**: `balancer` / `balancer`
* **DB Name**: `balancer_dev`

**Python Example (Jupyter):**
```python
from sqlalchemy import create_engine
import pandas as pd

# Connect to local docker database
engine = create_engine("postgresql+psycopg2://balancer:balancer@localhost:5433/balancer_dev")

# Query embeddings table
df = pd.read_sql("SELECT * FROM api_embeddings;", engine)
print(df.head())
```

---

## 🤝 Contributing

We welcome contributors of all skill levels!

## Architecture
1. **Join the Community**:
* Join the [Code for Philly Slack](https://codeforphilly.org/chat).
* Say hello in the **#balancer** channel.
2. **Find a Task**:
* Check our [GitHub Project Board](https://github.com/orgs/CodeForPhilly/projects/2).
3. **Code of Conduct**:
* Please review the [Code for Philly Code of Conduct](https://codeforphilly.org/pages/code_of_conduct/).

The Balancer website is a Postgres, Django REST, and React project. The source code layout is:
### Pull Request Workflow
1. Fork the repo.
2. Create a feature branch (`git checkout -b feature/amazing-feature`).
3. Commit your changes.
4. Open a Pull Request against the `develop` branch.

![Architecture Drawing](Architecture.png)
---

## License
## 📄 License

Balancer is licensed under the [AGPL-3.0 license](https://choosealicense.com/licenses/agpl-3.0/)
Balancer is open-source software licensed under the **[AGPL-3.0 License](https://choosealicense.com/licenses/agpl-3.0/)**.
Loading
Loading