> Blockquotes are very handy in email to emulate reply text.
2 | > This line is part of the same quote.
3 |
Quote break.
4 |
> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can putMarkdown into a blockquote.
5 |
--------------------------------------------------------------------------------
/wcivf/settings/lambda.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from .base import * # noqa
4 |
5 | DATABASES["default"] = { # noqa
6 | "ENGINE": "django.db.backends.postgresql",
7 | "NAME": os.environ.get("RDS_DB_NAME"),
8 | "USER": "wcivf",
9 | "PASSWORD": os.environ.get("RDS_DB_PASSWORD"),
10 | "HOST": os.environ.get("RDS_HOST"),
11 | "PORT": os.environ.get("RDS_DB_PORT", "5432"),
12 | }
13 | EE_BASE = "https://elections.democracyclub.org.uk"
14 |
--------------------------------------------------------------------------------
/wcivf/apps/administrations/templates/responsibilities/single_id/O::LCE::mayor.html:
--------------------------------------------------------------------------------
1 | {% load markdown_filter %}
2 |
3 | {# fmt:off #}
4 | {% filter markdown %}
5 |
6 | The City Mayor has responsibility for all council decisions during their four-year term
7 | and for selecting up to nine councillors as a supporting cabinet.
8 |
9 | [Find more about the mayor](https://www.leicester.gov.uk/your-council/city-mayor-peter-soulsby/)
10 |
11 |
12 | {% endfilter %}
13 |
--------------------------------------------------------------------------------
/wcivf/apps/core/tests/forbidden_markdown/expected/code.txt:
--------------------------------------------------------------------------------
1 |
Inline `code` has `back-ticks around` it.
2 |
```javascript
3 | var s = "JavaScript syntax highlighting";
4 | alert(s);
5 | ```
6 |
```python
7 | s = "Python syntax highlighting"
8 | print s
9 | ```
10 |
```
11 | No language indicated, so no syntax highlighting.
12 | But let's throw in a <b>tag</b>.
13 | ```
{% blocktrans with person=person.name %}You can meet candidates and question them at events (often known as 'hustings'). Here are some events where {{ person }} may be appearing:{% endblocktrans %}
6 | {% include "hustings/includes/_list.html" with hustings=hustings %}
7 |
8 |
9 |
--------------------------------------------------------------------------------
/wcivf/apps/leaflets/migrations/0002_auto_20210603_0922.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.20 on 2021-06-03 09:22
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 | dependencies = [
8 | ("leaflets", "0001_initial"),
9 | ]
10 |
11 | operations = [
12 | migrations.AlterField(
13 | model_name="leaflet",
14 | name="thumb_url",
15 | field=models.URLField(blank=True, max_length=800, null=True),
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/people/migrations/0040_person_blog_url.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.7 on 2022-01-26 18:21
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 | dependencies = [
8 | ("people", "0039_auto_20210928_1303"),
9 | ]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="person",
14 | name="blog_url",
15 | field=models.CharField(blank=True, max_length=800, null=True),
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/people/templates/people/includes/intros/_votes_cast.html:
--------------------------------------------------------------------------------
1 | {% load humanize %}
2 | {% load i18n %}
3 |
4 | {% if candidacy.elected %}
5 | {% blocktrans trimmed with num_votes=candidacy.votes_cast|intcomma %}
6 | They were elected with {{ num_votes }} votes.
7 | {% endblocktrans %}
8 | {% else %}
9 | {% blocktrans trimmed with num_votes=candidacy.votes_cast|intcomma %}
10 | They received {{ num_votes }} votes.
11 | {% endblocktrans %}
12 | {% endif %}
13 |
--------------------------------------------------------------------------------
/deploy/files/systemd/db_replication.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Manages DB replication subscription
3 | Before=${PROJECT_NAME}_gunicorn.service
4 | After=postgresql.service
5 |
6 | [Service]
7 | User=${PROJECT_NAME}
8 | Type=oneshot
9 | RemainAfterExit=true
10 | ExecStart=${PROJECT_ROOT}/setup_db_replication.sh
11 | ExecStop=${PROJECT_ROOT}/remove_db_replication.sh
12 |
13 | StandardOutput=file:/var/log/db_replication/logs.log
14 | StandardError=file:/var/log/db_replication/logs.log
15 |
16 | [Install]
17 | WantedBy=multi-user.target
--------------------------------------------------------------------------------
/wcivf/apps/people/migrations/0043_personpost_rank.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.1.6 on 2023-05-05 09:01
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 | dependencies = [
8 | ("people", "0042_personpost_previous_party_affiliations"),
9 | ]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="personpost",
14 | name="rank",
15 | field=models.PositiveIntegerField(null=True),
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: https://github.com/rtts/djhtml
3 | rev: 3.0.6
4 | hooks:
5 | - id: djhtml
6 | - repo: https://github.com/charliermarsh/ruff-pre-commit
7 | # Ruff version.
8 | rev: 'v0.4.6'
9 | hooks:
10 | - id: ruff
11 | args: [--fix, --exit-non-zero-on-fix, --extend-exclude, TCH]
12 | - id: ruff-format
13 | - repo: local
14 | hooks:
15 | - id: pip-check
16 | name: pip-check
17 | entry: pip check
18 | files: ^requirements\S*\.txt$
19 | language: system
20 |
--------------------------------------------------------------------------------
/CHANGELOG.txt:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
6 | ## [Unreleased]
7 |
8 | ## 2023-11-17
9 |
10 | ### Added
11 |
12 | - A Change log
13 |
14 | ### Changed
15 |
16 | - Upgraded DC logging client to version 1.0.1
17 |
18 | ### Removed
19 |
20 | - LoggedPostcode model and related code.
21 | Postcodes will no longer be logged in the database, and will now only be logged into AWS Firehose
22 |
--------------------------------------------------------------------------------
/wcivf/apps/administrations/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import re_path
2 |
3 | from .views import YourArea
4 |
5 | urlpatterns = [
6 | re_path(
7 | r"^(?P[^/]+)/(?P[^/]+)/$",
8 | YourArea.as_view(),
9 | name="your_area_view",
10 | ),
11 | re_path(
12 | r"^(?P[^/]+)/$",
13 | YourArea.as_view(),
14 | name="your_area_view",
15 | ),
16 | re_path(
17 | r"^$",
18 | YourArea.as_view(),
19 | name="your_area_view",
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/migrations/0038_default_winner_count.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.13 on 2022-05-23 15:19
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 | dependencies = [
8 | ("elections", "0037_dummypostelection"),
9 | ]
10 |
11 | operations = [
12 | migrations.AlterField(
13 | model_name="postelection",
14 | name="winner_count",
15 | field=models.IntegerField(blank=True, default=1),
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/parishes/migrations/0002_alter_parishcouncilelection_is_contested.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2 on 2021-09-28 13:03
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 | dependencies = [
8 | ("parishes", "0001_initial"),
9 | ]
10 |
11 | operations = [
12 | migrations.AlterField(
13 | model_name="parishcouncilelection",
14 | name="is_contested",
15 | field=models.BooleanField(null=True),
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/people/migrations/0009_auto_20170303_1823.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.6 on 2017-03-03 18:23
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("people", "0008_remove_person_party")]
10 |
11 | operations = [
12 | migrations.AlterModelOptions(
13 | name="personpost",
14 | options={"ordering": ("-election__election_date",)},
15 | )
16 | ]
17 |
--------------------------------------------------------------------------------
/wcivf/apps/people/migrations/0044_person_mastodon_username.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2 on 2023-07-11 13:51
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 | dependencies = [
8 | ("people", "0043_personpost_rank"),
9 | ]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="person",
14 | name="mastodon_username",
15 | field=models.CharField(blank=True, max_length=800, null=True),
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/parties/migrations/0020_party_alternative_name.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.1.6 on 2023-06-14 15:25
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 | dependencies = [
8 | ("parties", "0019_alter_partydescription_options_and_more"),
9 | ]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="party",
14 | name="alternative_name",
15 | field=models.CharField(max_length=765, null=True),
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/people/migrations/0024_personpost_elected.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.7 on 2017-06-06 15:51
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("people", "0023_auto_20170605_1559")]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="personpost",
14 | name="elected",
15 | field=models.NullBooleanField(),
16 | )
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/administrations/templates/responsibilities/single_id/O::WECA::mayor.html:
--------------------------------------------------------------------------------
1 | {% load markdown_filter %}
2 |
3 | {# fmt:off #}
4 | {% filter markdown %}
5 | The Mayor of the West of England is the directly elected mayor who leads the West of England Combined Authority. The body, a combined authority, is responsible for the strategic administration of the West of England, including planning, transport and skills.
6 |
7 | [Find more about the mayor and combined authority](https://www.westofengland-ca.gov.uk/what-we-do/)
8 |
9 |
10 | {% endfilter %}
11 |
--------------------------------------------------------------------------------
/wcivf/apps/feedback/migrations/0002_feedback_token.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.7 on 2017-04-29 19:42
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("feedback", "0001_initial")]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="feedback",
14 | name="token",
15 | field=models.CharField(blank=True, max_length=100),
16 | )
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/people/migrations/0005_person_wikipedia_bio.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.4 on 2016-04-14 12:28
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("people", "0004_auto_20160414_0927")]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="person",
14 | name="wikipedia_bio",
15 | field=models.TextField(null=True),
16 | )
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/parishes/management/commands/import_parish_council_elections.py:
--------------------------------------------------------------------------------
1 | from django.core.management.base import BaseCommand
2 | from parishes.importers import ParishCouncilElectionImporter
3 |
4 |
5 | class Command(BaseCommand):
6 | URL = "https://docs.google.com/spreadsheets/d/e/2PACX-1vTbfedaTDoTT3UcJOcOJHojISaSb06yzjFNvTiIUxHd7oWsN0wqFNqHXoklcTeK2G7aoUUH9NHvWy0q/pub?gid=756401424&single=true&output=csv"
7 |
8 | def handle(self, **options):
9 | importer = ParishCouncilElectionImporter(url=self.URL)
10 | importer.import_objects()
11 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/migrations/0009_election_for_post_role.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.4 on 2016-04-30 19:41
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("elections", "0008_auto_20160405_1412")]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="election",
14 | name="for_post_role",
15 | field=models.TextField(blank=True),
16 | )
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/migrations/0040_postelection_requires_voter_id.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.1.6 on 2023-03-22 11:03
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 | dependencies = [
8 | ("elections", "0039_remove_historic_metadata"),
9 | ]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="postelection",
14 | name="requires_voter_id",
15 | field=models.CharField(blank=True, max_length=50, null=True),
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/hustings/migrations/0003_husting_video_url.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.7 on 2017-06-02 10:28
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("hustings", "0002_auto_20170601_2155")]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="husting",
14 | name="video_url",
15 | field=models.URLField(blank=True, null=True),
16 | )
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/hustings/migrations/0005_auto_20210326_1111.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.18 on 2021-03-26 11:11
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 | dependencies = [
8 | ("hustings", "0004_auto_20170602_1031"),
9 | ]
10 |
11 | operations = [
12 | migrations.AlterField(
13 | model_name="husting",
14 | name="postevent_url",
15 | field=models.URLField(blank=True, default=""),
16 | preserve_default=False,
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/wcivf/apps/people/migrations/0021_person_twfy_id.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.7 on 2017-06-02 15:17
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("people", "0020_person_favourite_biscuit")]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="person",
14 | name="twfy_id",
15 | field=models.IntegerField(blank=True, null=True),
16 | )
17 | ]
18 |
--------------------------------------------------------------------------------
/deploy/files/systemd/gunicorn.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=WCIVF gunicorn daemon
3 | After=network.target
4 | [Service]
5 | User=wcivf
6 | Group=wcivf
7 | WorkingDirectory=/var/www/wcivf/code/
8 | ExecStart=/bin/bash -c 'PATH=/var/www/wcivf/code/.venv/bin/:$PATH exec /var/www/wcivf/code/.venv/bin/gunicorn \
9 | --access-logfile - \
10 | --workers 3 \
11 | --bind 0.0.0.0:8000 \
12 | --worker-class=gevent \
13 | wcivf.wsgi:application'
14 | ExecReload=/bin/kill -s HUP $MAINPID
15 | ExecStop=/bin/kill -s TERM $MAINPID
16 | [Install]
17 | WantedBy=multi-user.target
18 |
--------------------------------------------------------------------------------
/wcivf/apps/administrations/templates/responsibilities/single_id/O::MAS::mayor.html:
--------------------------------------------------------------------------------
1 |
2 | {% load markdown_filter %}
3 |
4 | {# fmt:off #}
5 | {% filter markdown %}
6 | It is the job of the Mayor to represent the council and its residents, make key decisions on policies, services and how the council spends its money.
7 |
8 | The Mayor is supported by a Cabinet of councillors who help him or her develop and implement these policies.
9 |
10 | [Find more about the Mayor](https://www.mansfield.gov.uk/council-councillors-democracy/meet-mayor-1)
11 |
12 |
13 | {% endfilter %}
14 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/migrations/0016_postelection_contested.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.6 on 2017-03-22 16:59
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("elections", "0015_auto_20170304_1354")]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="postelection",
14 | name="contested",
15 | field=models.BooleanField(default=True),
16 | )
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/migrations/0018_postelection_locked.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.10 on 2018-03-04 13:18
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("elections", "0017_parl-2017-06-08-weight")]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="postelection",
14 | name="locked",
15 | field=models.BooleanField(default=False),
16 | )
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/parties/migrations/0002_auto_20160412_1739.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.4 on 2016-04-12 17:39
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("parties", "0001_initial")]
10 |
11 | operations = [
12 | migrations.AlterField(
13 | model_name="party",
14 | name="emblem",
15 | field=models.ImageField(null=True, upload_to="parties/emblems"),
16 | )
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/people/migrations/0011_person_statement_to_voters.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.6 on 2017-04-28 10:58
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("people", "0010_auto_20170306_1206")]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="person",
14 | name="statement_to_voters",
15 | field=models.TextField(null=True),
16 | )
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/templates/elections/includes/_elections_breadcrumbs.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 |
17 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/migrations/0005_election_election_type.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.4 on 2016-03-26 10:39
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("elections", "0004_auto_20160326_1038")]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="election",
14 | name="election_type",
15 | field=models.CharField(blank=True, max_length=100),
16 | )
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/migrations/0006_votingsystem_uses_lists.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.4 on 2016-04-04 18:06
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("elections", "0005_election_election_type")]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="votingsystem",
14 | name="uses_lists",
15 | field=models.BooleanField(default=False),
16 | )
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/people/migrations/0013_auto_20170515_2047.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.7 on 2017-05-15 20:47
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("people", "0012_auto_20170515_1641")]
10 |
11 | operations = [
12 | migrations.AlterField(
13 | model_name="person",
14 | name="ynr_id",
15 | field=models.CharField(db_index=True, max_length=255, unique=True),
16 | )
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/people/migrations/0020_person_favourite_biscuit.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.7 on 2017-06-02 14:04
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("people", "0019_associatedcompany")]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="person",
14 | name="favourite_biscuit",
15 | field=models.CharField(max_length=800, null=True),
16 | )
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/people/migrations/0034_auto_20191213_0942.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.7 on 2019-12-13 09:42
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 | dependencies = [("people", "0033_facebookadvert")]
8 |
9 | operations = [
10 | migrations.AlterModelOptions(
11 | name="facebookadvert",
12 | options={
13 | "get_latest_by": "ad_json__ad_delivery_start_time",
14 | "ordering": ("-ad_json__ad_delivery_start_time",),
15 | },
16 | )
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/people/migrations/0047_person_statement_to_voters_last_updated.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.10 on 2024-03-20 14:40
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 | dependencies = [
8 | ("people", "0046_personpost_deselected_personpost_deselected_source"),
9 | ]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="person",
14 | name="statement_to_voters_last_updated",
15 | field=models.DateTimeField(null=True),
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/migrations/0033_auto_20210210_1422.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.13 on 2021-02-10 14:22
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 | dependencies = [
8 | ("elections", "0032_auto_20210210_1155"),
9 | ]
10 |
11 | operations = [
12 | migrations.AlterField(
13 | model_name="post",
14 | name="ynr_id",
15 | field=models.CharField(
16 | max_length=100, primary_key=True, serialize=False
17 | ),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/wcivf/apps/ppc_2024/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 | from django.views.generic import RedirectView
3 |
4 | urlpatterns = [
5 | path(
6 | "",
7 | RedirectView.as_view(
8 | url="https://whocanivotefor.co.uk/elections/parl.2024-07-04/uk-parliament-elections/"
9 | ),
10 | name="home",
11 | ),
12 | path(
13 | "details/",
14 | RedirectView.as_view(
15 | url="https://whocanivotefor.co.uk/elections/parl.2024-07-04/uk-parliament-elections/"
16 | ),
17 | name="details",
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/wcivf/apps/administrations/tests/test_admistration_object.py:
--------------------------------------------------------------------------------
1 | # from administrations.helpers import BASE_DATA_PATH, Administration
2 | #
3 | #
4 | # def test_all_files_parse():
5 | # for filename in BASE_DATA_PATH.glob("*.json"):
6 | # admin_class = Administration(filename.stem)
7 | # has_template = False
8 | # try:
9 | # admin_class.responsibilities_template()
10 | # has_template = True
11 | # except:
12 | # raise
13 | # if not has_template:
14 | # print(admin_class.admin_id, admin_class.role_name())
15 |
--------------------------------------------------------------------------------
/wcivf/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for wcivf project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | import dotenv
13 | from django.core.wsgi import get_wsgi_application
14 |
15 | dotenv.read_dotenv(
16 | os.path.join(os.path.dirname(os.path.dirname(__file__)), ".env")
17 | )
18 |
19 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "wcivf.settings")
20 |
21 | application = get_wsgi_application()
22 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/migrations/0020_postelection_ballot_paper_id.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.12 on 2018-04-17 16:12
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("elections", "0019_election_metadata")]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="postelection",
14 | name="ballot_paper_id",
15 | field=models.CharField(blank=True, max_length=800),
16 | )
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/people/migrations/0027_person_party_ppc_page_url.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.12 on 2018-05-03 12:20
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("people", "0026_auto_20180417_1944")]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="person",
14 | name="party_ppc_page_url",
15 | field=models.CharField(blank=True, max_length=800, null=True),
16 | )
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/migrations/0021_postelection_winner_count.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.12 on 2018-05-02 09:46
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("elections", "0020_postelection_ballot_paper_id")]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="postelection",
14 | name="winner_count",
15 | field=models.IntegerField(blank=True, null=True),
16 | )
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/migrations/0024_default_for_has_by_elections.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.20 on 2019-03-21 19:22
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("elections", "0023_election_any_non_by_elections")]
10 |
11 | operations = [
12 | migrations.AlterField(
13 | model_name="election",
14 | name="any_non_by_elections",
15 | field=models.BooleanField(default=False),
16 | )
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/feedback/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import re_path
2 |
3 | from .views import (
4 | FeedbackFormView,
5 | NoElectionFeedbackFormView,
6 | RecordJsonFeedback,
7 | )
8 |
9 | urlpatterns = [
10 | re_path(
11 | r"^submit_initial",
12 | RecordJsonFeedback.as_view(),
13 | name="json_feedback_view",
14 | ),
15 | re_path(r"^$", FeedbackFormView.as_view(), name="feedback_form_view"),
16 | re_path(
17 | r"^no_election$",
18 | NoElectionFeedbackFormView.as_view(),
19 | name="no_election_feedback_form_view",
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/wcivf/apps/administrations/templates/responsibilities/post_type/GLA_C.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {% load markdown_filter %}
4 |
5 | {# fmt:off #}
6 | {% filter markdown %}
7 | The Assembly holds the Mayor and Mayoral advisers to account by publicly examining policies and programmes through committee meetings, plenary sessions, site visits and investigations.
8 |
9 | Eleven Assembly Members represent the whole capital and 14 are elected by constituencies
10 |
11 | [Read more about what the London Assembly](https://www.london.gov.uk/who-we-are/what-london-assembly-does/about-london-assembly)
12 |
13 | {% endfilter %}
14 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/migrations/0017_parl-2017-06-08-weight.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.7 on 2017-06-08 18:21
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations
6 |
7 |
8 | def change_weight(apps, schema_editor):
9 | Election = apps.get_model("elections", "Election")
10 | Election.objects.filter(slug="parl.2017-06-08").update(election_weight=100)
11 |
12 |
13 | class Migration(migrations.Migration):
14 | dependencies = [("elections", "0016_postelection_contested")]
15 |
16 | operations = [migrations.RunPython(change_weight)]
17 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/migrations/0027_unique_on_ballot_id.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.20 on 2019-05-04 11:05
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("elections", "0026_postelection_voting_system")]
10 |
11 | operations = [
12 | migrations.AlterField(
13 | model_name="postelection",
14 | name="ballot_paper_id",
15 | field=models.CharField(blank=True, max_length=800, unique=True),
16 | )
17 | ]
18 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/migrations/0032_auto_20210210_1155.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.13 on 2021-02-10 11:55
2 |
3 | from django.db import migrations
4 |
5 |
6 | def forwards(apps, schema_editor):
7 | Post = apps.get_model("elections", "Post")
8 | count, _ = Post.objects.filter(ynr_id="").delete()
9 |
10 |
11 | class Migration(migrations.Migration):
12 | dependencies = [
13 | ("elections", "0031_post_division_type"),
14 | ]
15 |
16 | operations = [
17 | migrations.RunPython(
18 | code=forwards, reverse_code=migrations.RunPython.noop
19 | )
20 | ]
21 |
--------------------------------------------------------------------------------
/wcivf/apps/people/migrations/0010_auto_20170306_1206.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.6 on 2017-03-06 12:06
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [
10 | ("elections", "0015_auto_20170304_1354"),
11 | ("people", "0009_auto_20170303_1823"),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterUniqueTogether(
16 | name="personpost",
17 | unique_together={("person", "post", "election")},
18 | )
19 | ]
20 |
--------------------------------------------------------------------------------
/wcivf/apps/administrations/templates/responsibilities/post_type/UTW.html:
--------------------------------------------------------------------------------
1 | {% load markdown_filter %}
2 |
3 | {# fmt:off #}
4 | {% filter markdown %}
5 | Unitary authorities are responsible for services like:
6 |
7 | * education
8 | * transport
9 | * planning
10 | * fire and public safety
11 | * social care
12 | * libraries
13 | * waste management
14 | * trading standards
15 | * rubbish collection
16 | * recycling
17 | * Council Tax collections
18 | * housing
19 | * planning applications
20 |
21 |
22 | [Understand how your council works](https://www.gov.uk/understand-how-your-council-works)
23 | {% endfilter %}
24 |
--------------------------------------------------------------------------------
/wcivf/apps/peoplecvs/migrations/0003_delete_cvs.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.20 on 2021-04-19 13:32
2 |
3 | from django.apps.registry import apps
4 | from django.db import migrations
5 |
6 |
7 | def delete_cvs(app, schema_editor):
8 | CV = apps.get_model("peoplecvs", "CV")
9 | CV.objects.all().delete()
10 |
11 |
12 | class Migration(migrations.Migration):
13 | dependencies = [
14 | ("peoplecvs", "0002_auto_20170522_1324"),
15 | ]
16 |
17 | operations = [
18 | migrations.RunPython(
19 | code=delete_cvs, reverse_code=migrations.RunPython.noop
20 | )
21 | ]
22 |
--------------------------------------------------------------------------------
/wcivf/apps/core/middleware.py:
--------------------------------------------------------------------------------
1 | class UTMTrackerMiddleware(object):
2 | def __init__(self, get_response):
3 | self.get_response = get_response
4 |
5 | def __call__(self, request):
6 | self.process_request(request)
7 | return self.get_response(request)
8 |
9 | def process_request(self, request):
10 | def _get_value_from_req(key):
11 | return (key, request.GET.get(key, None))
12 |
13 | keys = ("utm_source", "utm_medium", "utm_campaign")
14 | utm_data = {k: v for k, v in map(_get_value_from_req, keys) if v}
15 | request.session["utm_data"] = utm_data
16 |
--------------------------------------------------------------------------------
/wcivf/apps/feedback/migrations/0011_alter_noelectionfeedback_options.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.20 on 2025-08-28 12:10
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 | dependencies = [
8 | ("feedback", "0010_noelectionfeedback"),
9 | ]
10 |
11 | operations = [
12 | migrations.AlterModelOptions(
13 | name="noelectionfeedback",
14 | options={
15 | "verbose_name": "No Election Page Feedback",
16 | "verbose_name_plural": "No Election Page Feedback",
17 | },
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/wcivf/apps/core/utils.py:
--------------------------------------------------------------------------------
1 | from django.db.models import Transform
2 |
3 |
4 | class LastWord(Transform):
5 | """
6 | Split a field on space and get the last element
7 | """
8 |
9 | function = "LastWord"
10 | template = """
11 | (regexp_split_to_array(%(field)s, ' '))[
12 | array_upper(regexp_split_to_array(%(field)s, ' '), 1)
13 | ]
14 | """
15 |
16 | def __init__(self, column, output_field=None):
17 | super(LastWord, self).__init__(column, output_field=output_field)
18 |
19 | def as_postgresql(self, compiler, connection):
20 | return self.as_sql(compiler, connection)
21 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/migrations/0019_election_metadata.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.10 on 2018-04-10 12:33
3 | from __future__ import unicode_literals
4 |
5 | import django.contrib.postgres.fields.jsonb
6 | from django.db import migrations
7 |
8 |
9 | class Migration(migrations.Migration):
10 | dependencies = [("elections", "0018_postelection_locked")]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name="election",
15 | name="metadata",
16 | field=django.contrib.postgres.fields.jsonb.JSONField(null=True),
17 | )
18 | ]
19 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/migrations/0023_election_any_non_by_elections.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.20 on 2019-03-21 18:45
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("elections", "0022_auto_20181031_1603")]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="election",
14 | name="any_non_by_elections",
15 | field=models.BooleanField(default=False),
16 | preserve_default=False,
17 | )
18 | ]
19 |
--------------------------------------------------------------------------------
/wcivf/apps/administrations/templates/responsibilities/post_type/PCC.html:
--------------------------------------------------------------------------------
1 | {% load markdown_filter %}
2 |
3 | {# fmt:off #}
4 | {% filter markdown %}
5 | Police and crime commissioners are elected in areas of England and Wales to make sure that local police meet the needs of the community.
6 |
7 | They are responsible for:
8 |
9 | * how your area is policed
10 | * the police budget
11 | * the amount of Council Tax charged for the police
12 | * the information you get about what the local police are doing
13 | * appointing and dismissing the chief constable (the most senior police officer for the area)
14 |
15 | {% endfilter %}
16 |
--------------------------------------------------------------------------------
/wcivf/apps/feedback/migrations/0005_feedback_vote.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.7 on 2021-11-23 15:54
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 | dependencies = [
8 | ("feedback", "0004_feedback_sources"),
9 | ]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="feedback",
14 | name="vote",
15 | field=models.CharField(
16 | blank=True,
17 | choices=[("YES", "Yes"), ("NO", "No")],
18 | max_length=100,
19 | ),
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/wcivf/apps/core/migrations/0002_auto_20170526_1448.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.7 on 2017-05-26 14:48
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations
6 |
7 |
8 | def add_site(apps, schema_editor):
9 | Site = apps.get_model("sites", "Site")
10 |
11 | site = Site(
12 | pk=1, name="whocanivotefor.co.uk", domain="whocanivotefor.co.uk"
13 | )
14 | site.save()
15 |
16 |
17 | class Migration(migrations.Migration):
18 | dependencies = [("core", "0001_initial"), ("sites", "0001_initial")]
19 |
20 | operations = [migrations.RunPython(add_site)]
21 |
--------------------------------------------------------------------------------
/wcivf/apps/people/migrations/0012_auto_20170515_1641.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.7 on 2017-05-15 16:41
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("people", "0011_person_statement_to_voters")]
10 |
11 | operations = [
12 | migrations.RemoveField(model_name="person", name="photo"),
13 | migrations.AddField(
14 | model_name="person",
15 | name="photo_url",
16 | field=models.URLField(blank=True, null=True),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/wcivf/apps/administrations/templates/responsibilities/single_id/O::SLF::mayor.html:
--------------------------------------------------------------------------------
1 | {% load markdown_filter %}
2 |
3 | {# fmt:off #}
4 | {% filter markdown %}
5 |
6 | The City Mayor leads the council and has overall responsibility for the delivery of all council services.
7 |
8 | They develop budgets to meet the needs of the city's residents, through the city plan, make
9 | sure the work of the council is conducted in accordance with the constitution and the law and that all decisions are
10 | open, transparent and understandable.
11 |
12 | [Find more about the mayor's role](https://www.salford.gov.uk/cmrole)
13 |
14 |
15 | {% endfilter %}
16 |
--------------------------------------------------------------------------------
/wcivf/apps/administrations/templates/responsibilities/single_id/O::london::mayor.html:
--------------------------------------------------------------------------------
1 | {% load markdown_filter %}
2 |
3 | {# fmt:off #}
4 | {% filter markdown %}
5 | The Mayor sets an overall vision for London. He has a duty to create plans and policies for the capital covering:
6 |
7 | * Arts & Culture
8 | * Business & Economy
9 | * Environment
10 | * Fire
11 | * Health
12 | * Housing and Land
13 | * Planning
14 | * Policing & Crime
15 | * Regeneration
16 | * Sport
17 | * Transport
18 | * Young People
19 |
20 | [Read more about what the Mayor of London does](https://www.london.gov.uk/who-we-are/what-mayor-does)
21 |
22 | {% endfilter %}
23 |
--------------------------------------------------------------------------------
/wcivf/apps/core/tests/forbidden_markdown/input/tables.txt:
--------------------------------------------------------------------------------
1 | Colons can be used to align columns.
2 |
3 | | Tables | Are | Cool |
4 | | ------------- |:-------------:| -----:|
5 | | col 3 is | right-aligned | $1600 |
6 | | col 2 is | centered | $12 |
7 | | zebra stripes | are neat | $1 |
8 |
9 | There must be at least 3 dashes separating each header cell.
10 | The outer pipes (|) are optional, and you don't need to make the
11 | raw Markdown line up prettily. You can also use inline Markdown.
12 |
13 | Markdown | Less | Pretty
14 | --- | --- | ---
15 | *Still* | `renders` | **nicely**
16 | 1 | 2 | 3
17 |
--------------------------------------------------------------------------------
/wcivf/apps/administrations/templates/responsibilities/single_id/O::WAT::mayor.html:
--------------------------------------------------------------------------------
1 | {% load markdown_filter %}
2 |
3 | {# fmt:off #}
4 | {% filter markdown %}
5 |
6 | The Mayor of Watford is a directly elected and the council's principal
7 | public spokesperson, representing the council and the borough on local,
8 | national and international platforms. The Mayor is responsible for:
9 |
10 |
11 | * the overall political direction of the council
12 | * the scheme of delegation for Executive functions
13 | * Chairing the Cabinet
14 |
15 |
16 | [Find more about the mayor](https://www.watford.gov.uk/electedmayor)
17 |
18 |
19 | {% endfilter %}
20 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/migrations/0011_auto_20170303_1823.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.6 on 2017-03-03 18:23
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("elections", "0010_auto_20160502_1542")]
10 |
11 | operations = [
12 | migrations.RemoveField(model_name="post", name="election"),
13 | migrations.AddField(
14 | model_name="post",
15 | name="elections",
16 | field=models.ManyToManyField(to="elections.Election"),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/wcivf/apps/parties/migrations/0010_localparty_is_local.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.18 on 2021-03-29 14:58
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 | dependencies = [
8 | ("parties", "0009_manifesto_easy_read_url"),
9 | ]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="localparty",
14 | name="is_local",
15 | field=models.BooleanField(
16 | default=True,
17 | help_text="Used to identify if obj is related to a local election",
18 | ),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/wcivf/apps/parties/migrations/0011_replace_emblem_with_emblem_url.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.7 on 2021-11-05 15:53
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 | dependencies = [
8 | ("parties", "0010_localparty_is_local"),
9 | ]
10 |
11 | operations = [
12 | migrations.RemoveField(
13 | model_name="party",
14 | name="emblem",
15 | ),
16 | migrations.AddField(
17 | model_name="party",
18 | name="emblem_url",
19 | field=models.URLField(blank=True, null=True),
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/migrations/0007_auto_20160405_0857.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.4 on 2016-04-05 08:57
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("elections", "0006_votingsystem_uses_lists")]
10 |
11 | operations = [
12 | migrations.RemoveField(model_name="votingsystem", name="uses_lists"),
13 | migrations.AddField(
14 | model_name="election",
15 | name="uses_lists",
16 | field=models.BooleanField(default=False),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/migrations/0037_dummypostelection.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.10 on 2022-03-09 15:25
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 | dependencies = [
8 | ("elections", "0036_add_timestamps"),
9 | ]
10 |
11 | operations = [
12 | migrations.CreateModel(
13 | name="DummyPostElection",
14 | fields=[],
15 | options={
16 | "proxy": True,
17 | "indexes": [],
18 | "constraints": [],
19 | },
20 | bases=("elections.postelection",),
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/templates/elections/includes/_postcode_search_form.html:
--------------------------------------------------------------------------------
1 | {% load dc_forms %}
2 | {% load i18n %}
3 |
4 |
5 |
6 |
{% trans "All elections where you live" %}
7 |
{% trans "Enter your postcode to get information about elections, your candidates and where to vote." %}
| Tables | Are | Cool |
3 | | ------------- |:-------------:| -----:|
4 | | col 3 is | right-aligned | $1600 |
5 | | col 2 is | centered | $12 |
6 | | zebra stripes | are neat | $1 |
7 |
There must be at least 3 dashes separating each header cell.
8 | The outer pipes (|) are optional, and you don't need to make the
9 | raw Markdown line up prettily. You can also use inline Markdown.
6 | {% for candidacy in candidacies %}
7 | {% blocktrans trimmed with person_name=person.name count counter=candidacy.previous_party_affiliations.count %}
8 | {{ person_name }} has declared their affiliation with the following party in the past 12 months:
9 | {% plural %}
10 | {{ person_name}} has declared their affiliation with the following parties in the past 12 months:
11 | {% endblocktrans %}
12 | {% for previous_party in candidacy.previous_party_affiliations.all %}
13 | {{ previous_party.party_name }}{% if forloop and not forloop.last %}, {% endif %}
14 | {% endfor %}
15 | {% endfor %}
16 |
17 |
--------------------------------------------------------------------------------
/wcivf/apps/administrations/templates/responsibilities/single_id/O::west-yorkshire::mayor.html:
--------------------------------------------------------------------------------
1 | {% load markdown_filter %}
2 |
3 | {# fmt:off #}
4 | {% filter markdown %}
5 |
6 | The West Yorkshire Combined Authority brings together the local authorities of Bradford, Calderdale, Kirklees, Leeds
7 | and Wakefield. It develops and delivers policies, programmes and services which directly
8 | benefit the people of West Yorkshire.
9 |
10 | The Mayor of West Yorkshire is a directly elected mayor responsible for the metropolitan county of West Yorkshire in
11 | England. The Mayor chairs and leads the West Yorkshire Combined Authority, and assumes the office and powers of the
12 | West Yorkshire Police and Crime Commissioner.
13 |
14 |
15 | [Find more about the mayor's role](https://www.westyorks-ca.gov.uk/about-us/)
16 |
17 |
18 | {% endfilter %}
19 |
--------------------------------------------------------------------------------
/wcivf/apps/feedback/templates/feedback/admin/change_list.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/change_list.html" %}
2 | {% block object-tools %}
3 |
16 | {% endblock %}
17 |
--------------------------------------------------------------------------------
/wcivf/apps/people/templates/people/includes/intros/_mayor_or_pcc.html:
--------------------------------------------------------------------------------
1 | {% extends "people/includes/intros/base.html" %}
2 | {% load i18n %}
3 |
4 | {% block intro_line %}
5 | {% if verb == "is" %}
6 | {% blocktrans trimmed %}
7 | {{ person_name }} is the {{ party_name }}
8 | candidate for {{ election_name }}.
9 | {% endblocktrans %}
10 | {% else %}
11 | {% blocktrans trimmed %}
12 | {{ person_name }} was the {{ party_name }}
13 | candidate for {{ election_name }}.
14 | {% endblocktrans %}
15 |
16 | {% endif %}
17 |
18 |
19 | {% endblock %}
20 |
--------------------------------------------------------------------------------
/wcivf/apps/hustings/admin.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from django.contrib import admin
3 |
4 | from .models import Husting
5 |
6 |
7 | class HustingAdmin(admin.ModelAdmin):
8 | list_display = ["title", "status", "starts", "ends", "url"]
9 | list_filter = ("starts", "status")
10 | search_fields = ("post_election__ballot_paper_id",)
11 | autocomplete_fields = ["post_election"]
12 | date_hierarchy = "starts"
13 |
14 | def formfield_for_dbfield(self, db_field, request, **kwargs):
15 | if db_field.name in ["title", "url", "location", "postevent_url"]:
16 | kwargs["widget"] = forms.Textarea
17 | if db_field.name == "status":
18 | kwargs["widget"] = forms.RadioSelect
19 | return super().formfield_for_dbfield(db_field, request, **kwargs)
20 |
21 |
22 | admin.site.register(Husting, HustingAdmin)
23 |
--------------------------------------------------------------------------------
/wcivf/apps/people/migrations/0002_auto_20160412_1739.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.4 on 2016-04-12 17:39
3 | from __future__ import unicode_literals
4 |
5 | import django.db.models.deletion
6 | from django.db import migrations, models
7 |
8 |
9 | class Migration(migrations.Migration):
10 | dependencies = [
11 | ("parties", "0002_auto_20160412_1739"),
12 | ("people", "0001_initial"),
13 | ]
14 |
15 | operations = [
16 | migrations.RemoveField(model_name="person", name="party_name"),
17 | migrations.AddField(
18 | model_name="person",
19 | name="party",
20 | field=models.ForeignKey(
21 | null=True,
22 | on_delete=django.db.models.deletion.CASCADE,
23 | to="parties.Party",
24 | ),
25 | ),
26 | ]
27 |
--------------------------------------------------------------------------------
/docs/sslcertificate.md:
--------------------------------------------------------------------------------
1 | ## SSL Certificate
2 |
3 | You will need to use the AWS Console to provision an SSL certificate to be used in the deployment. Follow the steps below to create the SSL certificate.
4 |
5 | - In the AWS console, go to Certificate Manager
6 | - Change the region to US East (N. Virginia) (us-east-1)
7 | - Choose "Request a Certificate" and select public certificate
8 | - Add the domain and optionally a wildcard for the sub domain e.g. ‘wcivf.club’ and ‘* wcivf.club’
9 | - Choose DNS validation, add any tags, and confirm
10 | - On the review screen, open the domain and click the button “Create record in Route 53”. Repeat for any additional domains you added. This will create DNS records in route 53 to validate the certificate.
11 | - The ARN of the created certificate is then used when deploying to a new environment - this is outlined elsewhere in the documentation.
12 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/postal_votes.py:
--------------------------------------------------------------------------------
1 | import csv
2 | from datetime import datetime
3 | from pathlib import Path
4 |
5 |
6 | def get_postal_vote_dispatch_dates(council_id):
7 | with open(
8 | Path(__file__).parent / "data" / "20250501_postal_votes.csv"
9 | ) as csvfile:
10 | reader = csv.DictReader(csvfile)
11 | dispatch_dates = {row.pop("Reg"): row for row in list(reader)}
12 |
13 | if council_id not in dispatch_dates:
14 | return None
15 |
16 | try:
17 | row = dispatch_dates[council_id]
18 | return [
19 | datetime.strptime(row["Date 1"], "%d/%m/%Y").date(),
20 | datetime.strptime(row["Date 2"], "%d/%m/%Y").date(),
21 | datetime.strptime(row["Date 3"], "%d/%m/%Y").date(),
22 | ]
23 | except ValueError:
24 | return None
25 |
--------------------------------------------------------------------------------
/wcivf/apps/people/templates/people/includes/intros/_constituency.html:
--------------------------------------------------------------------------------
1 | {% extends "people/includes/intros/base.html" %}
2 | {% load i18n %}
3 | {% block intro_line %}
4 | {% if verb == "is" %}
5 | {% blocktrans trimmed %}
6 | {{ name }} is the {{ party_name }} candidate for {{ post_label }} constituency in the
7 | {{ election_name }}.
8 | {% endblocktrans %}
9 | {% else %}
10 | {% blocktrans trimmed %}
11 | {{ name }} was the {{ party_name }} candidate for {{ post_label }} constituency in the
12 | {{ election_name }}.
13 | {% endblocktrans %}
14 | {% endif %}
15 | {% endblock %}
16 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/migrations/0013_auto_20170304_1354.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.6 on 2017-03-04 13:54
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 | dependencies = [("elections", "0012_auto_20170304_1351")]
10 |
11 | operations = [
12 | migrations.AddField(
13 | model_name="post",
14 | name="elections2",
15 | field=models.ManyToManyField(
16 | through="elections.PostElection", to="elections.Election"
17 | ),
18 | ),
19 | migrations.AlterField(
20 | model_name="post",
21 | name="elections",
22 | field=models.ManyToManyField(
23 | related_name="fake", to="elections.Election"
24 | ),
25 | ),
26 | ]
27 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/templates/elections/includes/_contact_details_registration.html:
--------------------------------------------------------------------------------
1 | {% load postcode_tags %}
2 | {% load i18n %}
3 |
4 | {% if council and registration and council.address != registration.address and not postcode|ni_postcode %}
5 |
{% trans "Electoral Registration" %}
6 | {% include "elections/includes/_council_contact_details.html" with contact_details=registration with_address=True %}
7 |
{% trans "Your Local Council" %}
8 | {% include "elections/includes/_council_contact_details.html" with contact_details=council with_address=True %}
9 | {% elif council %}
10 | {% include "elections/includes/_council_contact_details.html" with contact_details=council with_address=True %}
11 | {% elif registration %}
12 | {% include "elections/includes/_council_contact_details.html" with contact_details=registration with_address=True %}
13 | {% endif %}
14 |
--------------------------------------------------------------------------------
/wcivf/apps/hustings/README.md:
--------------------------------------------------------------------------------
1 | A husting originally referred to a native Germanic governing assembly, the thing.
2 |
3 | By metonymy, the term may now refer to any event, such as debates or speeches,
4 | during an election campaign where one or more of the representative candidates are present.
5 | The term is used synonymously with stump in the United States.
6 |
7 | **Or so says [Wikipedia](https://en.wikipedia.org/wiki/Husting)**
8 |
9 |
10 | To load the hustings from a Google Sheet check the URLS in `import_hustings` are correct.
11 |
12 | Then run `python manage.py import_hustings`.
13 |
14 | You can also import hustings from a csv file by using the `--filename` flag and passing the
15 | path to the csv file. However this will delete all existing hustings and only create those
16 | from within the file, so be careful when using this - it is mainly intended to help with
17 | local development.
18 |
--------------------------------------------------------------------------------
/wcivf/apps/people/templates/people/dummy_statements/rhuanedd-llewelyn.txt:
--------------------------------------------------------------------------------
1 | I am a qualified doctor and a volunteer with Llantalbot Good Neighbours. I am a founder and trustee of Llantalbot Pride. I chose to settle in Llantalbot because I loved the town for its warm feel. I felt part of community. I have believed in my town and proud to be in Llantalbot.
2 |
3 | One issue close to my heart is Llantalbot park. I love this park - it's where I used to take my children when they were young. But over recent years it's been plagued by vandalism. If you choose me to represent you, I'll make sure that the concerns about the park are heard. It needs a bit of love and care, including new fences, new play equipment, and the gates to be opened and locked in the mornings and evenings, just like many other parks have across the county. I woud also love to see more cycle paths to help our town get healthier and more eco-friendly.
4 |
--------------------------------------------------------------------------------
/wcivf/apps/parties/tests/factories.py:
--------------------------------------------------------------------------------
1 | import factory
2 | from elections.tests.factories import PostElectionFactory
3 | from parties.models import LocalParty, NationalParty, Party
4 |
5 |
6 | class PartyFactory(factory.django.DjangoModelFactory):
7 | class Meta:
8 | model = Party
9 | django_get_or_create = ("party_id",)
10 |
11 | party_id = "PP01"
12 | party_name = "Test Party"
13 |
14 |
15 | class LocalPartyFactory(factory.django.DjangoModelFactory):
16 | parent = factory.SubFactory(PartyFactory)
17 | post_election = factory.SubFactory(PostElectionFactory)
18 |
19 | class Meta:
20 | model = LocalParty
21 |
22 |
23 | class NationalPartyFactory(factory.django.DjangoModelFactory):
24 | parent = factory.SubFactory(PartyFactory)
25 | post_election = factory.SubFactory(PostElectionFactory)
26 |
27 | class Meta:
28 | model = NationalParty
29 |
--------------------------------------------------------------------------------
/wcivf/apps/core/tests/forbidden_markdown/input/links.txt:
--------------------------------------------------------------------------------
1 | [I'm an inline-style link](https://www.google.com)
2 |
3 | [I'm an inline-style link with title](https://www.google.com "Google's Homepage")
4 |
5 | [I'm a reference-style link][Arbitrary case-insensitive reference text]
6 |
7 | [I'm a relative reference to a repository file](../blob/master/LICENSE)
8 |
9 | [You can use numbers for reference-style link definitions][1]
10 |
11 | Or leave it empty and use the [link text itself].
12 |
13 | URLs and URLs in angle brackets will automatically get turned into links.
14 | http://www.example.com or and sometimes
15 | example.com (but not on Github, for example).
16 |
17 | Some text to show that the reference links can follow later.
18 |
19 | [arbitrary case-insensitive reference text]: https://www.mozilla.org
20 | [1]: http://slashdot.org
21 | [link text itself]: http://www.reddit.com
22 |
--------------------------------------------------------------------------------
/wcivf/apps/people/migrations/0030_insta-youtube.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.4 on 2019-10-31 22:32
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 | dependencies = [("people", "0029_person_last_updated")]
8 |
9 | operations = [
10 | migrations.AddField(
11 | model_name="person",
12 | name="instagram_id",
13 | field=models.CharField(blank=True, max_length=800, null=True),
14 | ),
15 | migrations.AddField(
16 | model_name="person",
17 | name="instagram_url",
18 | field=models.CharField(blank=True, max_length=800, null=True),
19 | ),
20 | migrations.AddField(
21 | model_name="person",
22 | name="youtube_profile",
23 | field=models.CharField(blank=True, max_length=800, null=True),
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/wcivf/apps/people/templates/people/includes/_person_multiple_current_candidacies.html:
--------------------------------------------------------------------------------
1 | {% load i18n static %}
2 |
3 |
{{ person_name }}
4 | {% with party=candidacies.0.party %}
5 | {% blocktrans trimmed with party_name=party.party_name party_link=party.get_absolute_url a_or_an=party.is_independent|yesno:_("an,a") %}
6 | {{ verb }} {{ a_or_an }} {{ party_name }} candidate in the following elections:
7 | {% endblocktrans %}
8 | {% endwith %}
9 |
20 | {% endfor %}
21 |
--------------------------------------------------------------------------------
/wcivf/apps/elections/sitemaps.py:
--------------------------------------------------------------------------------
1 | from django.contrib.sitemaps import Sitemap
2 | from django.db.models import Q
3 |
4 | from .models import Election, PostElection
5 |
6 |
7 | class ElectionSitemap(Sitemap):
8 | changefreq = "weekly"
9 | priority = 0.5
10 | protocol = "https"
11 |
12 | def items(self):
13 | return Election.objects.all()
14 |
15 |
16 | class PostElectionSitemap(Sitemap):
17 | changefreq = "weekly"
18 | priority = 0.9
19 | protocol = "https"
20 |
21 | # Only include posts for general elections, since
22 | # otherwise the sitemap gets close to the Google limit.
23 | # of 50,000 URLs.
24 | def items(self):
25 | return PostElection.objects.filter(
26 | Q(election__election_type="parl")
27 | | Q(election__election_type="2010")
28 | | Q(election__election_type="2015")
29 | ).order_by("-election__election_date")
30 |
--------------------------------------------------------------------------------
/wcivf/apps/administrations/templates/responsibilities/single_id/O::MDB::mayor.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {% load markdown_filter %}
4 |
5 | {# fmt:off #}
6 | {% filter markdown %}
7 | The Mayor of Middlesbrough leads the Middlesbrough Council and represents the borough at local, regional, and national levels. The Mayor's responsibilities include setting strategic priorities, overseeing economic development, and managing budgets for public services. The Mayor also works to improve transportation, housing, education, and community safety. Additionally, the Mayor engages with residents, businesses, and other stakeholders to address local issues and promote the well-being of the community. The role involves both independent decision-making and collaboration with the council and other local authorities.
8 |
9 | [Find more about the council](https://www.middlesbrough.gov.uk/council-and-democracy/about-the-council/)
10 |
11 |
12 | {% endfilter %}
13 |
--------------------------------------------------------------------------------
/wcivf/apps/news_mentions/templates/news_mentions/news_articles.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
[I'm an inline-style link](https://www.google.com)
2 |
[I'm an inline-style link with title](https://www.google.com "Google's Homepage")
3 |
[I'm a reference-style link][Arbitrary case-insensitive reference text]
4 |
[I'm a relative reference to a repository file](../blob/master/LICENSE)
5 |
[You can use numbers for reference-style link definitions][1]
6 |
Or leave it empty and use the [link text itself].
7 |
URLs and URLs in angle brackets will automatically get turned into links.
8 | http://www.example.com or <http://www.example.com> and sometimes
9 | example.com (but not on Github, for example).
10 |
Some text to show that the reference links can follow later.