colab-user commited on
Commit
f2126e0
·
1 Parent(s): d8fc038

fix FE & schema

Browse files
Files changed (2) hide show
  1. app/schemas/models.py +18 -8
  2. app/static/js/app.js +37 -21
app/schemas/models.py CHANGED
@@ -15,15 +15,23 @@ class ProcessingStatus(str, Enum):
15
 
16
 
17
  class TranscriptSegment(BaseModel):
18
- """A single segment of the transcript with speaker and timing."""
19
- start: float = Field(..., description="Start time in seconds")
20
- end: float = Field(..., description="End time in seconds")
21
- speaker: str = Field(..., description="Speaker identifier (e.g. SPEAKER_0)")
22
- role: Optional[str] = Field(
23
  default=None,
24
- description="Speaker role (e.g. NV, KH)"
 
 
 
 
 
 
 
 
 
 
25
  )
26
- text: str = Field(..., description="Transcribed text")
27
 
28
 
29
  @property
@@ -63,11 +71,13 @@ class TranscriptionResponse(BaseModel):
63
  duration: float = Field(default=0.0, description="Audio duration in seconds")
64
  speaker_count: int = Field(default=0, description="Number of detected speakers")
65
  processing_time: float = Field(default=0.0, description="Processing time in seconds")
 
66
 
67
  roles: Optional[dict[str, str]] = Field(
68
  default=None,
69
- description="Speaker to role mapping (e.g. SPEAKER_0 AGENT)"
70
  )
 
71
  download_txt: Optional[str] = Field(default=None, description="Download URL for TXT file")
72
  download_csv: Optional[str] = Field(default=None, description="Download URL for CSV file")
73
 
 
15
 
16
 
17
  class TranscriptSegment(BaseModel):
18
+ start: float
19
+ end: float
20
+
21
+ speaker: Optional[str] = Field(
 
22
  default=None,
23
+ description="Internal speaker id (debug only)"
24
+ )
25
+
26
+ role: str = Field(
27
+ ...,
28
+ description="Conversation role (NV = agent, KH = customer)"
29
+ )
30
+
31
+ text: str = Field(
32
+ ...,
33
+ description="Transcribed text"
34
  )
 
35
 
36
 
37
  @property
 
71
  duration: float = Field(default=0.0, description="Audio duration in seconds")
72
  speaker_count: int = Field(default=0, description="Number of detected speakers")
73
  processing_time: float = Field(default=0.0, description="Processing time in seconds")
74
+ speakers: Optional[list[str]] = None
75
 
76
  roles: Optional[dict[str, str]] = Field(
77
  default=None,
78
+ description="Internal mapping speaker_id → role (debug / audit only)"
79
  )
80
+
81
  download_txt: Optional[str] = Field(default=None, description="Download URL for TXT file")
82
  download_csv: Optional[str] = Field(default=None, description="Download URL for CSV file")
83
 
app/static/js/app.js CHANGED
@@ -206,53 +206,68 @@ document.addEventListener('DOMContentLoaded', () => {
206
  // =====================
207
 
208
  function displayResults(result) {
209
- // Update metadata
210
- elements.speakerCount.textContent =`${result.speaker_count} speaker${result.speaker_count !== 1 ? 's' : ''}`;
 
 
 
 
 
 
 
 
 
 
211
 
212
- elements.durationInfo.textContent = formatDuration(result.duration);
213
- elements.processingTime.textContent = `${result.processing_time}s`;
 
 
 
214
 
215
- // Set download links
216
- elements.downloadTxt.href = result.download_txt;
217
- elements.downloadCsv.href = result.download_csv;
 
218
 
219
- // Render transcript segments
220
- renderTranscript(result.segments);
221
 
222
- // Show results section
223
  showSection('results');
224
  }
225
 
226
  function renderTranscript(segments) {
227
  elements.transcriptContainer.innerHTML = '';
228
 
229
- const speakerColors = {};
230
  let colorIndex = 0;
231
 
232
  segments.forEach((segment) => {
 
233
 
234
- const displayName = segment.role
235
- ? segment.role
236
- : segment.speaker;
237
-
238
- if (!(displayName in speakerColors)) {
239
  colorIndex++;
240
- speakerColors[displayName] = `speaker-${Math.min(colorIndex, 5)}`;
241
  }
242
 
243
  const segmentEl = document.createElement('div');
244
- segmentEl.className = `segment ${speakerColors[displayName]}`;
 
 
 
 
245
 
246
  segmentEl.innerHTML = `
247
  <div class="segment-header">
248
  <span class="segment-speaker">
249
- ${escapeHtml(displayName)}
250
  </span>
251
  <span class="segment-time">
252
- ${formatTime(segment.start)} - ${formatTime(segment.end)}
253
  </span>
254
  </div>
255
- <p class="segment-text">${escapeHtml(segment.text)}</p>
256
  `;
257
 
258
  elements.transcriptContainer.appendChild(segmentEl);
@@ -260,6 +275,7 @@ document.addEventListener('DOMContentLoaded', () => {
260
  }
261
 
262
 
 
263
  function formatTime(seconds) {
264
  const h = Math.floor(seconds / 3600);
265
  const m = Math.floor((seconds % 3600) / 60);
 
206
  // =====================
207
 
208
  function displayResults(result) {
209
+ // ===== Metadata =====
210
+
211
+ // Nếu chỉ dùng ROLE thì không nên hiển thị speaker_count
212
+ if (result.roles) {
213
+ const roleCount = Object.keys(result.roles).length;
214
+ elements.speakerCount.textContent = `${roleCount} role${roleCount !== 1 ? 's' : ''}`;
215
+ } else {
216
+ elements.speakerCount.textContent = '';
217
+ }
218
+
219
+ elements.durationInfo.textContent = formatDuration(result.duration || 0);
220
+ elements.processingTime.textContent = `${result.processing_time || 0}s`;
221
 
222
+ // ===== Download links =====
223
+ if (result.download_txt) {
224
+ elements.downloadTxt.href = result.download_txt;
225
+ elements.downloadTxt.style.display = 'inline-block';
226
+ }
227
 
228
+ if (result.download_csv) {
229
+ elements.downloadCsv.href = result.download_csv;
230
+ elements.downloadCsv.style.display = 'inline-block';
231
+ }
232
 
233
+ // ===== Render transcript =====
234
+ renderTranscript(result.segments || []);
235
 
236
+ // ===== Show results =====
237
  showSection('results');
238
  }
239
 
240
  function renderTranscript(segments) {
241
  elements.transcriptContainer.innerHTML = '';
242
 
243
+ const roleColors = {};
244
  let colorIndex = 0;
245
 
246
  segments.forEach((segment) => {
247
+ const role = segment.role || 'UNKNOWN';
248
 
249
+ if (!(role in roleColors)) {
 
 
 
 
250
  colorIndex++;
251
+ roleColors[role] = `speaker-${Math.min(colorIndex, 5)}`;
252
  }
253
 
254
  const segmentEl = document.createElement('div');
255
+ segmentEl.className = `segment ${roleColors[role]}`;
256
+
257
+ const start = typeof segment.start === 'number' ? segment.start : 0;
258
+ const end = typeof segment.end === 'number' ? segment.end : 0;
259
+ const text = segment.text ? escapeHtml(segment.text) : '';
260
 
261
  segmentEl.innerHTML = `
262
  <div class="segment-header">
263
  <span class="segment-speaker">
264
+ ${escapeHtml(role)}
265
  </span>
266
  <span class="segment-time">
267
+ ${formatTime(start)} - ${formatTime(end)}
268
  </span>
269
  </div>
270
+ <p class="segment-text">${text}</p>
271
  `;
272
 
273
  elements.transcriptContainer.appendChild(segmentEl);
 
275
  }
276
 
277
 
278
+
279
  function formatTime(seconds) {
280
  const h = Math.floor(seconds / 3600);
281
  const m = Math.floor((seconds % 3600) / 60);