Add manuscript-aligned EasyCom EOT INT review workflow
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from review_floor_events import (
|
||||
CandidateRepository,
|
||||
ReviewStore,
|
||||
expand_candidate_paths,
|
||||
load_candidates,
|
||||
parse_byte_range,
|
||||
)
|
||||
|
||||
|
||||
class FloorEventReviewTests(unittest.TestCase):
|
||||
def _fixture(self, root: Path) -> tuple[Path, Path]:
|
||||
dataset = root / "dataset"
|
||||
video_dir = dataset / "Main" / "Video_Compressed" / "Session_4"
|
||||
transcript_dir = dataset / "Main" / "Speech_Transcriptions" / "Session_4"
|
||||
video_dir.mkdir(parents=True)
|
||||
transcript_dir.mkdir(parents=True)
|
||||
(video_dir / "00-00-000.mp4").write_bytes(b"mock-video")
|
||||
(transcript_dir / "00-00-000.json").write_text(
|
||||
json.dumps(
|
||||
[
|
||||
{
|
||||
"Start_Frame": 1,
|
||||
"End_Frame": 20,
|
||||
"Participant_ID": 4,
|
||||
"Transcription": "Are you coming?",
|
||||
"Target_of_Speech": [2],
|
||||
},
|
||||
{
|
||||
"Start_Frame": 25,
|
||||
"End_Frame": 40,
|
||||
"Participant_ID": 2,
|
||||
"Transcription": "Yes.",
|
||||
"Target_of_Speech": [4],
|
||||
},
|
||||
]
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
events = root / "candidates.jsonl"
|
||||
events.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"uid": "Session_4/00-00-000/0001",
|
||||
"session": "Session_4",
|
||||
"split": "train",
|
||||
"event_type": "ordinary_switch",
|
||||
"confidence": "timing",
|
||||
"participant_role": "wearer",
|
||||
"clip_stem": "00-00-000",
|
||||
"source_start_frame": 25,
|
||||
"dense_start_seconds": 1.2,
|
||||
}
|
||||
)
|
||||
+ "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
return dataset, events
|
||||
|
||||
def test_candidate_detail_resolves_media_and_transcript(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
root = Path(temporary)
|
||||
dataset, events = self._fixture(root)
|
||||
candidates = load_candidates([events])
|
||||
repository = CandidateRepository(
|
||||
candidates,
|
||||
dataset,
|
||||
context_before_seconds=3.0,
|
||||
context_after_seconds=3.0,
|
||||
)
|
||||
detail = repository.detail(candidates[0])
|
||||
self.assertTrue(detail["media_available"])
|
||||
self.assertEqual(detail["event_local_seconds"], 1.2)
|
||||
self.assertEqual(len(detail["transcript"]), 2)
|
||||
self.assertTrue(detail["transcript"][1]["is_anchor"])
|
||||
|
||||
def test_review_store_writes_resumable_jsonl_and_csv(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
root = Path(temporary)
|
||||
_, events = self._fixture(root)
|
||||
candidates = load_candidates([events])
|
||||
output = root / "reviews" / "reviews.jsonl"
|
||||
store = ReviewStore(output, candidates)
|
||||
review = {
|
||||
"protocol_disposition": "positive",
|
||||
"hard_negative_type": "not_applicable",
|
||||
"validity": "valid",
|
||||
"onset_function": "backchannel",
|
||||
"context": "addressed_wearer",
|
||||
"offset_outcome": "not_applicable",
|
||||
"anchor_adjustment_bins": 0,
|
||||
"behavior_adjustment_bins": 0,
|
||||
"region_end_adjustment_bins": None,
|
||||
"confidence": "high",
|
||||
"reviewer": "r1",
|
||||
"notes": "clear acknowledgement",
|
||||
}
|
||||
store.save(candidates[0].candidate_id, review)
|
||||
self.assertTrue(output.is_file())
|
||||
self.assertTrue(output.with_suffix(".csv").is_file())
|
||||
resumed = ReviewStore(output, candidates)
|
||||
self.assertEqual(
|
||||
resumed.reviews[candidates[0].candidate_id]["review"]["onset_function"],
|
||||
"backchannel",
|
||||
)
|
||||
|
||||
def test_directory_expansion_excludes_output(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
root = Path(temporary)
|
||||
candidate = root / "Session_4.events.jsonl"
|
||||
output = root / "reviews.jsonl"
|
||||
candidate.write_text("{}\n", encoding="utf-8")
|
||||
output.write_text("{}\n", encoding="utf-8")
|
||||
self.assertEqual(
|
||||
expand_candidate_paths([root], output_path=output),
|
||||
[candidate.resolve()],
|
||||
)
|
||||
|
||||
def test_byte_ranges(self) -> None:
|
||||
self.assertEqual(parse_byte_range("bytes=2-5", 10), (2, 5))
|
||||
self.assertEqual(parse_byte_range("bytes=7-", 10), (7, 9))
|
||||
self.assertEqual(parse_byte_range("bytes=-3", 10), (7, 9))
|
||||
with self.assertRaises(ValueError):
|
||||
parse_byte_range("bytes=20-30", 10)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user