Skip to content

Commit

Permalink
DATASET: Flood risk data
Browse files Browse the repository at this point in the history
Also, a change to the _place_data template that means that datasets with
no actual data in them do not render empty cards
  • Loading branch information
alexander-griffen committed Aug 24, 2023
1 parent 4d1d857 commit 007d383
Show file tree
Hide file tree
Showing 2 changed files with 248 additions and 120 deletions.
107 changes: 107 additions & 0 deletions hub/management/commands/import_flood_risk_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from django.conf import settings
from django.core.management.base import BaseCommand

import pandas as pd
from tqdm import tqdm

from hub.models import Area, AreaData, DataSet, DataType


class Command(BaseCommand):
help = "Import flood risk data"

data_file = settings.BASE_DIR / "data" / "risk_of_flooding.csv"
message = "Importing constituency flood risk data"
defaults = {
"label": "Flood risk from rivers or sea",
"description": "Data relating to risk of flooding from rivers or sea, recorded in 2018",
"data_type": "percent",
"category": "place",
"source_label": "Defra",
"is_range": True,
"source_type": "csv",
"table": "areadata",
"exclude_countries": ["Northern Ireland", "Scotland", "Wales"],
"comparators": DataSet.numerical_comparators(),
"default_value": 10,
"is_shadable": False,
}

def add_arguments(self, parser):
parser.add_argument(
"-q", "--quiet", action="store_true", help="Silence progress bars."
)

def handle(self, quiet=False, *args, **options):
self._quiet = quiet
df = self.get_dataframe()
self.data_types = self.create_data_types(df)
self.delete_data()
self.import_data(df)

def create_data_types(self, df):
if not self._quiet:
self.stdout.write("Creating dataset + types")
data_set, created = DataSet.objects.update_or_create(
name="constituency_flood_risk", defaults=self.defaults
)
data_types = []
for col in tqdm(df.columns, disable=self._quiet):
data_type, created = DataType.objects.update_or_create(
data_set=data_set,
name=f"flood_risk_{col.lower().replace(' ', '_')}",
defaults={
"data_type": "percent",
"label": col,
"description": f"Percentage of the constituency marked as '{col}' risk of flooding from rivers or the sea",
},
)
data_types.append(data_type)

return data_types

def import_data(self, df):
if not self._quiet:
self.stdout.write("Importing flood risk data")
for gss, row in tqdm(df.iterrows(), disable=self._quiet):
try:
area = Area.objects.get(gss=gss)
except Area.DoesNotExist:
self.stdout.write(f"Failed to find area with code {gss}")
continue
for data_type in self.data_types:
AreaData.objects.create(
data_type=data_type,
area=area,
data=row[data_type.label],
)
for col in df.columns:
average = df[col].mean()
data_type = DataType.objects.get(
name=f"flood_risk_{col.lower().replace(' ', '_')}"
)
data_type.average = average
data_type.save()

def delete_data(self):
AreaData.objects.filter(data_type__in=self.data_types).delete()

def get_dataframe(self):
df = pd.read_csv(self.data_file)
totals = (
df.dropna()[["gss", "prob_4band"]]
.groupby("gss")
.count()
.prob_4band.to_dict()
)
df["total"] = df.gss.apply(lambda x: totals.get(x, None))
df = (
df.dropna()[["gss", "prob_4band", "total"]]
.groupby("gss")
.value_counts()
.reset_index()
.rename(columns={0: "value"})
)
df["percentage"] = df.value / df.total * 100
df = df.pivot(columns="prob_4band", values="percentage", index="gss").fillna(0)
return df
261 changes: 141 additions & 120 deletions hub/templates/hub/area/_place_data.html
Original file line number Diff line number Diff line change
@@ -1,131 +1,152 @@
{% load humanize %}
{% load hub_filters %}

<div class="col d-flex flex-column">
<div class="card flex-grow-1 dataset-card {% if category.is_favourite or category.data.is_favourite %}dataset-card--favourite{% endif %}">
<div class="dataset-card-header">
<h5>{{ category.name }}</h5>
{% include "hub/area/_favourite.html" %}
</div>
<div class="card-body">
{% if category.name == "Constituency age distribution" %}
<table class="table mb-0 js-chart" data-chart-type="bar" data-chart-direction="y">
<thead>
<tr>
<th scope="col">Age band</th>
<th scope="col" data-color="#068670">This area</th>
<th scope="col" data-color="#ced4da">UK average</th>
</tr>
</thead>
<tbody>
{% for range in category.data %}
<tr>
<th>{{ range.label }}</th>
<td>{{ range.value|floatformat }}{% if range.is_percentage %}%{% endif %}</td>
<td>{{ range.average|floatformat }}{% if range.is_percentage %}%{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% elif category.name == "Ethnicity" %}
<table class="table mb-0">
<thead>
<tr>
<th scope="col">Ethnicity</th>
<th scope="col" data-color="#068670">This area</th>
<th scope="col" data-color="#ced4da">UK average</th>
</tr>
</thead>
<tbody>
{% for range in category.data %}
<tr>
<th>{{ range.label }}</th>
<td>{{ range.value|floatformat }}{% if range.is_percentage %}%{% endif %}</td>
<td>{{ range.average|floatformat }}{% if range.is_percentage %}%{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% elif category.name == 'Socio-Economic Status' %}
<table class="table mb-0">
<thead>
<tr>
<th scope="col">Status</th>
<th scope="col">This area</th>
<th scope="col" class="text-muted">UK average</th>
</tr>
</thead>
<tbody>
{% for range in category.data %}
<tr>
<th>{{ range.label }}</th>
<td>{{ range.value|floatformat }}%</td>
<td class="text-muted">{{ range.average|floatformat }}%</td>
</tr>
{% endfor %}
</tbody>
</table>
{% elif category.data.data_type.name == 'constituency_popular_petitions' %}
<table class="table mb-0">
<thead>
<tr>
<th scope="col">Petition</th>
<th scope="col">Signatures</th>
</tr>
</thead>
<tbody>
{% for petition in category.data.value %}
<tr>
<td><a href="{{ petition.url }}">{{ petition.action }}</a></td>
<td class="text-end">{{ petition.signatures }}</td>
</tr>
{% endfor %}
</body>
</table>
{% elif category.name == 'Air pollution' %}
<table class="table mb-0">
<thead>
<tr>
<th scope="col">Pollutant</th>
<th scope="col">This area</th>
<th scope="col" class="text-muted">UK average</th>
</tr>
</thead>
<tbody>
{% if category.data %}
<div class="col d-flex flex-column">
<div class="card flex-grow-1 dataset-card {% if category.is_favourite or category.data.is_favourite %}dataset-card--favourite{% endif %}">
<div class="dataset-card-header">
<h5>{{ category.name }}</h5>
{% include "hub/area/_favourite.html" %}
</div>
<div class="card-body">
{% if category.name == "Constituency age distribution" %}
<table class="table mb-0 js-chart" data-chart-type="bar" data-chart-direction="y">
<thead>
<tr>
<th scope="col">Age band</th>
<th scope="col" data-color="#068670">This area</th>
<th scope="col" data-color="#ced4da">UK average</th>
</tr>
</thead>
<tbody>
{% for range in category.data %}
<tr>
<th>
{{ range.label|html_format_dataset_name|safe }}
{% if range.data_type.description %}
<small class="d-block mt-1 fs-8 fw-normal">{{ range.data_type.description|escape }}</small>
{% endif %}
</th>
<td>{{ range.value|floatformat }}%</td>
<td class="text-muted">{{ range.average|floatformat }}%</td>
</tr>
<tr>
<th>{{ range.label }}</th>
<td>{{ range.value|floatformat }}{% if range.is_percentage %}%{% endif %}</td>
<td>{{ range.average|floatformat }}{% if range.is_percentage %}%{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
{% if category.data.is_number %}
<p class="card-text mb-0 display-6 lh-1 text-primary">{{ category.data.value|floatformat }}{% if category.data.is_percentage %}%{% endif %}</p>
{% if category.data.average %}
<p class="card-text mt-2 text-muted">{{ category.data.average|floatformat }}{% if category.data.is_percentage %}%{% endif %} national average</p>
{% endif %}
</tbody>
</table>
{% elif category.name == "Ethnicity" %}
<table class="table mb-0">
<thead>
<tr>
<th scope="col">Ethnicity</th>
<th scope="col" data-color="#068670">This area</th>
<th scope="col" data-color="#ced4da">UK average</th>
</tr>
</thead>
<tbody>
{% for range in category.data %}
<tr>
<th>{{ range.label }}</th>
<td>{{ range.value|floatformat }}{% if range.is_percentage %}%{% endif %}</td>
<td>{{ range.average|floatformat }}{% if range.is_percentage %}%{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% elif category.name == 'Socio-Economic Status' %}
<table class="table mb-0">
<thead>
<tr>
<th scope="col">Status</th>
<th scope="col">This area</th>
<th scope="col" class="text-muted">UK average</th>
</tr>
</thead>
<tbody>
{% for range in category.data %}
<tr>
<th>{{ range.label }}</th>
<td>{{ range.value|floatformat }}%</td>
<td class="text-muted">{{ range.average|floatformat }}%</td>
</tr>
{% endfor %}
</tbody>
</table>
{% elif category.data.data_type.name == 'constituency_popular_petitions' %}
<table class="table mb-0">
<thead>
<tr>
<th scope="col">Petition</th>
<th scope="col">Signatures</th>
</tr>
</thead>
<tbody>
{% for petition in category.data.value %}
<tr>
<td><a href="{{ petition.url }}">{{ petition.action }}</a></td>
<td class="text-end">{{ petition.signatures }}</td>
</tr>
{% endfor %}
</body>
</table>
{% elif category.name == 'Air pollution' %}
<table class="table mb-0">
<thead>
<tr>
<th scope="col">Pollutant</th>
<th scope="col">This area</th>
<th scope="col" class="text-muted">UK average</th>
</tr>
</thead>
<tbody>
{% for range in category.data %}
<tr>
<th>
{{ range.label|html_format_dataset_name|safe }}
{% if range.data_type.description %}
<small class="d-block mt-1 fs-8 fw-normal">{{ range.data_type.description|escape }}</small>
{% endif %}
</th>
<td>{{ range.value|floatformat }}%</td>
<td class="text-muted">{{ range.average|floatformat }}%</td>
</tr>
{% endfor %}
</tbody>
</table>
{% elif category.name == 'Flood risk from rivers or sea' %}
<table class="table mb-0 js-chart" data-chart-type="bar" data-chart-direction="y"">
<thead>
<tr>
<th scope="col">Risk</th>
<th scope="col" data-color="#068670">This area</th>
<th scope="col" data-color="#ced4da">UK average</th>
</tr>
</thead>
<tbody>
{% for range in category.data %}
<tr>
<th>{{ range.label }}</th>
<td>{{ range.value|floatformat }}%</td>
<td class="text-muted">{{ range.average|floatformat }}%</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="card-text fs-5">{{ category.data.value }}</p>
{% if category.data.average %}
<p class="card-text mt-2 text-muted">{{ category.data.average }} national average</p>
{% if category.data.is_number %}
<p class="card-text mb-0 display-6 lh-1 text-primary">{{ category.data.value|floatformat }}{% if category.data.is_percentage %}%{% endif %}</p>
{% if category.data.average %}
<p class="card-text mt-2 text-muted">{{ category.data.average|floatformat }}{% if category.data.is_percentage %}%{% endif %} national average</p>
{% endif %}
{% else %}
<p class="card-text fs-5">{{ category.data.value }}</p>
{% if category.data.average %}
<p class="card-text mt-2 text-muted">{{ category.data.average }} national average</p>
{% endif %}
{% endif %}
{% endif %}
</div>
<div class="card-footer bg-white">
{% if category.source_url %}
<p class="card-text fs-8"><a href="{{ category.source_url }}" class="text-decoration-none text-muted">Data from {{ category.source }}</a></p>
{% else %}
<p class="card-text fs-8 text-muted">Data from {{ category.source }}</p>
{% endif %}
</div>
<div class="card-footer bg-white">
{% if category.source_url %}
<p class="card-text fs-8"><a href="{{ category.source_url }}" class="text-decoration-none text-muted">Data from {{ category.source }}</a></p>
{% else %}
<p class="card-text fs-8 text-muted">Data from {{ category.source }}</p>
{% endif %}
</div>
</div>
</div>
</div>
{% endif %}

0 comments on commit 007d383

Please sign in to comment.