Skip to content
This repository has been archived by the owner on Sep 3, 2024. It is now read-only.

Commit

Permalink
Merge pull request #31 from DemocracyClub/remove-signup-server
Browse files Browse the repository at this point in the history
Slim down project to only use event_bridge
  • Loading branch information
symroe authored Sep 26, 2023
2 parents 20f0eec + 77447c9 commit ce23bc3
Show file tree
Hide file tree
Showing 32 changed files with 282 additions and 830 deletions.
45 changes: 45 additions & 0 deletions .github/workflows/run_tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: Tests

on:
push:

jobs:
build:

runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12.0-rc.3"]
django-version: [">4.2,<5", "==5.0a1"]
exclude:
# exclude Django 5 on Python 3.9
- python-version: "3.9"
django-version: "==5.0a1"

steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Update Pip
run: |
python -m pip install --upgrade pip
- name: Install dependencies
run: |
python -m pip install "Django${{ matrix.django-version }}"
python -m pip install .
python -m pip install -r testing_requirements.txt
- name: Lint
run: |
# stop the build if there are Python syntax errors or undefined names
black . --check
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
ruff .
- name: Test with pytest
run: |
python run_tests.py
51 changes: 11 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
# DC Signup Form

[![Build Status](https://travis-ci.org/DemocracyClub/dc_signup_form.svg?branch=master)](https://travis-ci.org/DemocracyClub/dc_signup_form)
[![Coverage Status](https://coveralls.io/repos/github/DemocracyClub/dc_signup_form/badge.svg?branch=master)](https://coveralls.io/github/DemocracyClub/dc_signup_form?branch=master)
![Tests](https://github.com/DemocracyClub/dc_signup_form/actions/workflows/run_tests.yaml/badge.svg) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)

Django app with Email signup form for use on Democracy Club websites. Currently, this app is used on:
* [Democracy Club](https://democracyclub.org.uk)
* [Who Can I Vote For?](https://whocanivotefor.co.uk)
* [Where Do I Vote?](https://wheredoivote.co.uk)



## Installation
Expand All @@ -18,17 +17,18 @@ git+git://github.com/DemocracyClub/dc_signup_form.git
```

This project depends on [`dc_django_utils`](https://github.com/DemocracyClub/dc_django_utils)
and we assume this is already set up on the project
and we assume this is already set up on the project.

For deployments in AWS you need to ensure that the role has
`events:put_events` permission on the EventBridge ARN.

## Configuration

For all backends, `dc_signup_form` needs to be in `INSTALLED_APPS` and
`dc_signup_form` needs to be in `INSTALLED_APPS` and
`dc_signup_form.context_processors.signup_form` needs to be added as a
context processor.


Using AWS EventBridge (recommended):

```python

EMAIL_SIGNUP_BACKEND = "event_bridge"
Expand All @@ -40,45 +40,15 @@ EMAIL_SIGNUP_BACKEND_KWARGS = {
```

Note that `bus_arn` needs to change for dev, stage and prod accounts. It's
recomended to take this from the environment when running the app.

Using the remote backend (deprecated):

```python
recommended to take this from the environment when running the app.

EMAIL_SIGNUP_API_KEY = 'f00b42'
EMAIL_SIGNUP_ENDPOINT = 'https://foo.bar/baz/'
```

Using the local backend (deprecated):

```python
INSTALLED_APPS = [
...
'dc_signup_form.signup_server',
]


SENDGRID_API_KEY = 'f00b42'
```

## Usage

Default routes:

```python
url(r'^emails/', include('dc_signup_form.urls')),
```

Routes for local backend:
```python
url(r'^emails/api_signup/', include('dc_signup_form.signup_server.urls')),
```

Custom routes:

```python
from dc_signup_form.views import SignupFormView
from dc_signup_form.forms import MailingListSignupForm

email_urls = [
url(r'^$',
Expand All @@ -90,7 +60,8 @@ email_urls = [
'source': 'EveryElection',
},
thanks_message="My custom thanks message",
backend='local_db'
backend='event_bridge',
backend_kwargs=settings.EMAIL_SIGNUP_BACKEND_KWARGS
),
name='mailing_list_signup_view'),
]
Expand Down
47 changes: 14 additions & 33 deletions dc_signup_form/backends.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
from dataclasses import dataclass
import requests
import json
from django.conf import settings
from dc_signup_form.signup_server.models import SignupQueue
from dataclasses import dataclass


class TestBackend:
Expand All @@ -15,30 +12,6 @@ def submit(self, data, mailing_lists):
)


class LocalDbBackend:
def submit(self, data, mailing_lists):
record = SignupQueue(
email=data["email"], data=data, mailing_lists=mailing_lists
)
record.save()


class RemoteDbBackend:
def submit(self, data, mailing_lists):
key = getattr(settings, "EMAIL_SIGNUP_API_KEY", "")
url = getattr(settings, "EMAIL_SIGNUP_ENDPOINT", "")

headers = {"Authorization": key}

payload = {
"data": data,
"mailing_lists": mailing_lists,
}

r = requests.post(url, data=json.dumps(payload), headers=headers)
r.raise_for_status()


@dataclass
class EmailSubscriber:
name: str
Expand All @@ -56,9 +29,13 @@ def __post_init__(self):
raise ValueError("Name cannot contain an email address")
if not isinstance(self.list_uuids, list):
raise ValueError("'list_uuids' must be a list")
self.list_uuids = list([int(x) for x in self.list_uuids])
self.list_uuids = [int(x) for x in self.list_uuids]

def as_listmonk_json(self):
if self.source:
if not self.extra_context:
self.extra_context = {}
self.extra_context["source_url"] = self.source
return {
"email": self.email,
"name": self.name,
Expand All @@ -71,7 +48,9 @@ def as_listmonk_json(self):
class EventBridgeBackend:
def __init__(self, source=None, bus_arn=None):
if not source:
raise ValueError("'source' required. This should be the project name")
raise ValueError(
"'source' required. This should be the project name"
)

self.source = source

Expand Down Expand Up @@ -111,10 +90,12 @@ def list_name_to_list_id(self, dev_mode):

def submit(self, data, mailing_lists):
list_id_map = self.list_name_to_list_id(self.dev_mode)
list_ids = list([list_id_map[list_name] for list_name in mailing_lists])

list_ids = [list_id_map[list_name] for list_name in mailing_lists]
subscriber = EmailSubscriber(
email=data["email"], name=data["full_name"], list_uuids=list_ids
email=data["email"],
name=data["full_name"],
list_uuids=list_ids,
source=data.get("source_url"),
)

self.client.put_events(
Expand Down
13 changes: 2 additions & 11 deletions dc_signup_form/context_processors.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .forms import ElectionRemindersSignupForm, MailingListSignupForm
from .constants import MAILING_LIST_FORM_PREFIX, ELECTION_REMINDERS_FORM_PREFIX
from .constants import MAILING_LIST_FORM_PREFIX
from .forms import MailingListSignupForm


def signup_form(request):
Expand All @@ -13,15 +13,6 @@ def signup_form(request):
else:
mailing_list_form = MailingListSignupForm(initial=initial)

if ELECTION_REMINDERS_FORM_PREFIX in request.POST:
election_reminders_form = ElectionRemindersSignupForm(
initial=initial,
data=request.POST,
)
else:
election_reminders_form = ElectionRemindersSignupForm(initial=initial)

return {
"mailing_list_form": mailing_list_form,
"election_reminders_form": election_reminders_form,
}
34 changes: 15 additions & 19 deletions dc_signup_form/forms.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,34 @@
from django import forms
from django.core.validators import RegexValidator

from .constants import ELECTION_REMINDERS_FORM_PREFIX, MAILING_LIST_FORM_PREFIX
from .constants import MAILING_LIST_FORM_PREFIX


def emails_not_accepted(value):
if '@' in value:
raise forms.ValidationError("Please enter your full name, not your email address.")
if "@" in value:
raise forms.ValidationError(
"Please enter your full name, not your email address."
)


class EmailSignupForm(forms.Form):
full_name = forms.CharField(
required=True,
max_length=1000,
required=True,
max_length=1000,
label="Full Name",
validators=[emails_not_accepted],
widget=forms.TextInput(attrs={'autocomplete': 'off', 'pattern': '[^@]+', 'title': 'Please enter your full name, not your email address.'})
widget=forms.TextInput(
attrs={
"autocomplete": "off",
"pattern": "[^@]+",
"title": "Please enter your full name, not your email address.",
}
),
)
email = forms.EmailField(required=True, max_length=255, label="Email")
source_url = forms.CharField(widget=forms.HiddenInput())

class ElectionRemindersSignupForm(EmailSignupForm):

prefix = ELECTION_REMINDERS_FORM_PREFIX

main_list = forms.BooleanField(
required=False,
initial=False,
label="Subscribe to the Democracy Club mailing list",
)
election_reminders = forms.BooleanField(initial=True, widget=forms.HiddenInput())


class MailingListSignupForm(EmailSignupForm):

prefix = MAILING_LIST_FORM_PREFIX

main_list = forms.BooleanField(initial=True, widget=forms.HiddenInput())
Empty file.
9 changes: 0 additions & 9 deletions dc_signup_form/signup_server/admin.py

This file was deleted.

Empty file.
Empty file.
Loading

0 comments on commit ce23bc3

Please sign in to comment.