Skip to content

Commit

Permalink
Merge branch '529-mps-standing-down' into staging
Browse files Browse the repository at this point in the history
  • Loading branch information
struan committed Apr 17, 2024
2 parents ed26e1b + 107544d commit 506fbb6
Show file tree
Hide file tree
Showing 19 changed files with 1,253 additions and 683 deletions.
6 changes: 6 additions & 0 deletions .env-example
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@ MAPIT_URL="https://mapit.mysociety.org/"
MAPIT_API_KEY=""
GOOGLE_ANALYTICS=""
GOOGLE_SITE_VERIFICATION=""
MAILCHIMP_MYSOC_KEY=""
MAILCHIMP_MYSOC_SERVER_PREFIX=""
MAILCHIMP_MYSOC_LIST_ID=""
MAILCHIMP_TCC_KEY=""
MAILCHIMP_TCC_SERVER_PREFIX=""
MAILCHIMP_TCC_LIST_ID=""
8 changes: 8 additions & 0 deletions conf/env-example
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
SECRET_KEY="secr3t-k3y"
GOOGLE_ANALYTICS=""
GOOGLE_SITE_VERIFICATION=""
MAILCHIMP_MYSOC_KEY=""
MAILCHIMP_MYSOC_SERVER_PREFIX=""
MAILCHIMP_MYSOC_LIST_ID=""
MAILCHIMP_MYSOC_DATA_UPDATE_TAG=""
MAILCHIMP_MYSOC_CLIMATE_INTEREST=""
MAILCHIMP_TCC_KEY=""
MAILCHIMP_TCC_SERVER_PREFIX=""
MAILCHIMP_TCC_LIST_ID=""
18 changes: 18 additions & 0 deletions hub/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
BooleanField,
CharField,
EmailField,
Form,
ModelForm,
modelformset_factory,
)
Expand Down Expand Up @@ -158,3 +159,20 @@ def confirm_login_allowed(self, user):
self.error_messages["inactive"],
code="inactive",
)


class MailingListSignupForm(Form):
email = EmailField(label="Email")
full_name = CharField()
mysoc_signup = BooleanField(
required=False,
label=mark_safe(
'mySociety <a href="https://www.mysociety.org/privacy/">(privacy policy)</a>'
),
)
tcc_signup = BooleanField(
required=False,
label=mark_safe(
'The Climate Coalition <a href="https://www.theclimatecoalition.org/privacy-policy">(privacy policy)</a>'
),
)
90 changes: 90 additions & 0 deletions hub/management/commands/import_mps_standing_down_2024.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from django.conf import settings
from django.core.management.base import BaseCommand

import pandas as pd
from tqdm import tqdm

from hub.models import AreaType, DataSet, DataType, Person, PersonData


class Command(BaseCommand):
help = "Import MPs standing down in 2024"
data_file = settings.BASE_DIR / "data" / "mps_standing_down_2024.xlsx"
data_url = (
"https://researchbriefings.files.parliament.uk/documents/CBP-9808/CBP-9808.xlsx"
)

name_map = {
"John Cruddas": "Jon Cruddas",
"Matt Hancock": "Matthew Hancock",
"William Cash": "Bill Cash",
"Robert Neill": "Bob Neill",
}

def get_area_type(self):
return AreaType.objects.get(code="WMC")

def handle(self, quiet=False, *args, **options):
self._quiet = quiet
self.data_types = self.create_data_types()
df = self.get_df()
self.import_results(df)

def get_person_from_name(self, name):
name = self.name_map.get(name, name)
try:
person = Person.objects.get(name=name, person_type="MP")
return person
except Person.DoesNotExist:
self.stderr.write(f"could not find a person for {name}")
return None

def get_df(self):
df = pd.read_excel(self.data_file, header=1, sheet_name="MPs standing down")
df = df.dropna(subset=["Name"])
df.Name = df.Name.str.strip()
df.Name = df.Name.str.replace("Sir |Dame |Dr ", "", regex=True)
df["mp"] = df.Name.apply(lambda name: self.get_person_from_name(name))
return df[["mp", "Date announced"]]

def create_data_types(self):
options = [{"title": "Standing Down", "shader": "purple-500"}]
data_types = {}

ds, created = DataSet.objects.update_or_create(
name="mp_standing_down_2024",
defaults={
"data_type": "string",
"label": "MP standing down at the 2024 election",
"source_label": "Data from the House of Commons Library.",
"release_date": "April 2024",
"source": "https://commonslibrary.parliament.uk/research-briefings/cbp-9808/",
"data_url": "https://researchbriefings.files.parliament.uk/documents/CBP-9808/CBP-9808.xlsx",
"table": "person__persondata",
"options": options,
"subcategory": "",
"comparators": DataSet.comparators_default(),
"public": True,
},
)
ds.areas_available.add(self.get_area_type())
data_type, created = DataType.objects.update_or_create(
data_set=ds,
name="mp_standing_down_2024",
area_type=self.get_area_type(),
defaults={"data_type": "text"},
)

data_types["mp_standing_down_2024"] = data_type

return data_types

def import_results(self, df):
self.stdout.write("Importing MPs standing down")
for index, row in tqdm(df.iterrows(), disable=self._quiet):
if not pd.isna(row.mp):
data, created = PersonData.objects.update_or_create(
person=row.mp,
data_type=self.data_types["mp_standing_down_2024"],
data="Standing Down",
)
21 changes: 21 additions & 0 deletions hub/management/commands/mailchimp_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from django.conf import settings
from django.core.management.base import BaseCommand

import mailchimp_marketing as MailchimpMarketing
from mailchimp_marketing.api_client import ApiClientError


class Command(BaseCommand):
def handle(self, *args, **kwargs):
try:
client = MailchimpMarketing.Client()
client.set_config(
{
"api_key": settings.MAILCHIMP_MYSOC_KEY,
"server": settings.MAILCHIMP_MYSOC_SERVER_PREFIX,
}
)
response = client.ping.get()
print(response)
except ApiClientError as error:
print(error)
14 changes: 14 additions & 0 deletions hub/static/css/_area.scss
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,17 @@
[data-copy-text][data-copied] {
animation: 500ms linear success-ping;
}

// Mailing list signup on area page
// TODO: This is super hacky, maybe we should make a component for this?
.row > * + * > .mailing-list-signup {
margin-top: map-get($spacers, 4);
padding-top: map-get($spacers, 4);
border-top: var(--#{$prefix}border-width) var(--#{$prefix}border-style) var(--#{$prefix}border-color);

@include media-breakpoint-up('lg') {
margin-top: 0;
padding-top: 0;
border-top: none;
}
}
16 changes: 16 additions & 0 deletions hub/static/css/_utilities.scss
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,22 @@ $utilities: map-merge(
),
),
),
"max-width": map-merge(
map-get($utilities, "max-width"),
(
values: (
50: 50%,
100: 100%,
10rem: 10rem, // 160px, 3-6 words
20rem: 20rem, // 320px, 6-9 words
30rem: 30rem, // 480px, 9-12 words
35rem: 35rem,
40rem: 40rem, // 640px, 12-15 words
45rem: 45rem,
50rem: 50rem,
),
),
),
"position": map-merge(
map-get($utilities, "position"),
(
Expand Down
4 changes: 2 additions & 2 deletions hub/static/js/explore.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ const app = createApp({
fillColor: feature.properties.color,
fillOpacity: feature.properties.opacity,
color: 'white',
weight: 2,
weight: 1,
opacity: 1,
}
},
Expand All @@ -447,7 +447,7 @@ const app = createApp({
layer.bindTooltip(feature.properties.name)
layer.on({
mouseover: (e) => { e.target.setStyle({ weight: 5 }) },
mouseout: (e) => { e.target.setStyle({ weight: 2}) },
mouseout: (e) => { e.target.setStyle({ weight: 1 }) },
click: (e) => {
trackEvent('explore_area_click', {
'area_type': feature.properties.type,
Expand Down
68 changes: 68 additions & 0 deletions hub/static/js/home.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,42 @@ import $ from 'jquery/dist/jquery.slim'
import Collapse from 'bootstrap/js/dist/collapse'
import trackEvent from './analytics.esm.js'

async function mailingListSignup($form) {
const response = await fetch($form.attr('action'), {
method: $form.attr('method') || 'GET',
mode: 'cors',
credentials: 'same-origin',
body: $form.serialize(),
headers: {
"Content-Type": 'application/x-www-form-urlencoded',
"Accept": 'application/json; charset=utf-8',
},
})
return response.json()
}

var setUpCollapsableMailingListForm = function() {
var $form = $(this);
var selectors = '.js-mailing-list-name, .js-mailing-list-extras';
var trigger = '.js-mailing-list-email input#email';
var instances = [];

var updateUI = function() {
var emailEntered = $(trigger).val() !== '';
$.each(instances, function(i, instance){
emailEntered ? instance.show() : instance.hide();
});
};

$(selectors, $form).addClass('collapse').each(function(){
instances.push(new Collapse(this, { toggle: false }));
});
$(trigger, $form).on('keyup change', function(){
updateUI();
});
updateUI();
};

$(function(){
if( 'geolocation' in navigator ) {
$('.js-geolocate').removeClass('d-none');
Expand Down Expand Up @@ -85,4 +121,36 @@ $(function(){
window.location.href = href;
});
})

$('.js-collapsable-mailing-list-form').each(setUpCollapsableMailingListForm);

$('.js-mailing-list-signup').on('submit', function(e){
e.preventDefault();
var $form = $(this);
$('.invalid-feedback').remove()
mailingListSignup($form).then(function(response){
if (response['response'] == 'ok') {
$form.hide()
$('.js-mailing-list-success').removeClass('d-none')
} else {
console.log(response)
for (var k in response["errors"]) {
var id = '#' + k
var el = $(id)
el.addClass('is-invalid')
var error_el = $('<div>')
error_el.addClass('invalid-feedback d-block fs-6 mt-2')
error_el.html( '<p>' + response["errors"][k].join(", ") + '</p>' )
el.after(error_el)
}

if ("mailchimp" in response["errors"]) {
var error_el = $('<div>')
error_el.addClass('invalid-feedback d-block fs-6 mt-2')
error_el.html( '<p>There was a problem signing you up, please try again.</p>' )
$form.before(error_el)
}
}
});
})
})
30 changes: 20 additions & 10 deletions hub/templates/hub/area.html
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,9 @@ <h4 class="h6 text-muted fw-bold">APPGs</h4>
{% endfor %}
</ul>
{% endif %}
{% if mp.mp_standing_down_2024 == 'Standing Down' %}
<h4 class="h6 text-muted fw-bold">Standing down at next election</h4>
{% endif %}
</div>
<div class="card-footer">
<p class="card-text">Data from <a href="https://www.parliament.uk/about/mps-and-lords/members/">UK Parliament</a>{% if mp.job_titles %} and <a href="https://green-alliance.org.uk/">Green Alliance</a>{% endif %}.</p>
Expand Down Expand Up @@ -663,18 +666,25 @@ <h3 class="h5">{{ dataset.label }}</h3>
</div>
</div>

{% if not user.is_authenticated %}
<div class="py-4 py-lg-5 bg-yellow-100 border-top d-print-none">
<aside class="py-4 py-lg-5 bg-yellow-100 border-top d-print-none">
<div class="container">
<aside class="d-flex align-items-center">
{% include 'hub/includes/icons/tcc-heart.html' with width="3em" height="3em" classes="me-3 me-lg-4 flex-shrink-0 flex-grow-0" %}
<div>
<h2 class="h4">Climate Coalition member?</h2>
<p class="mb-0"><a href="{% url 'login' %}">Sign in</a> or <a href="{% url 'signup' %}">request an account</a> to access exclusive members-only MRP and local movement data.</p>
<div class="row">
{% if not user.is_authenticated %}
<div class="col-lg-5 me-lg-auto">
<div class="d-flex align-items-center">
{% include 'hub/includes/icons/tcc-heart.html' with width="3em" height="3em" classes="me-3 me-lg-4 flex-shrink-0 flex-grow-0" %}
<div>
<h2 class="h4">Climate Coalition member?</h2>
<p class="mb-0"><a href="{% url 'login' %}">Sign in</a> or <a href="{% url 'signup' %}">request an account</a> to access exclusive members-only MRP and local movement data.</p>
</div>
</div>
</div>
</aside>
{% endif %}
<div class="col-lg-6">
{% include 'hub/includes/mailing-list-form.html' with classes="js-collapsable-mailing-list-form" %}
</div>
</div>
</div>
</div>
{% endif %}
</aside>

{% endblock %}
48 changes: 48 additions & 0 deletions hub/templates/hub/includes/mailing-list-form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<form class="mailing-list-signup js-mailing-list-signup {{ classes }}" method="POST" action="{% url 'mailing_list_signup' %}">
{% csrf_token %}
<h2 class="h4 mt-0">Want to know when we add new data?</h2>
<fieldset class="mw-30rem">
<legend class="float-none mb-0">Tell us your email address and we’ll notify you when we add new data to the Local Intelligence Hub.</legend>
<div class="mt-3 js-mailing-list-email">
<label class="form-label" for="email">Email address</label>
<input type="email" name="email" id="email" class="form-control" autocomplete="email" spellcheck="false">
{% if form.errors.email %}
<div class="invalid-feedback d-block fs-6 mt-2 mb-n3">
{{ form.errors.email }}
</div>
{% endif %}
</div>
<div class="mt-3 js-mailing-list-name">
<label class="form-label" for="full_name">Name</label>
<input type="text" name="full_name" id="full_name" class="form-control">
{% if form.errors.full_name %}
<div class="invalid-feedback d-block fs-6 mt-2 mb-n3">
{{ form.errors.full_name }}
</div>
{% endif %}
</div>
</fieldset>
<fieldset class="mt-4 js-mailing-list-extras mw-30rem">
<legend>You can also, optionally, join our newsletters for more inspiration on data, democracy, and climate action:</legend>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="yes" name="mysoc_signup" id="mysoc_signup">
<label class="form-check-label" for="mysoc_signup">
mySociety newsletter
<a href="https://www.mysociety.org/privacy/" target="_blank" title="Opens in a new window">(privacy policy)</a>
</label>
</div>
<div class="form-check mt-2">
<input class="form-check-input" type="checkbox" value="yes" name="tcc_signup" id="tcc_signup">
<label class="form-check-label" for="tcc_signup">
The Climate Coalition newsletter
<a href="https://www.theclimatecoalition.org/privacy-policy" target="_blank" title="Opens in a new window">(privacy policy)</a>
</label>
</div>
</fieldset>
<button type="submit" class="btn btn-primary mt-4">Subscribe</button>
</form>

<div class="d-none js-mailing-list-success">
<h2 class="h4 mt-0">Thanks for signing up!</h2>
<p class="mw-30rem">We’ll let you know when there’s new data we think you’ll be interested in, on the Local Intelligence Hub.</p>
</div>
Loading

0 comments on commit 506fbb6

Please sign in to comment.