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

most improved graphics #727

Merged
merged 7 commits into from
Mar 3, 2025
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
10 changes: 9 additions & 1 deletion caps/admin.py
Original file line number Diff line number Diff line change
@@ -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):
Expand Down Expand Up @@ -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)
Original file line number Diff line number Diff line change
@@ -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),
),
]
93 changes: 90 additions & 3 deletions scoring/management/commands/generate_social_sharing_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
[
Expand All @@ -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/"
)
Expand All @@ -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)
)
Expand All @@ -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"],
Expand Down Expand Up @@ -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
):
Expand Down
101 changes: 95 additions & 6 deletions scoring/management/commands/import_actions_scores.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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]
Expand All @@ -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,
Expand All @@ -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,
)
Expand All @@ -224,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)

Expand Down Expand Up @@ -333,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}"
Expand All @@ -344,3 +432,4 @@ def handle(
if update_questions:
self.import_questions()
self.import_question_scores()
self.label_most_improved(previous_year)
Loading
Loading