126 lines
4.1 KiB
Python
126 lines
4.1 KiB
Python
"""Tests for conservative EasyCom floor-event auditing."""
|
|||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import unittest
|
||
|
|
|
||
|
|
from audit_easycom_floor_events import derive_eot_candidates
|
||
|
|
from derive_easycom_interaction_events import Rules, Utterance
|
||
|
|
|
||
|
|
|
||
|
|
def utterance(
|
||
|
|
uid: str,
|
||
|
|
participant: int,
|
||
|
|
start: float,
|
||
|
|
end: float,
|
||
|
|
text: str,
|
||
|
|
*,
|
||
|
|
cross_clip: bool = False,
|
||
|
|
starts_at_boundary: bool = False,
|
||
|
|
ends_at_boundary: bool = False,
|
||
|
|
) -> Utterance:
|
||
|
|
return Utterance(
|
||
|
|
uid=uid,
|
||
|
|
session="Session_7",
|
||
|
|
clip_stem="00-00-000",
|
||
|
|
clip_index=0,
|
||
|
|
segment_id=0,
|
||
|
|
participant_id=participant,
|
||
|
|
target_of_speech=("Group",),
|
||
|
|
transcription=text,
|
||
|
|
source_start_frame=round(start * 20) + 1,
|
||
|
|
source_end_frame=round(end * 20),
|
||
|
|
dense_start_frame=round(start * 20),
|
||
|
|
dense_end_frame_exclusive=round(end * 20),
|
||
|
|
dense_start_seconds=start,
|
||
|
|
dense_end_seconds=end,
|
||
|
|
duration_seconds=end - start,
|
||
|
|
starts_at_clip_boundary=starts_at_boundary,
|
||
|
|
ends_at_clip_boundary=ends_at_boundary,
|
||
|
|
annotation_end_frame_clamped_by=0,
|
||
|
|
is_cross_clip_continuation=cross_clip,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
class EasyComFloorAuditTests(unittest.TestCase):
|
||
|
|
def setUp(self) -> None:
|
||
|
|
self.rules = Rules()
|
||
|
|
|
||
|
|
def candidates(self, rows: list[Utterance]):
|
||
|
|
return derive_eot_candidates(
|
||
|
|
rows,
|
||
|
|
camera_wearer_id=2,
|
||
|
|
rules=self.rules,
|
||
|
|
)
|
||
|
|
|
||
|
|
def test_sustained_overlapping_incoming_speaker_is_positive_proxy(self) -> None:
|
||
|
|
holder = utterance("holder", 4, 0.0, 2.0, "I finished my point.")
|
||
|
|
incoming = utterance("incoming", 2, 1.7, 3.2, "Here is my response.")
|
||
|
|
result = {row.holder_uid: row for row in self.candidates([holder, incoming])}
|
||
|
|
self.assertEqual(
|
||
|
|
result["holder"].category,
|
||
|
|
"eot_positive_overlap_takeover_proxy",
|
||
|
|
)
|
||
|
|
self.assertEqual(result["holder"].incoming_uid, "incoming")
|
||
|
|
|
||
|
|
def test_same_speaker_resumption_is_mid_turn_pause_negative(self) -> None:
|
||
|
|
first = utterance("first", 4, 0.0, 1.0, "First clause.")
|
||
|
|
resumed = utterance("resumed", 4, 1.3, 2.5, "Second clause.")
|
||
|
|
result = {row.holder_uid: row for row in self.candidates([first, resumed])}
|
||
|
|
self.assertEqual(
|
||
|
|
result["first"].category,
|
||
|
|
"eot_negative_mid_turn_pause_proxy",
|
||
|
|
)
|
||
|
|
self.assertAlmostEqual(result["first"].pause_seconds, 0.3)
|
||
|
|
|
||
|
|
def test_long_same_speaker_gap_is_ambiguous(self) -> None:
|
||
|
|
first = utterance("first", 4, 0.0, 1.0, "First clause.")
|
||
|
|
resumed = utterance("resumed", 4, 4.0, 5.0, "Much later.")
|
||
|
|
result = {row.holder_uid: row for row in self.candidates([first, resumed])}
|
||
|
|
self.assertEqual(
|
||
|
|
result["first"].category,
|
||
|
|
"eot_ambiguous_long_same_speaker_gap",
|
||
|
|
)
|
||
|
|
|
||
|
|
def test_short_unresolved_incoming_speaker_is_kept_for_review(self) -> None:
|
||
|
|
holder = utterance("holder", 4, 0.0, 1.0, "First speaker.")
|
||
|
|
short = utterance("short", 2, 1.2, 1.6, "Maybe.")
|
||
|
|
resumed = utterance("resumed", 4, 3.5, 4.5, "First speaker continues.")
|
||
|
|
result = {
|
||
|
|
row.holder_uid: row
|
||
|
|
for row in self.candidates([holder, short, resumed])
|
||
|
|
}
|
||
|
|
self.assertEqual(
|
||
|
|
result["holder"].category,
|
||
|
|
"eot_ambiguous_short_or_unresolved_incoming",
|
||
|
|
)
|
||
|
|
self.assertEqual(result["holder"].incoming_uid, "short")
|
||
|
|
|
||
|
|
def test_adjacent_cross_file_continuation_is_excluded(self) -> None:
|
||
|
|
first = utterance(
|
||
|
|
"first",
|
||
|
|
4,
|
||
|
|
0.0,
|
||
|
|
60.0,
|
||
|
|
"Continued [X]",
|
||
|
|
ends_at_boundary=True,
|
||
|
|
)
|
||
|
|
resumed = utterance(
|
||
|
|
"resumed",
|
||
|
|
4,
|
||
|
|
60.0,
|
||
|
|
61.0,
|
||
|
|
"[X] continuation",
|
||
|
|
cross_clip=True,
|
||
|
|
starts_at_boundary=True,
|
||
|
|
)
|
||
|
|
result = {row.holder_uid: row for row in self.candidates([first, resumed])}
|
||
|
|
self.assertEqual(
|
||
|
|
result["first"].category,
|
||
|
|
"eot_excluded_file_continuation",
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
unittest.main()
|