Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added council suggestions for comparison tool #698

Merged
merged 3 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ jobs:
with:
options: "--check --diff"
src: "."
version: "22.8.0"
version: "24.8.0"
1 change: 1 addition & 0 deletions caps/dataframe/la.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
local authority data

"""

from functools import lru_cache
from typing import Callable, Optional, List
import string
Expand Down
1 change: 0 additions & 1 deletion caps/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
Forms for the CAPS app
"""


from django.forms import CharField, ChoiceField, ModelChoiceField, RadioSelect, Select
from haystack.forms import SearchForm
from haystack.inputs import Exact
Expand Down
1 change: 1 addition & 0 deletions caps/management/commands/import_emissions_data.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Import local authority_emissions
"""

import pandas as pd
from caps.models import Council, DataPoint, DataType
from django.core.management.base import BaseCommand
Expand Down
1 change: 1 addition & 0 deletions caps/management/commands/import_polling_data.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Import local authority_emissions
"""

import pandas as pd
from caps.models import Council, DataPoint, DataType
from django.core.management.base import BaseCommand
Expand Down
1 change: 1 addition & 0 deletions caps/search_funcs.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Functions for use by the semantic search features
"""

import re
from collections import namedtuple
from itertools import groupby
Expand Down
24 changes: 12 additions & 12 deletions caps/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ def get_context_data(self, **kwargs):
)
context = add_context_for_plans_download_and_search(context)
context["page_title"] = "Local Climate Assemblies - CAPE"
context[
"page_description"
] = "Search and view reports from local climate assemblies."
context["page_description"] = (
"Search and view reports from local climate assemblies."
)
return context


Expand Down Expand Up @@ -156,19 +156,19 @@ def get_emissions_context(self, council: Council) -> dict[str, Any]:
latest_year_total_emissions = council_emissions_data.get(
year=latest_year, data_type__name="Total Emissions"
).value
context[
"latest_year_per_capita_emissions"
] = latest_year_per_capita_emissions
context["latest_year_per_capita_emissions"] = (
latest_year_per_capita_emissions
)
context["latest_year_per_km2_emissions"] = latest_year_per_km2_emissions
context["latest_year_total_emissions"] = latest_year_total_emissions
context["emissions_data"] = True
except DataPoint.DoesNotExist as e:
pass

if context["emissions_data"]:
context[
"current_emissions_breakdown"
] = council.current_emissions_breakdown(year=latest_year)
context["current_emissions_breakdown"] = (
council.current_emissions_breakdown(year=latest_year)
)
multi_emission_chart = charts.multi_emissions_chart(council, latest_year)
context["chart_collection"] = ChartCollection()
context["chart_collection"].register(multi_emission_chart)
Expand Down Expand Up @@ -231,9 +231,9 @@ def get_scorecard_context(self, council: Council) -> dict[str, Any]:
"num_sections": len(top_scoring_sections),
}
if len(top_scoring_sections) > 0:
context["scoring_accolades"][
"example_section"
] = top_scoring_sections[0]["description"]
context["scoring_accolades"]["example_section"] = (
top_scoring_sections[0]["description"]
)

context["scoring_hidden"] = getattr(settings, "SCORECARDS_PRIVATE", False)

Expand Down
2,545 changes: 1,415 additions & 1,130 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ django-libsass = "^0.9"

[tool.poetry.group.dev.dependencies]
pylint = "^3.1.0"
black = "^24.3.0"
black = "^24.8.0"
shot-scraper = "^1.4"

[tool.black]
Expand Down
38 changes: 27 additions & 11 deletions scoring/static/scoring/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,19 +144,35 @@ forEachElement('.js-location-compare-autocomplete', function(input){
autoFirst: true
}
);

input.parentNode.addEventListener('awesomplete-selectcomplete', function(data){
var council = findItem(councils, {'name': data.text });
var sp = new URLSearchParams(window.location.search)
var comparisons = sp.getAll('comparisons');
handleCouncilSelection(data.text);
});
});

if (!comparisons.includes(council.slug)) {
comparisons.push(council.slug);
}
sp.delete('comparisons');
for (comparison of comparisons.values()) {
sp.append('comparisons', comparison);
}
window.location.href = window.location.pathname + '?' + sp.toString() + '#questions';
// Function to handle selection from both autocomplete and suggestions
function handleCouncilSelection(councilName) {
var council = findItem(councils, {'name': councilName});
var sp = new URLSearchParams(window.location.search);
var comparisons = sp.getAll('comparisons');

if (!comparisons.includes(council.slug)) {
comparisons.push(council.slug);
}
sp.delete('comparisons');
for (comparison of comparisons.values()) {
sp.append('comparisons', comparison);
}
window.location.href = window.location.pathname + '?' + sp.toString() + '#questions';
}

// Handling suggestion clicks
forEachElement('.js-council-compare-suggestions', function(suggestion) {
suggestion.addEventListener('click', function(event) {
event.preventDefault();
var councilSlug = this.dataset.slug;
var council = findItem(councils, {'slug': councilSlug}); // Get the council by slug
handleCouncilSelection(council.name); // Pass the name to the selection handler
});
});

Expand Down
22 changes: 18 additions & 4 deletions scoring/templates/scoring/council.html
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ <h3>Action Scorecard: {{ council.short_name }}</h3>
</div>

<div class="row my-4 my-lg-5">
<div class="col-md-6 col-lg-4 d-none d-md-block">
<div class="col-md-6 col-lg-5 d-none d-md-block">
<div class="card h-100">
<div class="card-header">
<h3 class="fs-6 mb-0">Choose {% include "scoring/includes/scoring-group-name.html" with group=council.get_scoring_group.slug plural=1 %} to compare</h3>
Expand All @@ -164,9 +164,23 @@ <h3 class="fs-6 mb-0">Choose {% include "scoring/includes/scoring-group-name.htm
{% if comparisons|length > 2 %}
<p class="warning-message">You have reached the maximum number of councils that can be compared.</p>
{% else %}
<input class="form-control searchbar js-location-compare-autocomplete" name="search" id="compare-search" type="search" placeholder="Council name" aria-label="Council name" data-council_type="{{ council.get_scoring_group.slug }}">
<input class="form-control searchbar js-location-compare-autocomplete mb-3" name="search" id="compare-search" type="search" placeholder="Council name" aria-label="Council name" data-council_type="{{ council.get_scoring_group.slug }}">
{% if similar_councils %}
<p class="mb-1 fs-7">
<button type="button" class="d-inline-flex align-items-center p-0 me-1 border-0 bg-transparent" data-bs-toggle="tooltip" data-bs-placement="top" title="Overall similarity based on emissions profile, deprivation, rural/urban population density, and geographical proximity">
<span>Similar councils:</span>
{% include 'caps/icons/question-circle.html' with classes='ms-1 align-text-top' width='1rem' height='1rem' role='presentation' %}
</button>
</p>
<div class="d-flex flex-wrap lh-sm mt-2" style="column-gap: 1rem; row-gap: 0.5rem;">
{% for similar_council in similar_councils %}
<a class="d-inline-block js-council-compare-suggestions" href="#" data-slug="{{ similar_council.slug }}">{{ similar_council.name }}</a>
{% endfor %}
</div>
{% endif %}
{% endif %}
{% if comparisons %}
<hr class="mx-n3">
<div class="selected-council-wrapper mt-4 mb-3">
{% for comparison in comparisons %}
<a href="#" class="radio-btn is--with-label is--with-closed-icon mr-1 js-comparison-council" data-slug="{{ comparison.council.slug }}">{{ comparison.council.name }}</a>
Expand All @@ -178,8 +192,8 @@ <h3 class="fs-6 mb-0">Choose {% include "scoring/includes/scoring-group-name.htm
</div>
</div>
</div>
<div class="col-md-6 col-lg-8">
<div class="card h-100">
<div class="col-md-6 col-lg-7">
<div class="card">
<div class="card-header">
<h3 class="fs-6 mb-0">Filter questions in the table</h3>
</div>
Expand Down
16 changes: 16 additions & 0 deletions scoring/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,22 @@ def get_context_data(self, **kwargs):
context["sections"] = sorted(
sections.values(), key=lambda section: section["code"]
)
context["comparisons"] = comparisons

for group in council.get_related_councils(5):
if group["type"].slug == "composite":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does this only apply if it's composite?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We only want the 5 "overall" similar councils, not the other types of similarity.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I just copied this block, wholesale, from the caps app)

if comparisons:
# Filter out any related councils that are already being compared
comparison_slugs = {score.council.slug for score in comparisons}
context["similar_councils"] = [
sc
for sc in group["councils"]
if sc.slug not in comparison_slugs
]
else:
context["similar_councils"] = group["councils"]
break

context["page_title"] = "{name} Climate Action Scorecard".format(
name=council.name
)
Expand Down
72 changes: 37 additions & 35 deletions scoring2022/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,9 @@ def get_missing_councils(self, council_ids, authority_type):

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context[
"all_councils"
] = Council.objects.all() # for location search autocomplete
context["all_councils"] = (
Council.objects.all()
) # for location search autocomplete

authority_type = self.get_authority_type()

Expand Down Expand Up @@ -141,9 +141,11 @@ def get_context_data(self, **kwargs):
sorted_by = sort
councils = sorted(
councils,
key=lambda council: 0
if council["score"] == 0 or council["score"] is None
else council["all_scores"][sort]["score"],
key=lambda council: (
0
if council["score"] == 0 or council["score"] is None
else council["all_scores"][sort]["score"]
),
reverse=True,
)
councils = sorted(councils, key=itemgetter("code"))
Expand Down Expand Up @@ -306,18 +308,18 @@ def get_context_data(self, **kwargs):
)

context["comparisons"] = comparisons
context[
"page_description"
] = "Want to know how effective {name}’s climate plans are? Check out {name}’s Council Climate Scorecard to understand how their climate plans compare to local authorities across the UK.".format(
name=council.name
context["page_description"] = (
"Want to know how effective {name}’s climate plans are? Check out {name}’s Council Climate Scorecard to understand how their climate plans compare to local authorities across the UK.".format(
name=council.name
)
)
context[
"twitter_tweet_text"
] = "Up to 30% of the UK’s transition to zero carbon is within the influence of local councils - that’s why I’m checking {name}’s Climate Action Plan on 📋 #CouncilClimateScorecards".format(
name=(
"@{}".format(council.twitter_name)
if council.twitter_name
else council.name
context["twitter_tweet_text"] = (
"Up to 30% of the UK’s transition to zero carbon is within the influence of local councils - that’s why I’m checking {name}’s Climate Action Plan on 📋 #CouncilClimateScorecards".format(
name=(
"@{}".format(council.twitter_name)
if council.twitter_name
else council.name
)
)
)
return context
Expand All @@ -341,9 +343,9 @@ def get_authority_type(self):

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context[
"all_councils"
] = Council.objects.all() # for location search autocomplete
context["all_councils"] = (
Council.objects.all()
) # for location search autocomplete

filter_args = {
"data": self.request.GET or None,
Expand Down Expand Up @@ -498,9 +500,9 @@ class LocationResultsView(PrivateScorecardsAccessMixin, BaseLocationResultsView)

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context[
"all_councils"
] = Council.objects.all() # for location search autocomplete
context["all_councils"] = (
Council.objects.all()
) # for location search autocomplete
context["page_title"] = "Choose a council"
return context

Expand All @@ -511,9 +513,9 @@ class MethodologyView(PrivateScorecardsAccessMixin, TemplateView):

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context[
"all_councils"
] = Council.objects.all() # for location search autocomplete
context["all_councils"] = (
Council.objects.all()
) # for location search autocomplete

# questions = PlanQuestion.objects.all()
# sections = PlanSection.objects.all()
Expand Down Expand Up @@ -564,9 +566,9 @@ class AboutView(PrivateScorecardsAccessMixin, TemplateView):

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context[
"all_councils"
] = Council.objects.all() # for location search autocomplete
context["all_councils"] = (
Council.objects.all()
) # for location search autocomplete
context["page_title"] = "About"
context["current_page"] = "about-page"
return context
Expand All @@ -578,9 +580,9 @@ class ContactView(PrivateScorecardsAccessMixin, TemplateView):

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context[
"all_councils"
] = Council.objects.all() # for location search autocomplete
context["all_councils"] = (
Council.objects.all()
) # for location search autocomplete
context["page_title"] = "Contact"
context["current_page"] = "contact-page"
return context
Expand All @@ -592,9 +594,9 @@ class HowToUseView(TemplateView):

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context[
"all_councils"
] = Council.objects.all() # for location search autocomplete
context["all_councils"] = (
Council.objects.all()
) # for location search autocomplete
context["page_title"] = "How to use the Scorecards"
context["current_page"] = "how-to-page"
return context
Loading