Connects Labour campaign organisers with constituencies that are demographically similar to theirs and performed better than expected at the 2019 general election, so they can get in touch and share what worked.
Originally built by Robbie Sinclair (@velocirobbie) at Campaign Lab.
local_election_analysis/ Python model
data/ election results, 2011 census, CLP Twitter handles
localcampaigns.py builds the model and writes public/analysis.{json,js}
public/analysis.json model output (served at /analysis.json)
public/analysis.js same data as `var analysis = ...` for the browser
node.js Express server for local development
build.js static build for GitHub Pages -> dist/
views/ EJS templates
app/ browser JS: search, results, D3 charts
The server does no computation. Every page loads analysis.js and renders
in the browser.
npm install
npm start # http://localhost:5000 (PORT env var overrides)
npm run dev # auto-restart with nodemonThe site is fully static, so npm run build pre-renders every page into dist/
(index, methodology, about, 404.html, and /<constituency>/connections/ and
/<constituency>/info/ for all 571 seats).
.github/workflows/deploy-pages.yml builds and deploys on every push to main.
One-off setup in the repo on GitHub:
- Settings → Pages → Build and deployment → Source: GitHub Actions
- Push to
main(or run the workflow from the Actions tab). - Optional custom domain: add it under Settings → Pages → Custom domain and
create a
CNAMEDNS record pointing atcampaignlab.github.io. The workflow picks up the change on its next run (re-run it after adding the domain).
Links work both at a domain root and on campaignlab.github.io/campaign_connector/:
the workflow passes the right prefix to the build as BASE_PATH. To preview a
build locally:
npm run build && npx serve distcd local_election_analysis
pip install -r requirements.txt
python localcampaigns.pyThis overwrites public/analysis.json and public/analysis.js. Commit both.
- England and Wales only (the census data covers England and Wales). Scotland and Northern Ireland are not included; the Speaker's seats are excluded.
- 2010–2019 constituency boundaries; swing is Labour vote share 2019 minus 2017.
- See
/methodologyon the site for how the scores and recommendations are made.
campaignlabprojects/other_methods.py and cluster_analysis.py are earlier
experiments and are not used by the current model.