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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ instance/uploads
cover
.coveralls.yml

# Ignore new frontend build artifacts
/frontend/node_modules

# Ignore bento tool run paths (this line added by `bento init`)
.bento/
dump.rdb
23 changes: 10 additions & 13 deletions INSTALLATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ To lookup individual representatives, you need:

To test Twilio functionality in development, you will need your server to have a web-routable address.

* Twilio provides [ngrok](https://ngrok.com) to do this for free. When using the debug server you can use `flask run --host=SERVERID.ngrok.com` to set SERVER_NAME and STORE_DOMAIN
* Twilio provides [ngrok](https://ngrok.com) to do this for free. When using the debug server you can use `USE_NGROK=TRUE; python manage.py runserver`
* To test text-to-speech playback in the browser, you will need to create a [TwiML app](https://www.twilio.com/user/account/apps) with the Voice request URL http://YOUR_HOSTNAME/api/twilio/text-to-speech. Place the resulting application SID in your environment as TWILIO_PLAYBACK_APP

For production, you will also need to set:
Expand Down Expand Up @@ -66,15 +66,15 @@ To install locally and run in debug mode use:
export FLASK_APP=manager.py; FLASK_ENV=development; FLASK_DEBUG=1

# create the database
flask migrate up
python3 manage.py migrate

# compile assets
npm install -g bower
bower install
flask assets build

# create an admin user
flask createadminuser
python3 manage.py createadminuser

# if testing twilio, run in another tab
ngrok http 5000
Expand All @@ -83,10 +83,8 @@ To install locally and run in debug mode use:
export SERVER_NAME={{subdomain}}.ngrok.io
flask run --host=0.0.0.0

# if testing scheduled calls, run broker, scheduler and workers in new tabs
redis-server
flask rq scheduler
flask rq worker
# if testing scheduled calls, run the Django scheduler loop in a new tab
python3 manage.py runjobs

When the dev server is running, the front-end will be accessible at [http://localhost:5000/](http://localhost:5000/), and proxied to external routes at [http://ngrok.com](http://ngrok.com).

Expand All @@ -105,23 +103,22 @@ To run in production, with compiled assets:
iptables -A INPUT -p tcp --dport 80 -j ACCEPT

# initialize the database
flask migrate up
python3 manage.py migrate

# create an admin user (with optional --username, --password, --email)
flask createadminuser
python3 manage.py createadminuser

# prime cache with political data
flask loadpoliticaldata
python3 manage.py loadpoliticaldata

# if you are running a reverse proxy, you can start the application with foreman start
foreman start

# or point your WSGI server to `call_server.wsgi:application`
# to load the application directly

# if you wish to enable recurring outbound calls, you need to run the scheduler and at least one worker
flask rq scheduler
flask rq worker
# if you wish to enable recurring outbound calls, run the Django scheduler loop
python3 manage.py runjobs

Make sure your webserver can serve audio files out of `APPLICATION_ROOT/instance/uploads`. Or if you are using Amazon S3, ensure your buckets are configured for public access.

Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@ Call Power

Connecting people to power through their phones.

Migration status
----------------

This repository now contains a side-by-side Django + React migration in addition
to the legacy Flask + Backbone application.

- Legacy app: `call_server/`
- New Django app: `django_app/`
- New React admin UI: `frontend/`

The new stack currently ports the admin shell foundation, dashboard summary API,
campaign editing APIs, a Django political-data layer, and an initial Twilio
call-flow foundation while reusing the existing database tables.

The admin interface lets activists:

* Create and edit campaigns
Expand Down Expand Up @@ -52,6 +66,8 @@ This application should be easy to host on Heroku, with Docker, or directly on a

Read detailed instrustions at [INSTALLATION.md](INSTALLATION.md)

For the Django + React migration, see [django_app/README.md](django_app/README.md).

Testing
-------
`python tests/run.py`
Expand Down
91 changes: 91 additions & 0 deletions django_app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Django + React migration

This directory contains the new application stack for Call Power. It is designed
to run side-by-side with the legacy Flask app while features are ported over in
small slices.

## What is migrated in this slice

- Django project scaffold under `django_app/config`
- Legacy table mappings for campaigns, calls, sessions, scheduled calls, users, targets, CRM sync records, and blocklist records
- Django-managed scheduler metadata for recurring outbound calls and CRM sync timing
- JSON API endpoints for dashboard summary, campaign list/detail, campaign create/update, campaign copy, phone number lookup, and target lookup
- React admin shell in `frontend/` powered by Vite with editable campaign fields, phone-number assignment, target assignment, embed configuration, and CRM sync configuration
- Public Django pages for `/`, `/campaign/<id>/`, legacy `/create` aliases, and campaign embed endpoints
- Twilio call-flow foundation under `callpower/apps/calls/` for outbound call creation, inbound connection entrypoints, TwiML call chaining, and status callbacks backed by the legacy tables
- Twilio schedule prompt and recurring-call subscription flow backed by the Django-managed scheduler
- Django management commands for legacy operational tasks, including scheduler backfill and job execution
- Django CRM sync execution with `sync_call` tracking and scheduler-driven sync runs
- Django political-data providers under `callpower/apps/political_data/` for country data loading, cache-backed lookup, target hydration, and location-based target resolution used by the new Twilio flow

## Current migration boundary

The Django stack now covers the main webhook backbone and the political-data
lookup layer, but it is still not a perfect feature match with Flask.

- US custom-target and US location-based targeting are now the strongest paths in the new stack
- Canada support still depends on the optional `represent` Python package being available
- Legacy third-party embed snippets can now point at the Django-served `/api/campaign/<id>/embed.js` and iframe endpoints
- CRM sync now runs from Django for the legacy `rogue`, `mobilecommons`, and optional `actionkit` integrations
- `actionkit` still depends on the optional `python-actionkit` package being installed in the runtime

## Development

1. Install Python dependencies from `requirements/common.txt`
2. Install frontend dependencies in `frontend/`
3. Start Django:

```bash
python3 manage.py runserver
```

4. Start React:

```bash
cd frontend
npm install
npm run dev
```

The Django admin shell is served at `/admin/`, and in development it loads the
React app from the Vite dev server.

The public site root is served at `/`, public campaign pages at `/campaign/<id>/`,
and legacy call-congress compatibility aliases remain available at `/create`,
`/incoming_call`, and `/call_complete_status`.

## Scheduled jobs

Recurring jobs are now managed by Django instead of Flask RQ.

- Backfill scheduler rows from existing `schedule_call` and `sync_campaign` records:

```bash
python3 manage.py syncscheduledjobs
```

- Run the scheduler loop:

```bash
python3 manage.py runjobs
```

- Run a single scheduler tick:

```bash
python3 manage.py runjobs --once
```

- Run CRM sync immediately for one campaign or all configured campaigns:

```bash
python3 manage.py crmsync 123
python3 manage.py crmsync all
```

## Database compatibility

The Django models in this slice use `managed = False` so Django can read from
the existing schema without trying to recreate or alter the legacy tables. As
more of the application is migrated, we can introduce Django-native migrations
for new tables and selectively take ownership of old ones.
Empty file.
Empty file.
Empty file.
6 changes: 6 additions & 0 deletions django_app/callpower/apps/api/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class ApiConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "callpower.apps.api"
Loading