From 57e19cd7eb3bb20e3dae712b34fdc6751ffe1b46 Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Wed, 12 Feb 2025 15:48:49 +0000 Subject: [PATCH 1/7] [CAPE] catch up on some small model changes --- ...historicalplandocument_options_and_more.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 caps/migrations/0049_alter_historicalplandocument_options_and_more.py diff --git a/caps/migrations/0049_alter_historicalplandocument_options_and_more.py b/caps/migrations/0049_alter_historicalplandocument_options_and_more.py new file mode 100644 index 000000000..e85403745 --- /dev/null +++ b/caps/migrations/0049_alter_historicalplandocument_options_and_more.py @@ -0,0 +1,48 @@ +# Generated by Django 4.2.17 on 2025-02-12 13:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("caps", "0048_auto_20240208_1225"), + ] + + operations = [ + migrations.AlterModelOptions( + name="historicalplandocument", + options={ + "get_latest_by": ("history_date", "history_id"), + "ordering": ("-history_date", "-history_id"), + "verbose_name": "historical plan document", + "verbose_name_plural": "historical plan documents", + }, + ), + migrations.AlterModelOptions( + name="historicalpromise", + options={ + "get_latest_by": ("history_date", "history_id"), + "ordering": ("-history_date", "-history_id"), + "verbose_name": "historical promise", + "verbose_name_plural": "historical promises", + }, + ), + migrations.AlterField( + model_name="council", + name="related_authorities", + field=models.ManyToManyField( + blank=True, through="caps.Distance", to="caps.council" + ), + ), + migrations.AlterField( + model_name="historicalplandocument", + name="history_date", + field=models.DateTimeField(db_index=True), + ), + migrations.AlterField( + model_name="historicalpromise", + name="history_date", + field=models.DateTimeField(db_index=True), + ), + ] From fb545bea3920c2d70825314b046d56aadadbe82b Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Wed, 12 Feb 2025 15:49:48 +0000 Subject: [PATCH 2/7] [Scorecards] add most_improved field to plan and section scores for labeling the most improved councils on import, and then for generating graphics etc Also add plan scores to admin interface --- caps/admin.py | 10 +- ...oved_plansection_most_improved_and_more.py | 109 ++++++++++++++++++ scoring/models.py | 16 ++- 3 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 scoring/migrations/0019_planscore_most_improved_plansection_most_improved_and_more.py diff --git a/caps/admin.py b/caps/admin.py index ba9dc8208..bd106058a 100755 --- a/caps/admin.py +++ b/caps/admin.py @@ -1,7 +1,7 @@ from django.contrib import admin from caps.models import Council, DataPoint, DataType, PlanDocument -from scoring.models import PlanQuestion, PlanSection +from scoring.models import PlanQuestion, PlanScore, PlanSection class CouncilAdmin(admin.ModelAdmin): @@ -62,3 +62,11 @@ class PlanQuestionAdmin(admin.ModelAdmin): admin.site.register(PlanQuestion, PlanQuestionAdmin) + + +class PlanScoreAdmin(admin.ModelAdmin): + list_display = ("council", "year") + search_fields = ("council__name",) + + +admin.site.register(PlanScore, PlanScoreAdmin) diff --git a/scoring/migrations/0019_planscore_most_improved_plansection_most_improved_and_more.py b/scoring/migrations/0019_planscore_most_improved_plansection_most_improved_and_more.py new file mode 100644 index 000000000..b52494208 --- /dev/null +++ b/scoring/migrations/0019_planscore_most_improved_plansection_most_improved_and_more.py @@ -0,0 +1,109 @@ +# Generated by Django 4.2.17 on 2025-03-03 13:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("scoring", "0018_planscore_previous_year"), + ] + + operations = [ + migrations.AddField( + model_name="planscore", + name="most_improved", + field=models.CharField( + blank=True, + choices=[ + ("single", "Single Tier"), + ("county", "County Council"), + ("district", "District Council"), + ("combined", "Combined Authority"), + ("northern-ireland", "Northern Ireland Council"), + ], + max_length=20, + null=True, + ), + ), + migrations.AddField( + model_name="plansection", + name="most_improved", + field=models.CharField( + blank=True, + choices=[ + ("single", "Single Tier"), + ("county", "County Council"), + ("district", "District Council"), + ("combined", "Combined Authority"), + ("northern-ireland", "Northern Ireland Council"), + ], + max_length=20, + null=True, + ), + ), + migrations.AddField( + model_name="plansectionscore", + name="most_improved", + field=models.CharField( + blank=True, + choices=[ + ("single", "Single Tier"), + ("county", "County Council"), + ("district", "District Council"), + ("combined", "Combined Authority"), + ("northern-ireland", "Northern Ireland Council"), + ], + max_length=20, + null=True, + ), + ), + migrations.AlterField( + model_name="planscore", + name="top_performer", + field=models.CharField( + blank=True, + choices=[ + ("single", "Single Tier"), + ("county", "County Council"), + ("district", "District Council"), + ("combined", "Combined Authority"), + ("northern-ireland", "Northern Ireland Council"), + ], + max_length=20, + null=True, + ), + ), + migrations.AlterField( + model_name="plansection", + name="top_performer", + field=models.CharField( + blank=True, + choices=[ + ("single", "Single Tier"), + ("county", "County Council"), + ("district", "District Council"), + ("combined", "Combined Authority"), + ("northern-ireland", "Northern Ireland Council"), + ], + max_length=20, + null=True, + ), + ), + migrations.AlterField( + model_name="plansectionscore", + name="top_performer", + field=models.CharField( + blank=True, + choices=[ + ("single", "Single Tier"), + ("county", "County Council"), + ("district", "District Council"), + ("combined", "Combined Authority"), + ("northern-ireland", "Northern Ireland Council"), + ], + max_length=20, + null=True, + ), + ), + ] diff --git a/scoring/models.py b/scoring/models.py index 2efb4fbd9..17a695e28 100644 --- a/scoring/models.py +++ b/scoring/models.py @@ -95,7 +95,11 @@ class PlanScore(models.Model): total = models.FloatField(default=0) top_performer = models.CharField( - max_length=20, choices=Council.SCORING_GROUP_CHOICES, null=True + max_length=20, choices=Council.SCORING_GROUP_CHOICES, null=True, blank=True + ) + + most_improved = models.CharField( + max_length=20, choices=Council.SCORING_GROUP_CHOICES, null=True, blank=True ) # filter data @@ -181,7 +185,10 @@ class PlanSection(models.Model): description = models.CharField(max_length=1000) year = models.PositiveSmallIntegerField(null=True, blank=True) top_performer = models.CharField( - max_length=20, choices=Council.SCORING_GROUP_CHOICES, null=True + max_length=20, choices=Council.SCORING_GROUP_CHOICES, null=True, blank=True + ) + most_improved = models.CharField( + max_length=20, choices=Council.SCORING_GROUP_CHOICES, null=True, blank=True ) long_description = models.TextField(null=True, blank=True) @@ -313,7 +320,10 @@ class PlanSectionScore(ScoreFilterMixin, models.Model): # this is a percentage weighted_score = models.FloatField(default=0) top_performer = models.CharField( - max_length=20, choices=Council.SCORING_GROUP_CHOICES, null=True + max_length=20, choices=Council.SCORING_GROUP_CHOICES, null=True, blank=True + ) + most_improved = models.CharField( + max_length=20, choices=Council.SCORING_GROUP_CHOICES, null=True, blank=True ) def questions_answered(self, prev_year=None): From 0633b961144b2350d8932ead3619f2eee746310d Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Wed, 12 Feb 2025 15:51:09 +0000 Subject: [PATCH 3/7] [Scorecards] remove hard coded year from action scores import --- scoring/management/commands/import_actions_scores.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scoring/management/commands/import_actions_scores.py b/scoring/management/commands/import_actions_scores.py index 197b6d0ca..1f71637c0 100644 --- a/scoring/management/commands/import_actions_scores.py +++ b/scoring/management/commands/import_actions_scores.py @@ -185,8 +185,10 @@ def label_top_performers(self): plan_sections = PlanSection.objects.filter(year=self.YEAR) # reset top performers - PlanScore.objects.filter(year=2023).update(top_performer="") - PlanSectionScore.objects.filter(plan_score__year=2023).update(top_performer="") + PlanScore.objects.filter(year=self.YEAR).update(top_performer="") + PlanSectionScore.objects.filter(plan_score__year=self.YEAR).update( + top_performer="" + ) for group in Council.SCORING_GROUP_CHOICES: group_tag = group[0] @@ -200,7 +202,7 @@ def label_top_performers(self): group_params = Council.SCORING_GROUPS[group_tag] top_plan_scores = PlanScore.objects.filter( - year=2023, + year=self.YEAR, council__authority_type__in=group_params["types"], council__country__in=group_params["countries"], weighted_total__gt=0, @@ -215,7 +217,7 @@ def label_top_performers(self): continue top_section_scores = PlanSectionScore.objects.filter( - plan_score__year=2023, + plan_score__year=self.YEAR, plan_section=section, weighted_score__gte=80, ) From aaf2dec3216fa7710d095d7236db394f484b8441 Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Wed, 12 Feb 2025 15:51:36 +0000 Subject: [PATCH 4/7] [Scorecards] calculate most improved councils on action score import Does this for overall per council type and overall per section --- .../commands/import_actions_scores.py | 91 ++++++++++++++++++- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/scoring/management/commands/import_actions_scores.py b/scoring/management/commands/import_actions_scores.py index 1f71637c0..e61a2001e 100644 --- a/scoring/management/commands/import_actions_scores.py +++ b/scoring/management/commands/import_actions_scores.py @@ -15,7 +15,7 @@ from django.conf import settings from django.core.files import File from django.core.management.base import BaseCommand, CommandError -from django.db.models import F, Sum +from django.db.models import F, OuterRef, Subquery, Sum from django.template.defaultfilters import pluralize from caps.models import Council @@ -88,7 +88,7 @@ def add_arguments(self, parser): parser.add_argument( "--previous_year", action="store", - help="year of previous actions scorecards, for linking", + help="Previous scorecards year, for calculating most improved, and linking", ) def create_sections(self): @@ -226,6 +226,91 @@ def label_top_performers(self): section_score.top_performer = section.code section_score.save() + def label_most_improved(self, previous_year): + if not previous_year: + self.stderr.write("Can't calculate most improved, need previous year") + return + + plan_sections = PlanSection.objects.filter(year=self.YEAR) + + # reset top performers + PlanScore.objects.filter(year=self.YEAR).update(most_improved="") + PlanSectionScore.objects.filter(plan_score__year=self.YEAR).update( + most_improved="" + ) + + for group in Council.SCORING_GROUP_CHOICES: + group_tag = group[0] + group_params = Council.SCORING_GROUPS[group_tag] + + score_differences = ( + PlanScore.objects.filter( + year=self.YEAR, + council__authority_type__in=group_params["types"], + council__country__in=group_params["countries"], + weighted_total__gt=0, + ) + .annotate( + previous_score=Subquery( + PlanScore.objects.filter( + year=previous_year, council_id=OuterRef("council_id") + ).values("weighted_total") + ) + ) + .annotate(difference=(F("weighted_total") - F("previous_score"))) + .order_by("-difference") + ) + + most_improved = score_differences.first() + most_improved.most_improved = group_tag + most_improved.save() + + for section in plan_sections.all(): + if section.code in self.SKIP_SECTION_PERFORMERS: + continue + + differences = ( + PlanSectionScore.objects.filter( + plan_score__year=self.YEAR, + plan_section=section, + ) + .annotate( + previous_score=Subquery( + PlanSectionScore.objects.filter( + plan_score__year=previous_year, + plan_section__code=section.code, + plan_score__council_id=OuterRef("plan_score__council_id"), + ).values("weighted_score") + ) + ) + .annotate(difference=(F("weighted_score") - F("previous_score"))) + .order_by("-difference") + ) + + most_improved = differences.first() + most_improved.most_improved = section.code + most_improved.save() + + score_differences = ( + PlanScore.objects.filter( + year=self.YEAR, + weighted_total__gt=0, + ) + .annotate( + previous_score=Subquery( + PlanScore.objects.filter( + year=previous_year, council_id=OuterRef("council_id") + ).values("weighted_total") + ) + ) + .annotate(difference=(F("weighted_total") - F("previous_score"))) + .order_by("-difference") + ) + + most_improved = score_differences.first() + most_improved.most_improved = "overall" + most_improved.save() + def import_questions(self): df = pd.read_csv(self.QUESTIONS_CSV) @@ -335,6 +420,7 @@ def handle( **options, ): self.previous_year = previous_year + self.stdout.write(f"Importing council action scores for {self.YEAR}") if not update_questions: self.stdout.write( f"{YELLOW}Not creating or updating questions, call with --update_questions to do so{NOBOLD}" @@ -346,3 +432,4 @@ def handle( if update_questions: self.import_questions() self.import_question_scores() + self.label_most_improved(previous_year) From cde1eed6e0a6ce2184807065bacacd2381e20009 Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Wed, 12 Feb 2025 17:13:33 +0000 Subject: [PATCH 5/7] [Scorecards] views for generating most improved graphics --- .../templates/scoring/base-most-improved.html | 41 +++++ scoring/templates/scoring/base-preview.html | 2 +- .../scoring/council-most-improved.html | 1 + .../scoring/overall-most-improved.html | 8 + .../scoring/section-most-improved.html | 8 + scoring/urls.py | 15 ++ scoring/views.py | 148 ++++++++++++++++++ 7 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 scoring/templates/scoring/base-most-improved.html create mode 100644 scoring/templates/scoring/council-most-improved.html create mode 100644 scoring/templates/scoring/overall-most-improved.html create mode 100644 scoring/templates/scoring/section-most-improved.html diff --git a/scoring/templates/scoring/base-most-improved.html b/scoring/templates/scoring/base-most-improved.html new file mode 100644 index 000000000..36d12af2f --- /dev/null +++ b/scoring/templates/scoring/base-most-improved.html @@ -0,0 +1,41 @@ +{% extends "scoring/base-preview.html" %} +{% load static caps_templatetags %} + +{% block bodyclass %}version-2-gradient council-preview{% endblock %} + +{% block content %} +

+ + {{ council.name }} + +

+ {% block improved_label %} + + {% include 'caps/icons/scorecards-star.html' with classes='me-1 align-text-top' width='1.2em' height='auto' role='presentation' %} + Most Improved {% include 'scoring/includes/scoring-group-name.html' with group=scoring_group.slug plural=False %} + + {% endblock %} + + + + + + + + + + + + + + + + +
{{ plan_year }} score{{ previous_year }} scoreChange
+ {{ current_score|floatformat:0 }}% + + {{ last_score|floatformat:0 }}% + + {{ change|floatformat:0 }}% +
+{% endblock %} diff --git a/scoring/templates/scoring/base-preview.html b/scoring/templates/scoring/base-preview.html index 82d86e38d..70dade8b8 100644 --- a/scoring/templates/scoring/base-preview.html +++ b/scoring/templates/scoring/base-preview.html @@ -20,7 +20,7 @@
{% include 'caps/icons/council-climate-logo-2023.html' with classes='open-graph-preview__scorecards-logo' width='10em' height='auto' role='presentation' %} - 2023 + {{ plan_year }}
{% block content %}{% endblock %} diff --git a/scoring/templates/scoring/council-most-improved.html b/scoring/templates/scoring/council-most-improved.html new file mode 100644 index 000000000..57f221e0c --- /dev/null +++ b/scoring/templates/scoring/council-most-improved.html @@ -0,0 +1 @@ +{% extends "scoring/base-most-improved.html" %} diff --git a/scoring/templates/scoring/overall-most-improved.html b/scoring/templates/scoring/overall-most-improved.html new file mode 100644 index 000000000..5a7534026 --- /dev/null +++ b/scoring/templates/scoring/overall-most-improved.html @@ -0,0 +1,8 @@ +{% extends "scoring/base-most-improved.html" %} + +{% block improved_label %} + + {% include 'caps/icons/scorecards-star.html' with classes='me-1 align-text-top' width='1.2em' height='auto' role='presentation' %} + Most Improved Overall + +{% endblock %} diff --git a/scoring/templates/scoring/section-most-improved.html b/scoring/templates/scoring/section-most-improved.html new file mode 100644 index 000000000..ac00a0737 --- /dev/null +++ b/scoring/templates/scoring/section-most-improved.html @@ -0,0 +1,8 @@ +{% extends "scoring/base-most-improved.html" %} + +{% block improved_label %} + + {% include 'caps/icons/scorecards-star.html' with classes='me-1 align-text-top' width='1.2em' height='auto' role='presentation' %} + Most Improved: {{ section.description }} + +{% endblock %} diff --git a/scoring/urls.py b/scoring/urls.py index 658e90519..054e8b287 100644 --- a/scoring/urls.py +++ b/scoring/urls.py @@ -20,6 +20,16 @@ views.CouncilPreview.as_view(), name="council_preview", ), + path( + "councils/most_improved//", + views.OverallMostImproved.as_view(), + name="overall_most_improved", + ), + path( + "councils//most_improved//", + views.CouncilMostImproved.as_view(), + name="council_most_improved", + ), path( "councils//preview/top-performer", views.CouncilPreviewTopPerfomer.as_view(), @@ -33,6 +43,11 @@ views.SectionTopPerformerPreview.as_view(), name="section_top_preview", ), + path( + "section//most_improved//", + views.SectionMostImproved.as_view(), + name="section_most_improved", + ), path( "section//top-performer//preview/", views.SectionCouncilTopPerformerPreview.as_view(), diff --git a/scoring/views.py b/scoring/views.py index f452fc21a..ffd8bb899 100644 --- a/scoring/views.py +++ b/scoring/views.py @@ -627,6 +627,154 @@ def get_context_data(self, **kwargs): return context +class MostImprovedBase(DetailView): + def calculate_change(self, current_score, previous_score): + return ((current_score - previous_score) / previous_score) * 100 + + def get_changes(self, plan_score, previous_year): + previous_plan_score = PlanScore.objects.get( + council=plan_score.council, year=previous_year + ) + previous_score = previous_plan_score.weighted_total + + change = self.calculate_change(plan_score.weighted_total, previous_score) + + return previous_score, change + + def add_nosplit_span(self, council): + add_nosplit_span = False + words = council.name.split() + last_two_words = f"{words[-2]} {words[-1]}" + if len(last_two_words) < 16: + add_nosplit_span = True + + return add_nosplit_span + + def add_common_context(self, context, previous_year, council): + context["add_nosplit_span"] = self.add_nosplit_span(council) + context["plan_year"] = self.request.year + context["previous_year"] = previous_year + context["council"] = council + context["page_title"] = council.name + + return context + + +@method_decorator(cache_control(**cache_settings), name="dispatch") +class OverallMostImproved(MostImprovedBase): + model = PlanScore + context_object_name = "plan_score" + template_name = "scoring/overall-most-improved.html" + + def get_object(self, queryset=None): + plan_score = get_object_or_404( + PlanScore, + year=self.request.year, + most_improved="overall", + ) + + return plan_score + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + plan_score = context.get("plan_score") + previous_year = self.kwargs["previous_year"] + + council = plan_score.council + last_score, change = self.get_changes(plan_score, previous_year) + + context = self.add_common_context(context, previous_year, council) + context["current_score"] = plan_score.weighted_total + context["last_score"] = last_score + context["change"] = change + return context + + +@method_decorator(cache_control(**cache_settings), name="dispatch") +class CouncilMostImproved(MostImprovedBase): + model = PlanScore + context_object_name = "plan_score" + template_name = "scoring/council-most-improved.html" + + def get_object(self, queryset=None): + group = Council.SCORING_GROUPS[self.kwargs["group"]] + plan_score = get_object_or_404( + PlanScore, + year=self.request.year, + council__authority_type__in=group["types"], + council__country__in=group["countries"], + most_improved__in=[group["slug"], "overall"], + ) + + return plan_score + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + plan_score = context.get("plan_score") + previous_year = self.kwargs["previous_year"] + + council = plan_score.council + last_score, change = self.get_changes(plan_score, previous_year) + + context = self.add_common_context(context, previous_year, council) + + context["current_score"] = plan_score.weighted_total + context["scoring_group"] = council.get_scoring_group() + context["last_score"] = last_score + context["change"] = change + return context + + +@method_decorator(cache_control(**cache_settings), name="dispatch") +class SectionMostImproved(MostImprovedBase): + model = PlanSectionScore + context_object_name = "section_score" + template_name = "scoring/section-most-improved.html" + + def get_object(self, queryset=None): + section = self.kwargs["section"] + plan_score = get_object_or_404( + PlanSectionScore, + plan_score__year=self.request.year, + most_improved=section, + ) + + return plan_score + + def get_changes(self, section_score, previous_year): + previous_section_score = PlanSectionScore.objects.get( + plan_score__council=section_score.plan_score.council, + plan_section__code=section_score.plan_section.code, + plan_score__year=previous_year, + ) + previous_score = previous_section_score.weighted_score + + change = self.calculate_change(section_score.weighted_score, previous_score) + + return previous_score, change + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + section_score = context.get("section_score") + section_code = self.kwargs["section"] + previous_year = self.kwargs["previous_year"] + + section = get_object_or_404( + PlanSection, year=self.request.year, code=section_code + ) + council = section_score.plan_score.council + last_score, change = self.get_changes(section_score, previous_year) + + context = self.add_common_context(context, previous_year, council) + + context["section"] = section + context["current_score"] = section_score.weighted_score + context["scoring_group"] = council.get_scoring_group() + context["last_score"] = last_score + context["change"] = change + return context + + @method_decorator(cache_control(**cache_settings), name="dispatch") class CouncilTypeTopPerformerView(TemplateView): template_name = "scoring/council-top-performer-overall-preview.html" From 1da6abb5e13a2418f7b6de53eb9113e4548f4304 Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Thu, 13 Feb 2025 16:42:22 +0000 Subject: [PATCH 6/7] [Scorecards] update social image generation to do most improved Also put things in a per year folder --- .../generate_social_sharing_images.py | 93 ++++++++++++++++++- 1 file changed, 90 insertions(+), 3 deletions(-) diff --git a/scoring/management/commands/generate_social_sharing_images.py b/scoring/management/commands/generate_social_sharing_images.py index 13777ab9f..ff68f5284 100644 --- a/scoring/management/commands/generate_social_sharing_images.py +++ b/scoring/management/commands/generate_social_sharing_images.py @@ -27,6 +27,12 @@ def add_arguments(self, parser): help="Only generate graphics for top performers", ) + parser.add_argument( + "--previous_year", + action="store", + help="previous year for producing most improved graphics", + ) + def get_shot(self, url, filepath, width=1200, height=630): subprocess.run( [ @@ -44,8 +50,9 @@ def get_shot(self, url, filepath, width=1200, height=630): def handle(self, *args, **options): top_performers = options["top_performers_only"] + previous_year = options["previous_year"] - og_images_dir = Path(settings.BASE_DIR, "social-images") + og_images_dir = Path(settings.BASE_DIR, "social-images", settings.PLAN_YEAR) static_images_dir = Path( settings.BASE_DIR, "scoring/static/scoring/img/og-images/" ) @@ -54,11 +61,11 @@ def handle(self, *args, **options): static_images_dir.mkdir(parents=True, exist_ok=True) councils = Council.current_councils() - sections = PlanSection.objects.filter(year=2023) + sections = PlanSection.objects.filter(year=settings.PLAN_YEAR) if top_performers: overall = list( - PlanScore.objects.filter(year=2023) + PlanScore.objects.filter(year=settings.PLAN_YEAR) .exclude(top_performer="") .values_list("council_id", flat=True) ) @@ -85,6 +92,29 @@ def handle(self, *args, **options): f"Generating {size_by} images for {councils.count()} councils into {og_images_dir}/{size_by}/councils" ) + if previous_year: + url = "{}{}".format( + options["baseurl"], + reverse_lazy( + "scoring:overall_most_improved", + urlconf="scoring.urls", + kwargs={ + "previous_year": previous_year, + }, + ), + ) + dirpath = Path( + og_images_dir, + size_by, + "most_improved", + ) + dirpath.mkdir(parents=True, exist_ok=True) + filepath = Path( + dirpath, + f"most_improved_overall.png", + ) + self.get_shot(url, filepath, width=size[0], height=size[1]) + for council in tqdm(councils, leave=False): url = "{}{}".format( options["baseurl"], @@ -112,11 +142,68 @@ def handle(self, *args, **options): og_path = Path(static_images_dir, f"{council.slug}.png") copyfile(filepath, og_path) + if options["previous_year"]: + tqdm.write( + f"Generating {size_by} images for most improved into {og_images_dir}/{size_by}/most_improved" + ) + dirpath = Path( + og_images_dir, + size_by, + "most_improved", + ) + dirpath.mkdir(parents=True, exist_ok=True) + + for council_type in tqdm( + Council.SCORING_GROUPS.keys(), position=1, leave=False + ): + url = "{}{}".format( + options["baseurl"], + reverse_lazy( + "scoring:council_most_improved", + urlconf="scoring.urls", + kwargs={ + "group": council_type, + "previous_year": options["previous_year"], + }, + ), + ) + filepath = Path( + og_images_dir, + size_by, + "most_improved", + f"most_improved_{council_type}.png", + ) + self.get_shot(url, filepath, width=size[0], height=size[1]) + tqdm.write( f"Generating {size_by} images for {sections.count()} sections into {og_images_dir}/{size_by}/sections" ) for section in tqdm(sections, position=0, leave=False): + if previous_year: + url = "{}{}".format( + options["baseurl"], + reverse_lazy( + "scoring:section_most_improved", + urlconf="scoring.urls", + kwargs={ + "section": section.code, + "previous_year": previous_year, + }, + ), + ) + dirpath = Path( + og_images_dir, + size_by, + "most_improved", + ) + dirpath.mkdir(parents=True, exist_ok=True) + filepath = Path( + dirpath, + f"most_improved_{section.code}.png", + ) + self.get_shot(url, filepath, width=size[0], height=size[1]) + for council_type in tqdm( Council.SCORING_GROUPS.keys(), position=1, leave=False ): From 260ff9f16bb9faf7303dc940995860fcc0c3e055 Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Thu, 13 Feb 2025 17:21:03 +0000 Subject: [PATCH 7/7] [Scorecards] removed unused top performer view --- scoring/urls.py | 5 ----- scoring/views.py | 14 -------------- 2 files changed, 19 deletions(-) diff --git a/scoring/urls.py b/scoring/urls.py index 054e8b287..e5c85c41d 100644 --- a/scoring/urls.py +++ b/scoring/urls.py @@ -30,11 +30,6 @@ views.CouncilMostImproved.as_view(), name="council_most_improved", ), - path( - "councils//preview/top-performer", - views.CouncilPreviewTopPerfomer.as_view(), - name="council_top_performer_preview", - ), path("councils//", views.CouncilView.as_view(), name="council"), path("sections//", views.SectionView.as_view(), name="section"), path("sections/", views.SectionsView.as_view(), name="sections"), diff --git a/scoring/views.py b/scoring/views.py index ffd8bb899..89696a979 100644 --- a/scoring/views.py +++ b/scoring/views.py @@ -801,20 +801,6 @@ def get_context_data(self, **kwargs): return context -@method_decorator(cache_control(**cache_settings), name="dispatch") -class CouncilPreviewTopPerfomer(DetailView): - model = Council - context_object_name = "council" - template_name = "scoring/council-top-performer-preview.html" - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - council = context.get("council") - context["page_title"] = council.name - context["plan_year"] = self.request.year - return context - - @method_decorator(cache_control(**cache_settings), name="dispatch") class SectionView(PrivateScorecardsAccessMixin, SearchAutocompleteMixin, DetailView): model = PlanSection