diff --git a/.gitignore b/.gitignore
index 82fea29e..26adcf2c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,8 +9,10 @@ media/
static/CACHE/
node_modules/
staticfiles/
+.DS_Store
# Elastic Beanstalk Files
.elasticbeanstalk/*
!.elasticbeanstalk/*.cfg.yml
!.elasticbeanstalk/*.global.yml
+
diff --git a/README.md b/README.md
index 5dd57856..0fddd229 100644
--- a/README.md
+++ b/README.md
@@ -21,68 +21,34 @@ Visualize the results of ranked-choice voting elections.
Learn more on our Medium post: [An Illustrated Guide to Ranked-Choice Voting](https://medium.com/@armin.samii/an-illustrated-guide-to-ranked-choice-voting-4ce3c5fe73f9).
-## Installation
-Install `python3`, `virtualenv`, and `npm` with your favorite package manager, then run `./scripts/install.sh`.
-
-Create a .env file with your secrets and configuration options:
-
-```bash
-export RCVIS_SECRET_KEY=''
-export RCVIS_DEBUG=True
-export RCVIS_HOST=localhost
-
-# Either have OFFLINE_MODE=True
-export OFFLINE_MODE=True
-
-# The following fields are optional, though you will
-# have limited functionality without them.
-
-# Or set up an AWS bucket and enter your credentials
-# export OFFLINE_MODE=False
-# export AWS_STORAGE_BUCKET_NAME=''
-# export AWS_S3_REGION_NAME=''
-# export AWS_ACCESS_KEY_ID=''
-# export AWS_SECRET_ACCESS_KEY=''
-
-# To send registration emails when OFFLINE_MODE is False:
-# export SENDGRID_USERNAME=''
-# export SENDGRID_PASSWORD=''
+## Examples
+Check out [rcvis.com](https://www.rcvis.com) for live examples, including:
-# To clear cloudflare cache when models update:
-# export CLOUDFLARE_ZONE_ID=''
-# export CLOUDFLARE_AUTH_TOKEN=''
+| Barchart | Round-by-Round |
+| --- | --- |
+| ![Barchart](static/visualizer/icon_interactivebar.gif "Interactive Barchart") | ![Round-by-Round](static/visualizer/icon_interactiveroundbyround.gif "Round-by-Round") |
-# To run the SauceLabs integration tests, you will need
-export SAUCE_USERNAME=''
-export SAUCE_ACCESS_KEY=''
+| Sankey | Tabular Summary |
+| --- | --- |
+| ![Sankey](static/visualizer/icon_sankey.jpg "Sankey") | ![Tabular Summaries](static/visualizer/icon_singletable.png "Tabular Summaries") |
-# To generate videos (and to run movie tests), you will need:
-export IMAGEIO_FFMPEG_EXE='/usr/bin/ffmpeg'
-export SQS_QUEUE_NAME='default-queue'
-# export MOVIE_FONT_NAME="Roboto"
-# export AWS_POLLY_STORAGE_BUCKET_NAME="bucket-name-on-s3"
+## Embedding
+RCVis implements the [oembed protocol](http://www.oembed.com) with discoverability, allowing you to embed files into your website with an iframe.
-# To subscribe users to mailchimp upon registration, you need:
-# export MAILCHIMP_API_KEY=''
-# export MAILCHIMP_LIST_ID=''
-# export MAILCHIMP_DC=''
-# If you are updating a template, you'll need to clear the cache every time or set:
-# export DISABLE_CACHE=True
+# Running RCVis Locally
-```
+## Installation
+Install `python3`, `virtualenv`, and `npm` with your favorite package manager, then run `./scripts/install.sh`. This script will initialize a `.env` file in the root directory for your secrets and configuration. You will need to supply a secret key in `.env` before proceeding.
-To get moviepy working for Ubuntu 16.04 LTS users, comment out the following statement in `/etc/ImageMagick-6/policy.xml`:
-```xml
-
-```
-or, simply run `sudo ./scripts/fix-moviepy-on-ubuntu-1604.sh`
## Running
-To begin serving the website at localhost:8000:
+
+You can begin serving the website at localhost:8000 using:
```bash
./scripts/serve.sh
```
+The first time you execute this script, you will be prompted to create a new admin user. You can skip this step for future executions by storing the email of the admin user as `OFFLINE_ADMIN` in `.env`.
You may also need to run this whenever the npm dependencies change:
```bash
@@ -93,24 +59,8 @@ npm install # this works for me
python3 manage.py npminstall # this is purported to work but doesn't
```
-To run workers to generate movies (optional - only needed to use the movie generation flow):
-```bash
-source .env
-source venv/bin/activate
-export DISPLAY=":0" # if not already set
-celery -A rcvis worker --loglevel info
-```
-
-## Examples
-Check out [rcvis.com](https://www.rcvis.com) for live examples, including:
-
-| Barchart | Round-by-Round |
-| --- | --- |
-| ![Barchart](static/visualizer/icon_interactivebar.gif "Interactive Barchart") | ![Round-by-Round](static/visualizer/icon_interactiveroundbyround.gif "Round-by-Round") |
-
-| Sankey | Tabular Summary |
-| --- | --- |
-| ![Sankey](static/visualizer/icon_sankey.jpg "Sankey") | ![Tabular Summaries](static/visualizer/icon_singletable.png "Tabular Summaries") |
+## Test Data
+Test data, including real and mock elections, can be found in the `testData` directory. Example input formats can be found on [RCVFormats](https://github.com/artoonie/rcvformats/tree/main/testdata/inputs).
## REST API
The primary API documentation is in the form of [example code](visualizer/tests/testRestApiExampleCode.py), which is documented line-by-line.
@@ -120,7 +70,7 @@ Addition documentation is available at [rcvis.com/api/](https://www.rcvis.com/ap
To get started with programmatic access to rcvis:
1. Create an account on RCVis
-2. Email team@rcvis.com to enable API access
+2. Email team@rcvis.com to request API access
3. Submit a POST request to [https://www.rcvis.com/api/auth/get-token](https://www.rcvis.com/api/auth/get-token) to obtain an API Key, e.g. `curl -X POST https://www.rcvis.com/api/auth/get-token -d username=yourUserName -d password=yourAmazingPassword`
With your API key, you may access two endpoints:
@@ -129,5 +79,19 @@ With your API key, you may access two endpoints:
For both endpoints, upload with POST and modify with PUT or PATCH. Authenticated users are limited to 1000 requests per hour.
-## oembed
-RCVis implements the [oembed protocol](http://www.oembed.com) with discoverability, allowing you to embed files into your website with an iframe.
+
+## Movies
+
+To get moviepy working for Ubuntu 16.04 LTS users, comment out the following statement in `/etc/ImageMagick-6/policy.xml`:
+```xml
+
+```
+or, simply run `sudo ./scripts/fix-moviepy-on-ubuntu-1604.sh`
+
+To run workers to generate movies (optional - only needed to use the movie generation flow):
+```bash
+source .env
+source venv/bin/activate
+export DISPLAY=":0" # if not already set
+celery -A rcvis worker --loglevel info
+```
diff --git a/electionpage/tests.py b/electionpage/tests.py
index 598a6fc9..3e9bf021 100644
--- a/electionpage/tests.py
+++ b/electionpage/tests.py
@@ -304,8 +304,9 @@ def test_are_results_certified_initializes_correctly(self):
# Wait for the object to be loaded
self._ensure_eventually_asserts(
- ScrapableElectionPage.objects.filter(slug='cuteslug').exists
- )
+ lambda: self.assertTrue(
+ ScrapableElectionPage.objects.filter(
+ slug='cuteslug').exists()))
# Then ensure all scrapers have areResultsCertified
scrapableElectionPage = ScrapableElectionPage.objects.get(slug='cuteslug')
diff --git a/infra/.env.template b/infra/.env.template
new file mode 100644
index 00000000..1c17e2f3
--- /dev/null
+++ b/infra/.env.template
@@ -0,0 +1,52 @@
+### The following fields must be provided to run RCVis locally and visualize results.
+
+# a user-generated string
+export RCVIS_SECRET_KEY=''
+
+# set to True to enable debug mode
+export RCVIS_DEBUG=True
+
+# host IP address
+export RCVIS_HOST=localhost
+
+# toggle offline mode
+export OFFLINE_MODE=True
+
+# email address for local admin in offline mode
+export OFFLINE_ADMIN='name@domain.com'
+
+### The following fields are optional, though you will have limited functionality without them.
+### These limitations are suitable for local development, but must be set up when deploying -- including cache management, registration email management, and mailing list management.
+
+# As an alternative to using OFFLINE_MODE=True, you may uncomment the following block to use AWS storage instead of local storage
+# export OFFLINE_MODE=False
+# export AWS_STORAGE_BUCKET_NAME=''
+# export AWS_S3_REGION_NAME=''
+# export AWS_ACCESS_KEY_ID=''
+# export AWS_SECRET_ACCESS_KEY=''
+
+# To send registration emails when OFFLINE_MODE is False:
+# export SENDGRID_USERNAME=''
+# export SENDGRID_PASSWORD=''
+
+# To clear cloudflare cache when models update:
+# export CLOUDFLARE_ZONE_ID=''
+# export CLOUDFLARE_AUTH_TOKEN=''
+
+# To run the SauceLabs integration tests:
+export SAUCE_USERNAME=''
+export SAUCE_ACCESS_KEY=''
+
+# To generate videos (and to run movie tests):
+export IMAGEIO_FFMPEG_EXE='/usr/bin/ffmpeg'
+export SQS_QUEUE_NAME='default-queue'
+# export MOVIE_FONT_NAME="Roboto"
+# export AWS_POLLY_STORAGE_BUCKET_NAME="bucket-name-on-s3"
+
+# To subscribe users to mailchimp upon registration:
+# export MAILCHIMP_API_KEY=''
+# export MAILCHIMP_LIST_ID=''
+# export MAILCHIMP_DC=''
+
+# If you are updating a template, you'll need to clear the cache every time or set:
+# export DISABLE_CACHE=True
diff --git a/infra/requirements-core.txt b/infra/requirements-core.txt
index f7f72a0a..5d92b9bd 100644
--- a/infra/requirements-core.txt
+++ b/infra/requirements-core.txt
@@ -1,4 +1,4 @@
-boto3==1.17.100
+boto3==1.26.0
csscompressor==0.9.5
django-admin-cursor-paginator==0.1.3
django-compressor==4.3.1
diff --git a/scripts/install.sh b/scripts/install.sh
index bfb35b26..e43e87fe 100755
--- a/scripts/install.sh
+++ b/scripts/install.sh
@@ -8,6 +8,20 @@ pip3 install -r requirements.txt
# Install npm deps
npm i
+# initialize .env
+if [ -f .env ]; then
+ echo ""
+ echo ".env file already exists."
+ echo ""
+else
+ # Copy the template to .env
+ cp infra/.env.template .env
+ echo ""
+ echo ".env file has been initialized from the template."
+ echo "Please modify the .env file to include the necessary values."
+ echo ""
+fi
+
# Run migrations to set up db
source .env
python3 manage.py migrate
diff --git a/scripts/serve.sh b/scripts/serve.sh
index 477b3630..09ad7cd4 100755
--- a/scripts/serve.sh
+++ b/scripts/serve.sh
@@ -2,9 +2,24 @@
# runserver, along with everything that goes along with it
set -e
+# Source the .env file to get the environment variables
source .env
+
+# Activate the virtual environment
source venv/bin/activate
+# Check if the admin user with the email specified in .env exists
+admin_exists=$(python manage.py shell -c "from django.contrib.auth import get_user_model; User = get_user_model(); print(User.objects.filter(email='$OFFLINE_ADMIN', is_superuser=True).exists())")
+
+# Prompt the user to create the admin if not
+if [ "$admin_exists" = "False" && "$OFFLINE_MODE" = "True" ]; then
+ echo "Admin user with email $OFFLINE_ADMIN does not exist. Please create one."
+ python manage.py createsuperuser --email $OFFLINE_ADMIN
+else
+ echo "Admin user with email $OFFLINE_ADMIN already exists."
+fi
+
+# Clear cache, compress and run the server
python3 manage.py clearCache
python3 manage.py compress
-python3 manage.py runserver
+python3 manage.py runserver
\ No newline at end of file