Skip to content

Commit

Permalink
Fix reasoned amendments
Browse files Browse the repository at this point in the history
  • Loading branch information
ajparsons committed Nov 26, 2024
1 parent da63bef commit ecad6a7
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 5 deletions.
14 changes: 10 additions & 4 deletions src/parl_motion_detector/agreements.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,18 @@ def construct_motion(self):
else:
motion_lines = [self.preceeding_text, self.agreed_text]

return Motion(
motion = Motion(
date=self.date,
chamber=self.chamber,
major_heading_id=self.major_heading_id,
minor_heading_id=self.minor_heading_id,
speech_id=self.speech_id,
speech_start_pid=self.paragraph_pid,
motion_lines=motion_lines,
).add_title()
)
motion.add_title()
motion.self_flag()
return motion

@computed_field
@property
Expand Down Expand Up @@ -165,15 +168,18 @@ def construct_motion(self):
Sometimes (like for clauses) there isn't actually a perfect motion to hold onto
We're just going to cheat and make one.
"""
return Motion(
motion = Motion(
date=self.date,
chamber=self.chamber,
major_heading_id=self.major_heading_id,
minor_heading_id=self.minor_heading_id,
speech_id=self.speech_id,
speech_start_pid="",
motion_lines=[self.minor_heading_text, self.preceding_speech],
).add_title()
)
motion.add_title()
motion.self_flag()
return motion

@computed_field
@property
Expand Down
44 changes: 44 additions & 0 deletions src/parl_motion_detector/mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ def get_manual_text(data_dir: Path) -> dict[str, Motion]:
]
)


preamble = ["Motion made, and Question put,", "Resolved,"]

similar_phrases = {"additional amendment": "amendment"}
Expand Down Expand Up @@ -399,6 +400,27 @@ def multiple_decision_assignment(
possible_motions = condense_motions(possible_motions)
previous_loop = len(decisions) + 1

second_stage_motions = [
x for x in possible_motions if x.has_flag(Flag.SECOND_STAGE)
]
if len(second_stage_motions) > 1:
# if we have multiple second stage motions - we want to just keep the first
# because it might confuse reasoned amendment assignment
possible_motions = [
x for x in possible_motions if x not in second_stage_motions[1:]
]

if any(x.has_flag(Flag.REASONED_AMENDMENT_FULL) for x in possible_motions):
# remove any partial reasoned amendments if so
# this is because ANNOYINGLY sometimes they don't print the reasoned amendment
# so we need to fall back to another way of catagorising it to get the right map
# given this is ALWAYS in the same vote as a staged vote
possible_motions = [
x
for x in possible_motions
if not x.has_flag(Flag.REASONED_AMENDMENT_PARTIAL)
]

while len(decisions) < previous_loop:
previous_loop = len(decisions)
# print(len(decisions), previous_loop)
Expand Down Expand Up @@ -428,6 +450,28 @@ def multiple_decision_assignment(
raise ValueError("After decision motion not removed")
continue

# hints from flags
# if we try and construct a motion from the text surrounding the decision
# does it share a flag with one of the motions
for decision in decisions:
constructed_motion = decision.construct_motion()
possible_flagged_motions = []
for motion in possible_motions:
overlap_flags = set(motion.flags) & set(constructed_motion.flags)
if overlap_flags:
possible_flagged_motions.append(motion)
if len(possible_flagged_motions) == 1:
self.assign_motion_decision(
possible_flagged_motions[0],
decision,
"constructed motion flag match",
)
decisions = [x for x in decisions if x != decision]
possible_motions = [
x for x in possible_motions if x != possible_flagged_motions[0]
]
continue

if self.chamber == Chamber.SCOTLAND:
# Here we have some special casing for amendments in the scottish Parliament
# these should happen pretty good in order! But there may be a bit more spacing than usual
Expand Down
35 changes: 34 additions & 1 deletion src/parl_motion_detector/motions.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ class Flag(StrEnum):
MAIN_QUESTION = "main_question"
MOTION_AMENDMENT = "motion_amendment"
SCOTTISH_EXPANDED_MOTION = "scottish_expanded_motion"
REASONED_AMENDMENT_FULL = "reasoned_amendment_full"
REASONED_AMENDMENT_PARTIAL = "reasoned_amendment_partial"
SECOND_STAGE = "second_stage"


class Motion(BaseModel):
Expand Down Expand Up @@ -167,11 +170,18 @@ def self_flag(self):
"""
Any extra tags to add based on the final content
"""
content = str(self).lower()
content = str(self).lower().replace("\n", " ").replace(" ", " ")
if len(self.motion_lines) < 3:
if abstract_motion(content):
self.add_flag(Flag.ABSTRACT_MOTION)

if reasoned_amendment_full(content):
self.add_flag(Flag.REASONED_AMENDMENT_FULL)
if second_time_flag(content):
self.add_flag(Flag.SECOND_STAGE)
if reasoned_amendment_partial(content):
self.add_flag(Flag.REASONED_AMENDMENT_PARTIAL)

if amendment_flag(content):
self.add_flag(Flag.MOTION_AMENDMENT)
elif main_question(content):
Expand Down Expand Up @@ -225,12 +235,23 @@ def dump_test_data(self, tests_data_path: Path):
]
)

reasoned_amendment_partial = PhraseDetector(
criteria=[
re.compile(r"The reasoned amendment .* has been selected", re.IGNORECASE),
]
)

reasoned_amendment_full = PhraseDetector(
criteria=["declines to give a second reading", "declines to give a third reading"]
)

# for adding a flag after bringing the contents together
amendment_flag = PhraseDetector(
criteria=[
"I beg to move an amendment",
"I beg to move amendment",
"Amendment proposed: at the end of the Question",
re.compile(r"The reasoned amendment .* has been selected", re.IGNORECASE),
re.compile(
r"question is, (?:that|the) amendment \d+\w? be agreed to\. Are we(?: all)? agreed\?",
re.IGNORECASE,
Expand Down Expand Up @@ -377,6 +398,17 @@ def dump_test_data(self, tests_data_path: Path):
re.IGNORECASE,
),
re.compile(r"The next question is, that motion .* be agreed to", re.IGNORECASE),
re.compile(r"The reasoned amendment .* has been selected", re.IGNORECASE),
]
)

second_time_flag = PhraseDetector(
criteria=[
"That the Bill be now read a Second time",
"Motion made, That the Bill be read be now read a Second time",
"Motion made, That the Bill be now read a Secondtime",
"That the Bill be now read a second time",
"the Bill be now read a Second time",
]
)

Expand Down Expand Up @@ -440,6 +472,7 @@ def dump_test_data(self, tests_data_path: Path):
re.IGNORECASE,
),
re.compile(r"The next question is, that motion .* be agreed to", re.IGNORECASE),
re.compile(r"The reasoned amendment .* has been selected", re.IGNORECASE),
]
)

Expand Down

0 comments on commit ecad6a7

Please sign in to comment.