109 lines
4.3 KiB
Python
109 lines
4.3 KiB
Python
"""Focused tests for transcript-derived EasyCom interaction proxies."""
|
|||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import unittest
|
||
|
|
|
||
|
|
from derive_easycom_interaction_events import Rules, Utterance, classify_utterance
|
||
|
|
|
||
|
|
|
||
|
|
def utterance(
|
||
|
|
uid: str,
|
||
|
|
participant: int,
|
||
|
|
start: float,
|
||
|
|
end: float,
|
||
|
|
text: str,
|
||
|
|
*,
|
||
|
|
target: tuple[int | str, ...] = ("Group",),
|
||
|
|
) -> 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=target,
|
||
|
|
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=False,
|
||
|
|
ends_at_clip_boundary=False,
|
||
|
|
annotation_end_frame_clamped_by=0,
|
||
|
|
is_cross_clip_continuation=False,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
class InteractionProxyTests(unittest.TestCase):
|
||
|
|
def setUp(self) -> None:
|
||
|
|
self.rules = Rules()
|
||
|
|
|
||
|
|
def classify(self, target: Utterance, rows: list[Utterance]):
|
||
|
|
return classify_utterance(
|
||
|
|
target,
|
||
|
|
rows,
|
||
|
|
camera_wearer_id=2,
|
||
|
|
rules=self.rules,
|
||
|
|
)
|
||
|
|
|
||
|
|
def test_short_feedback_inside_retained_floor_is_backchannel_proxy(self) -> None:
|
||
|
|
floor = utterance("floor", 4, 0.0, 4.0, "Long explanation")
|
||
|
|
feedback = utterance("feedback", 2, 1.0, 1.4, "Yeah.", target=(4,))
|
||
|
|
event = self.classify(feedback, [floor, feedback])
|
||
|
|
self.assertEqual(event.event_type, "backchannel_proxy_high")
|
||
|
|
self.assertTrue(event.trainable_auxiliary)
|
||
|
|
self.assertEqual(event.retained_floor_participant_id, 4)
|
||
|
|
|
||
|
|
def test_short_answer_without_floor_return_is_not_backchannel(self) -> None:
|
||
|
|
question = utterance("question", 4, 0.0, 1.0, "Ready?")
|
||
|
|
answer = utterance("answer", 2, 1.1, 2.3, "Yeah, let us start.", target=(4,))
|
||
|
|
event = self.classify(answer, [question, answer])
|
||
|
|
self.assertEqual(event.event_type, "ordinary_switch")
|
||
|
|
|
||
|
|
def test_overlap_takeover_after_cutoff_is_interruption_proxy(self) -> None:
|
||
|
|
old = utterance("old", 4, 0.0, 2.0, "I was going to-")
|
||
|
|
incoming = utterance("incoming", 2, 1.7, 3.2, "Wait, that is wrong.", target=(4,))
|
||
|
|
event = self.classify(incoming, [old, incoming])
|
||
|
|
self.assertEqual(event.event_type, "interruption_proxy_high")
|
||
|
|
self.assertTrue(event.trainable_auxiliary)
|
||
|
|
|
||
|
|
def test_overlap_takeover_without_cutoff_is_not_called_interruption(self) -> None:
|
||
|
|
old = utterance("old", 4, 0.0, 2.0, "I finished my point.")
|
||
|
|
incoming = utterance("incoming", 2, 1.7, 3.2, "Here is my response.", target=(4,))
|
||
|
|
event = self.classify(incoming, [old, incoming])
|
||
|
|
self.assertEqual(event.event_type, "overlap_takeover")
|
||
|
|
self.assertTrue(event.trainable_auxiliary)
|
||
|
|
|
||
|
|
def test_short_nonlexical_floor_return_is_masked_candidate(self) -> None:
|
||
|
|
before = utterance("before", 4, 0.0, 1.2, "First part")
|
||
|
|
brief = utterance("brief", 2, 1.25, 1.8, "Possibly.", target=(4,))
|
||
|
|
after = utterance("after", 4, 1.9, 3.0, "Second part")
|
||
|
|
event = self.classify(brief, [before, brief, after])
|
||
|
|
self.assertEqual(event.event_type, "backchannel_candidate")
|
||
|
|
self.assertFalse(event.trainable_auxiliary)
|
||
|
|
|
||
|
|
def test_hesitation_marker_is_not_nonverbal_feedback(self) -> None:
|
||
|
|
floor = utterance("floor", 4, 0.0, 4.0, "Long explanation")
|
||
|
|
hesitation = utterance("hesitation", 2, 1.0, 1.2, "[H]", target=(4,))
|
||
|
|
event = self.classify(hesitation, [floor, hesitation])
|
||
|
|
self.assertEqual(event.event_type, "nonverbal_candidate")
|
||
|
|
self.assertFalse(event.trainable_auxiliary)
|
||
|
|
|
||
|
|
def test_cross_clip_continuation_never_creates_switch(self) -> None:
|
||
|
|
row = utterance("continued", 2, 60.0, 61.0, "[X] continued")
|
||
|
|
row = Utterance(
|
||
|
|
**{**row.__dict__, "is_cross_clip_continuation": True}
|
||
|
|
)
|
||
|
|
event = self.classify(row, [row])
|
||
|
|
self.assertEqual(event.event_type, "cross_clip_continuation")
|
||
|
|
self.assertFalse(event.trainable_auxiliary)
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
unittest.main()
|