Skip to content

Commit

Permalink
Update main vote analyzer to also handle special vote titles (#1060)
Browse files Browse the repository at this point in the history
  • Loading branch information
tillprochaska authored Nov 27, 2024
2 parents 4d8a334 + b80e8e6 commit 56dcf66
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 23 deletions.
53 changes: 39 additions & 14 deletions backend/howtheyvote/analysis/votes.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ def _make_group_key(self, date: datetime.date, vote: Vote) -> str | None:


class MainVoteAnalyzer:
"""This analyzer checks the vote description for common keywords that indicate
that the vote is a main vote in its vote group. Only main votes are displayed
in index pages and are searchable."""
"""This analyzer checks the vote description and title for common keywords
that indicate that the vote is a main vote in its vote group. Only main
votes are displayed in index pages and are searchable."""

MAIN_DESCRIPTIONS = set(
[
Expand Down Expand Up @@ -94,27 +94,52 @@ class MainVoteAnalyzer:
]
)

def __init__(self, vote_id: int, description: str | None):
MAIN_TITLES = set(
[
"election de la commission",
"election of the commission",
]
)

def __init__(self, vote_id: int, description: str | None, title: str | None):
self.vote_id = vote_id
self.description = description
self.title = title

def run(self) -> Fragment | None:
if self._description_is_main() or self._title_is_main():
return Fragment(
model="Vote",
source_id=self.vote_id,
source_name=type(self).__name__,
group_key=self.vote_id,
data={"is_main": True},
)

return None

def _description_is_main(self) -> bool:
if not self.description:
return None
return False

description = unidecode(self.description).lower()
parts = description.split(" - ")

if all([part not in self.MAIN_DESCRIPTIONS for part in parts]):
return None
if any([part in self.MAIN_DESCRIPTIONS for part in parts]):
return True

return Fragment(
model="Vote",
source_id=self.vote_id,
source_name=type(self).__name__,
group_key=self.vote_id,
data={"is_main": True},
)
return False

def _title_is_main(self) -> bool:
if not self.title:
return False

title = unidecode(self.title).lower()

if title in self.MAIN_TITLES:
return True

return False


class FeaturedVotesAnalyzer:
Expand Down
4 changes: 3 additions & 1 deletion backend/howtheyvote/pipelines/rcv_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,9 @@ def _analyze_main_votes(self) -> None:
writer = BulkWriter()

for vote in self._votes():
analyzer = MainVoteAnalyzer(vote.id, vote.description)
analyzer = MainVoteAnalyzer(
vote_id=vote.id, description=vote.description, title=vote.title
)
writer.add(analyzer.run())

writer.flush()
Expand Down
Empty file added backend/tests/__init__.py
Empty file.
64 changes: 64 additions & 0 deletions backend/tests/analysis/test_votes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from howtheyvote.analysis.votes import MainVoteAnalyzer
from howtheyvote.models.common import Fragment

from ..helpers import record_to_dict


def test_main_vote_analyzer_description():
analyzer = MainVoteAnalyzer(
vote_id=1,
description="Am 123",
title="Lorem Ipsum",
)
assert analyzer.run() is None

analyzer = MainVoteAnalyzer(
vote_id=2,
description="Proposition de résolution",
title="Lorem ipsum",
)
expected = Fragment(
model="Vote",
source_id=2,
source_name="MainVoteAnalyzer",
group_key=2,
data={"is_main": True},
)
assert record_to_dict(analyzer.run()) == record_to_dict(expected)

analyzer = MainVoteAnalyzer(
vote_id=3,
description="Accord provisoire - Am 123",
title="Lorem ipsum",
)
expected = Fragment(
model="Vote",
source_id=3,
source_name="MainVoteAnalyzer",
group_key=3,
data={"is_main": True},
)
assert record_to_dict(analyzer.run()) == record_to_dict(expected)


def test_main_vote_analyzer_title():
analyzer = MainVoteAnalyzer(
vote_id=1,
description=None,
title="Ordre du jour de mardi",
)
assert analyzer.run() is None

expected = Fragment(
model="Vote",
source_id=2,
source_name="MainVoteAnalyzer",
group_key=2,
data={"is_main": True},
)
analyzer = MainVoteAnalyzer(
vote_id=2,
description=None,
title="Élection de la Commission",
)
assert record_to_dict(analyzer.run()) == record_to_dict(expected)
7 changes: 7 additions & 0 deletions backend/tests/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from typing import Any

from sqlalchemy.orm import DeclarativeBase


def record_to_dict(record: DeclarativeBase) -> dict[str, Any]:
return {c.name: getattr(record, c.name) for c in record.__table__.columns}
7 changes: 0 additions & 7 deletions backend/tests/scrapers/helpers.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
from pathlib import Path
from typing import Any

from sqlalchemy.orm import DeclarativeBase

FIXTURES_BASE = Path(__file__).resolve().parent / "data"


def load_fixture(path: str) -> str:
return FIXTURES_BASE.joinpath(path).read_text()


def record_to_dict(record: DeclarativeBase) -> dict[str, Any]:
return {c.name: getattr(record, c.name) for c in record.__table__.columns}
3 changes: 2 additions & 1 deletion backend/tests/scrapers/test_votes.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
RCVListScraper,
)

from .helpers import load_fixture, record_to_dict
from ..helpers import record_to_dict
from .helpers import load_fixture


def test_rcv_list_scraper(responses):
Expand Down

0 comments on commit 56dcf66

Please sign in to comment.