mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2025-12-20 20:29:06 +00:00
* Add security review doc * Add tests to reproduce security issues * Fix security issues * Fix tests * Set auth auth backend for tests * Fix ics api tests * Fix transcript mutate check * Update frontent env var names * Remove permissions doc
177 lines
5.1 KiB
Python
177 lines
5.1 KiB
Python
"""
|
|
Reassign speakers in a transcript
|
|
=================================
|
|
|
|
"""
|
|
|
|
from typing import Annotated, Optional
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from pydantic import BaseModel, Field
|
|
|
|
import reflector.auth as auth
|
|
from reflector.db.transcripts import transcripts_controller
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class SpeakerAssignment(BaseModel):
|
|
speaker: Optional[int] = Field(None, ge=0)
|
|
participant: Optional[str] = Field(None)
|
|
timestamp_from: float
|
|
timestamp_to: float
|
|
|
|
|
|
class SpeakerAssignmentStatus(BaseModel):
|
|
status: str
|
|
|
|
|
|
class SpeakerMerge(BaseModel):
|
|
speaker_from: int
|
|
speaker_to: int
|
|
|
|
|
|
@router.patch("/transcripts/{transcript_id}/speaker/assign")
|
|
async def transcript_assign_speaker(
|
|
transcript_id: str,
|
|
assignment: SpeakerAssignment,
|
|
user: Annotated[auth.UserInfo, Depends(auth.current_user)],
|
|
) -> SpeakerAssignmentStatus:
|
|
user_id = user["sub"]
|
|
transcript = await transcripts_controller.get_by_id_for_http(
|
|
transcript_id, user_id=user_id
|
|
)
|
|
if transcript.user_id is not None and transcript.user_id != user_id:
|
|
raise HTTPException(status_code=403, detail="Not authorized")
|
|
|
|
if not transcript:
|
|
raise HTTPException(status_code=404, detail="Transcript not found")
|
|
|
|
if assignment.speaker is None and assignment.participant is None:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail="Either speaker or participant must be provided",
|
|
)
|
|
|
|
if assignment.speaker is not None and assignment.participant is not None:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail="Only one of speaker or participant must be provided",
|
|
)
|
|
|
|
# if it's a participant, search for it
|
|
if assignment.speaker is not None:
|
|
speaker = assignment.speaker
|
|
|
|
elif assignment.participant is not None:
|
|
participant = next(
|
|
(
|
|
participant
|
|
for participant in transcript.participants
|
|
if participant.id == assignment.participant
|
|
),
|
|
None,
|
|
)
|
|
if not participant:
|
|
raise HTTPException(
|
|
status_code=404,
|
|
detail="Participant not found",
|
|
)
|
|
|
|
# if the participant does not have a speaker, create one
|
|
if participant.speaker is None:
|
|
participant.speaker = transcript.find_empty_speaker()
|
|
await transcripts_controller.upsert_participant(transcript, participant)
|
|
|
|
speaker = participant.speaker
|
|
|
|
# reassign speakers from words in the transcript
|
|
ts_from = assignment.timestamp_from
|
|
ts_to = assignment.timestamp_to
|
|
changed_topics = []
|
|
for topic in transcript.topics:
|
|
changed = False
|
|
for word in topic.words:
|
|
if ts_from <= word.start <= ts_to:
|
|
word.speaker = speaker
|
|
changed = True
|
|
if changed:
|
|
changed_topics.append(topic)
|
|
|
|
# batch changes
|
|
for topic in changed_topics:
|
|
transcript.upsert_topic(topic)
|
|
await transcripts_controller.update(
|
|
transcript,
|
|
{
|
|
"topics": transcript.topics_dump(),
|
|
},
|
|
)
|
|
|
|
return SpeakerAssignmentStatus(status="ok")
|
|
|
|
|
|
@router.patch("/transcripts/{transcript_id}/speaker/merge")
|
|
async def transcript_merge_speaker(
|
|
transcript_id: str,
|
|
merge: SpeakerMerge,
|
|
user: Annotated[auth.UserInfo, Depends(auth.current_user)],
|
|
) -> SpeakerAssignmentStatus:
|
|
user_id = user["sub"]
|
|
transcript = await transcripts_controller.get_by_id_for_http(
|
|
transcript_id, user_id=user_id
|
|
)
|
|
if transcript.user_id is not None and transcript.user_id != user_id:
|
|
raise HTTPException(status_code=403, detail="Not authorized")
|
|
|
|
if not transcript:
|
|
raise HTTPException(status_code=404, detail="Transcript not found")
|
|
|
|
# ensure both speaker are not assigned to the 2 differents participants
|
|
participant_from = next(
|
|
(
|
|
participant
|
|
for participant in transcript.participants
|
|
if participant.speaker == merge.speaker_from
|
|
),
|
|
None,
|
|
)
|
|
participant_to = next(
|
|
(
|
|
participant
|
|
for participant in transcript.participants
|
|
if participant.speaker == merge.speaker_to
|
|
),
|
|
None,
|
|
)
|
|
if participant_from and participant_to:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail="Both speakers are assigned to participants",
|
|
)
|
|
|
|
# reassign speakers from words in the transcript
|
|
speaker_from = merge.speaker_from
|
|
speaker_to = merge.speaker_to
|
|
changed_topics = []
|
|
for topic in transcript.topics:
|
|
changed = False
|
|
for word in topic.words:
|
|
if word.speaker == speaker_from:
|
|
word.speaker = speaker_to
|
|
changed = True
|
|
if changed:
|
|
changed_topics.append(topic)
|
|
|
|
# batch changes
|
|
for topic in changed_topics:
|
|
transcript.upsert_topic(topic)
|
|
await transcripts_controller.update(
|
|
transcript,
|
|
{
|
|
"topics": transcript.topics_dump(),
|
|
},
|
|
)
|
|
|
|
return SpeakerAssignmentStatus(status="ok")
|