diff --git a/src/audita/core/schemas.py b/src/audita/core/schemas.py index ded9567..1ca00ca 100644 --- a/src/audita/core/schemas.py +++ b/src/audita/core/schemas.py @@ -8,7 +8,7 @@ from .errors import AuditaValidationError class TranscriptSegment(BaseModel): - model_config = ConfigDict(extra="forbid") + model_config = ConfigDict(extra="ignore") id: int = Field(ge=1) speaker: StrictStr @@ -50,7 +50,7 @@ class TranscriptSegment(BaseModel): class SourceTranscriptSegment(BaseModel): - model_config = ConfigDict(extra="forbid") + model_config = ConfigDict(extra="ignore") id: Optional[int] = Field(default=None, ge=1) speaker: StrictStr @@ -146,8 +146,9 @@ def validate_transcript_data( data: Any, require_sequential_ids: bool = True, ) -> List[TranscriptSegment]: + data = _extract_transcript_segments(data) if not isinstance(data, list): - raise AuditaValidationError("Transcript must be a JSON array.") + raise AuditaValidationError("Transcript must be a JSON array or an object with a segments array.") if not data: raise AuditaValidationError("Transcript must contain at least one segment.") try: @@ -160,8 +161,9 @@ def validate_transcript_data( def validate_source_transcript_data(data: Any) -> List[SourceTranscriptSegment]: + data = _extract_transcript_segments(data) if not isinstance(data, list): - raise AuditaValidationError("Transcript must be a JSON array.") + raise AuditaValidationError("Transcript must be a JSON array or an object with a segments array.") if not data: raise AuditaValidationError("Transcript must contain at least one segment.") try: @@ -215,6 +217,12 @@ def source_transcript_to_json(segments: List[SourceTranscriptSegment]) -> str: return json.dumps(payload, ensure_ascii=False, indent=2) + "\n" +def _extract_transcript_segments(data: Any) -> Any: + if isinstance(data, dict) and "segments" in data: + return data["segments"] + return data + + def _validate_sequential_ids(transcript: List[TranscriptSegment]) -> None: ids = [segment.id for segment in transcript] expected = list(range(1, len(transcript) + 1)) diff --git a/tests/test_new_transcript_schema.py b/tests/test_new_transcript_schema.py new file mode 100644 index 0000000..29802d6 --- /dev/null +++ b/tests/test_new_transcript_schema.py @@ -0,0 +1,69 @@ +from audita.core.schemas import parse_source_transcript_json, parse_transcript_json + + +SERIATIM_TRANSCRIPT = """ +{ + "metadata": { + "application": "seriatim", + "version": "dev", + "input_reader": "json-files", + "input_files": ["eric.json", "mike.json"], + "preprocessing_modules": ["validate-raw", "normalize-speakers", "trim-text"], + "postprocessing_modules": ["detect-overlaps", "resolve-overlaps", "backchannel"], + "output_modules": ["json"] + }, + "segments": [ + { + "id": 1, + "source": "eric.json", + "source_segment_index": 0, + "speaker": "Eric Rakestraw", + "start": 1.25, + "end": 3.5, + "text": "Hello there.", + "overlap_group_id": 1 + }, + { + "id": 2, + "source": "eric.json", + "source_ref": "word-run:1:1:1", + "derived_from": ["eric.json#0"], + "speaker": "Eric Rakestraw", + "start": 4.0, + "end": 4.5, + "text": "Resolved word run", + "categories": ["backchannel"] + } + ], + "overlap_groups": [ + { + "id": 1, + "start": 1.25, + "end": 4.0, + "segments": ["eric.json#0", "mike.json#0"], + "speakers": ["Eric Rakestraw", "Mike Brown"], + "class": "unknown", + "resolution": "unresolved" + } + ] +} +""" + + +def test_parse_source_transcript_json_accepts_seriatim_transcript_object(): + segments = parse_source_transcript_json(SERIATIM_TRANSCRIPT) + + assert len(segments) == 2 + assert segments[0].id == 1 + assert segments[0].speaker == "Eric Rakestraw" + assert segments[0].start == 1.25 + assert segments[0].end == 3.5 + assert segments[0].text == "Hello there." + assert segments[1].text == "Resolved word run" + + +def test_parse_transcript_json_accepts_seriatim_transcript_object_and_ignores_unused_fields(): + segments = parse_transcript_json(SERIATIM_TRANSCRIPT) + + assert [segment.id for segment in segments] == [1, 2] + assert [segment.text for segment in segments] == ["Hello there.", "Resolved word run"]