File size: 6,880 Bytes
a162c90
ca78935
c71e312
ca78935
c71e312
 
 
 
 
 
 
 
 
 
a162c90
c71e312
 
 
 
 
 
 
14279a0
 
 
0f7804c
 
 
c71e312
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a162c90
c71e312
 
 
 
a162c90
c71e312
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ca78935
a162c90
c71e312
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ca78935
c71e312
 
ca78935
c71e312
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ca78935
 
 
 
 
 
3f2cfa1
 
ca78935
 
 
 
3f2cfa1
ca78935
 
 
3f2cfa1
14279a0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84b9e0c
0f7804c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# src/expon/presentation/interfaces/rest/controllers/presentation_controller.py
from fastapi import APIRouter, UploadFile, File, Depends, HTTPException, status
from sqlalchemy.orm import Session
from uuid import UUID

from src.expon.shared.infrastructure.dependencies import get_db
from src.expon.iam.infrastructure.authorization.sfs.auth_bearer import get_current_user

from src.expon.presentation.application.internal.commandservices.audio_upload_service import AudioUploadService
from src.expon.presentation.application.internal.queryservices.presentation_query_service import PresentationQueryService

from src.expon.presentation.infrastructure.persistence.jpa.repositories.presentation_repository import PresentationRepository
from src.expon.presentation.infrastructure.services.storage.local_storage_service import LocalStorageService
from src.expon.presentation.domain.services.transcription_service import TranscriptionService
from src.expon.presentation.domain.services.multimodal_service import MultimodalService  # ← CAMBIO

from src.expon.presentation.interfaces.rest.responses.presentation_response import (
    PresentationResponse,
    AudioMetadataResponse,
    PresentationSummaryResponse
)

from src.expon.feedback.infrastructure.persistence.jpa.feedback_repository import FeedbackRepository
from src.expon.feedback.interfaces.rest.feedback_response import FeedbackResponse

from src.expon.presentation.infrastructure.services.storage.supabase_storage_service import generate_signed_audio_url


router = APIRouter()

@router.post("/upload", response_model=PresentationResponse)
def upload_presentation(
    file: UploadFile = File(...),
    db: Session = Depends(get_db),
    current_user=Depends(get_current_user)
):
    try:
        contents = file.file.read()
        if not contents:
            raise HTTPException(status_code=400, detail="El archivo está vacío.")
        file.file.seek(0)

        repository = PresentationRepository(db)
        storage_service = LocalStorageService()
        transcription_service = TranscriptionService()
        multimodal_service = MultimodalService()  # ← CAMBIO

        service = AudioUploadService(
            storage_service=storage_service,
            transcription_service=transcription_service,
            multimodal_service=multimodal_service,  # ← CAMBIO
            repository=repository
        )

        presentation = service.upload_and_analyze(file, current_user.id)

        return PresentationResponse(
            id=presentation.id,
            transcript=presentation.transcript,
            dominant_emotion=presentation.dominant_emotion,
            emotion_probabilities=presentation.emotion_probabilities,
            confidence=presentation.confidence,
            filename=presentation.filename,
            metadata=AudioMetadataResponse(
                duration=presentation.metadata.duration,
                sample_rate=presentation.metadata.sample_rate,
                language=presentation.metadata.language
            ),
            created_at=presentation.created_at
        )

    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Error al procesar el archivo: {str(e)}")



@router.get("/summary", response_model=list[PresentationSummaryResponse])
def get_presentations_summary(
    db: Session = Depends(get_db),
    current_user=Depends(get_current_user)
):
    repository = PresentationRepository(db)
    query_service = PresentationQueryService(repository)
    presentations = query_service.get_presentations_by_user(current_user.id)

    return [
        PresentationSummaryResponse(
            id=p.id,
            filename=p.filename,
            dominant_emotion=p.dominant_emotion,
            confidence=p.confidence,
            created_at=p.created_at
        ) for p in presentations
    ]


@router.get("/{presentation_id}", response_model=PresentationResponse)
def get_presentation_by_id(
    presentation_id: UUID,
    db: Session = Depends(get_db),
    current_user=Depends(get_current_user)
):
    repository = PresentationRepository(db)
    query_service = PresentationQueryService(repository)
    presentation = query_service.get_presentation_by_id_and_user(presentation_id, current_user.id)

    if presentation is None:
        raise HTTPException(status_code=404, detail="Presentación no encontrada")

    return PresentationResponse(
        id=presentation.id,
        transcript=presentation.transcript,
        dominant_emotion=presentation.dominant_emotion,
        emotion_probabilities=presentation.emotion_probabilities,
        confidence=presentation.confidence,
        filename=presentation.filename,
        metadata=AudioMetadataResponse(
            duration=presentation.metadata.duration,
            sample_rate=presentation.metadata.sample_rate,
            language=presentation.metadata.language
        ),
        created_at=presentation.created_at
    )


@router.delete("/{presentation_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_presentation(
    presentation_id: UUID,
    db: Session = Depends(get_db),
    current_user=Depends(get_current_user)
):
    repository = PresentationRepository(db)
    presentation = repository.get_by_id_and_user(presentation_id, current_user.id)

    if presentation is None:
        raise HTTPException(status_code=404, detail="Presentación no encontrada")

    # Eliminar archivo local
    storage_service = LocalStorageService()
    storage_service.delete(presentation.filename)

    # Eliminar de base de datos
    repository.delete(presentation)


@router.get("/feedback/presentation/{presentation_id}", response_model=FeedbackResponse)
def get_feedback_by_presentation(
    presentation_id: UUID,
    db: Session = Depends(get_db)
):
    repo = FeedbackRepository(db)
    feedback = repo.get_by_presentation(presentation_id)

    if feedback is None:
        raise HTTPException(status_code=404, detail="Feedback no encontrado")

    return feedback


@router.get("/{presentation_id}/audio-url")
def get_audio_signed_url(
    presentation_id: UUID,
    db: Session = Depends(get_db),
    current_user=Depends(get_current_user)
):
    """
    Retorna una URL firmada temporal del audio asociado a la presentación.
    """
    repository = PresentationRepository(db)
    presentation = repository.get_by_id_and_user(presentation_id, current_user.id)

    if presentation is None:
        raise HTTPException(status_code=404, detail="Presentación no encontrada")

    if not presentation.audio_path:
        raise HTTPException(status_code=404, detail="No hay un audio asociado a esta presentación")

    try:
        signed_url = generate_signed_audio_url(presentation.audio_path)
        return {"url": signed_url}
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Error generando URL firmada: {str(e)}")