Marek4321 commited on
Commit
ae0710d
·
verified ·
1 Parent(s): e34d8da

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +180 -78
app.py CHANGED
@@ -4,6 +4,8 @@ import os
4
  from datetime import datetime
5
  import time
6
  import traceback
 
 
7
 
8
  # Sprawdzenie zależności
9
  try:
@@ -75,6 +77,10 @@ class FGIIDIAnalyzer:
75
  self.report_generator = None
76
  self.file_handler = FileHandler()
77
  self.initialize_session_state()
 
 
 
 
78
 
79
  def initialize_session_state(self):
80
  """Inicjalizacja session state"""
@@ -90,6 +96,10 @@ class FGIIDIAnalyzer:
90
  st.session_state.research_brief = ""
91
  if 'logs' not in st.session_state:
92
  st.session_state.logs = []
 
 
 
 
93
 
94
  def log_message(self, message, level="INFO"):
95
  """Dodaj wiadomość do logów"""
@@ -129,15 +139,15 @@ class FGIIDIAnalyzer:
129
 
130
  max_file_size = st.sidebar.selectbox(
131
  "Maksymalny rozmiar części:",
132
- [15, 20, 25, 30],
133
  index=1,
134
- help="MB - większe pliki będą dzielone na części"
135
  )
136
 
137
  auto_compress = st.sidebar.checkbox(
138
  "Auto-kompresja dużych plików",
139
  value=True,
140
- help="Automatyczna kompresja plików >50MB"
141
  )
142
 
143
  language = st.sidebar.selectbox(
@@ -163,8 +173,7 @@ class FGIIDIAnalyzer:
163
 
164
  # Reset session
165
  if st.sidebar.button("🔄 Reset sesji", type="secondary"):
166
- for key in list(st.session_state.keys()):
167
- del st.session_state[key]
168
  st.rerun()
169
 
170
  return {
@@ -174,6 +183,21 @@ class FGIIDIAnalyzer:
174
  'language': language
175
  }
176
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  def render_file_upload(self, settings):
178
  """Renderuj sekcję upload plików"""
179
  st.header("📁 Upload plików audio/video")
@@ -194,7 +218,7 @@ class FGIIDIAnalyzer:
194
  "Wybierz pliki audio/video:",
195
  type=['mp3', 'wav', 'mp4', 'm4a', 'aac'],
196
  accept_multiple_files=True,
197
- help="Obsługiwane formaty: MP3, WAV, MP4, M4A, AAC"
198
  )
199
 
200
  if uploaded_files:
@@ -207,15 +231,16 @@ class FGIIDIAnalyzer:
207
  total_size += file_size_mb
208
 
209
  # Sprawdź rozmiar pojedynczego pliku
210
- if file_size_mb > 200: # 200MB limit dla pojedynczego pliku
211
- st.error(f"❌ {file.name}: Plik za duży ({file_size_mb:.1f}MB). Maksymalnie 200MB.")
212
  continue
213
 
214
  valid_files.append({
215
  'file': file,
216
  'name': file.name,
217
  'size_mb': file_size_mb,
218
- 'needs_splitting': file_size_mb > settings['max_file_size']
 
219
  })
220
 
221
  # Wyświetl informacje o plikach
@@ -233,7 +258,9 @@ class FGIIDIAnalyzer:
233
  st.write(f"{file_info['size_mb']:.1f}MB")
234
 
235
  with col3:
236
- if file_info['needs_splitting']:
 
 
237
  st.warning("Będzie podzielony")
238
  else:
239
  st.success("OK")
@@ -255,18 +282,55 @@ class FGIIDIAnalyzer:
255
 
256
  st.header("🚀 Przetwarzanie")
257
 
258
- # Przycisk start
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  if st.session_state.processing_status == 'ready':
260
  if st.button("🎯 Rozpocznij transkrypcję i analizę", type="primary"):
261
  st.session_state.processing_status = 'running'
262
- self.process_files(settings)
 
 
 
 
 
 
 
 
 
263
 
264
  elif st.session_state.processing_status == 'running':
265
  st.info("⏳ Przetwarzanie w toku...")
266
 
267
  if st.button("⏹️ Zatrzymaj", type="secondary"):
268
  st.session_state.processing_status = 'stopped'
269
- st.warning("Przetwarzanie zatrzymane")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
 
271
  # Progress display
272
  if st.session_state.processing_status == 'running':
@@ -277,65 +341,84 @@ class FGIIDIAnalyzer:
277
  progress_container = st.container()
278
 
279
  with progress_container:
280
- # Overall progress
 
 
 
 
 
 
 
281
  total_files = len(st.session_state.uploaded_files)
282
  completed_files = len(st.session_state.transcriptions)
 
283
 
284
- progress = completed_files / total_files if total_files > 0 else 0
285
- st.progress(progress)
286
- st.write(f"📊 Postęp ogólny: {completed_files}/{total_files} plików")
287
-
288
- # Current file info
289
- if completed_files < total_files:
290
- current_file = st.session_state.uploaded_files[completed_files]['name']
291
- st.write(f"🔄 Aktualnie: {current_file}")
292
 
293
- def process_files(self, settings):
294
- """Główna logika przetwarzania plików"""
295
  try:
296
- self.log_message("Rozpoczynam przetwarzanie plików")
297
 
298
- # Container dla live updates
299
- status_container = st.empty()
300
- progress_container = st.empty()
301
 
302
- # 1. Transkrypcja wszystkich plików
303
  for i, file_info in enumerate(st.session_state.uploaded_files):
304
  if st.session_state.processing_status != 'running':
 
305
  break
306
 
307
- status_container.info(f"🎙️ Transkrybuję: {file_info['name']}")
308
- self.log_message(f"Rozpoczynam transkrypcję: {file_info['name']}")
309
-
310
  try:
311
- # Przetwórz plik (podział jeśli potrzeba)
 
 
 
 
 
 
 
312
  processed_files = self.file_handler.process_file(
313
  file_info['file'],
314
  settings['max_file_size'],
315
  settings['auto_compress']
316
  )
317
 
318
- # Transkrypcja
319
- transcription = self.transcriber.transcribe_files(
320
- processed_files,
321
- language=settings['language']
322
- )
323
 
324
- st.session_state.transcriptions[file_info['name']] = transcription
325
- self.log_message(f"✅ Zakończono transkrypcję: {file_info['name']}")
 
 
 
 
326
 
327
- # Update progress
328
- progress = (i + 1) / len(st.session_state.uploaded_files)
329
- progress_container.progress(progress)
 
 
 
330
 
 
 
 
 
 
 
331
  except Exception as e:
332
- self.log_message(f"❌ Błąd transkrypcji {file_info['name']}: {str(e)}", "ERROR")
333
- st.error(f"Błąd przy {file_info['name']}: {str(e)}")
334
 
335
- # 2. Generowanie raportu
336
  if st.session_state.transcriptions and st.session_state.processing_status == 'running':
337
- status_container.info("📄 Generuję raport badawczy...")
338
- self.log_message("Rozpoczynam generowanie raportu")
 
 
339
 
340
  try:
341
  final_report = self.report_generator.generate_comprehensive_report(
@@ -345,18 +428,22 @@ class FGIIDIAnalyzer:
345
 
346
  st.session_state.final_report = final_report
347
  st.session_state.processing_status = 'completed'
 
 
348
 
349
- status_container.success(" Przetwarzanie zakończone!")
350
- self.log_message("✅ Raport wygenerowany pomyślnie")
351
 
352
  except Exception as e:
353
  self.log_message(f"❌ Błąd generowania raportu: {str(e)}", "ERROR")
354
- st.error(f"Błąd generowania raportu: {str(e)}")
355
  st.session_state.processing_status = 'error'
356
 
 
 
 
 
 
357
  except Exception as e:
358
  self.log_message(f"💥 Błąd krytyczny: {str(e)}", "ERROR")
359
- st.error(f"Błąd krytyczny: {str(e)}")
360
  st.session_state.processing_status = 'error'
361
 
362
  def render_results(self):
@@ -411,14 +498,15 @@ class FGIIDIAnalyzer:
411
  st.subheader("📋 Logi procesu")
412
 
413
  if st.session_state.logs:
414
- # Scroll to bottom option
415
- if st.button("⬇️ Przewiń na dół"):
416
- pass # Auto-scroll jest w CSS
 
417
 
418
  # Display logs
419
  logs_text = "\n".join(st.session_state.logs)
420
  st.text_area(
421
- "Logi:",
422
  value=logs_text,
423
  height=400,
424
  disabled=True
@@ -428,26 +516,36 @@ class FGIIDIAnalyzer:
428
 
429
  def run(self):
430
  """Główna funkcja aplikacji"""
431
- # Sidebar
432
- settings = self.render_sidebar()
433
-
434
- # Main content
435
- st.title("🎙️ FGI/IDI Research Analyzer")
436
- st.markdown("*Automatyczna transkrypcja i analiza wywiadów fokusowych oraz indywidualnych*")
437
- st.markdown("---")
438
-
439
- # File upload section
440
- files_uploaded = self.render_file_upload(settings)
441
-
442
- st.markdown("---")
443
-
444
- # Processing section
445
- self.render_processing_section(settings)
446
-
447
- st.markdown("---")
448
-
449
- # Results section
450
- self.render_results()
 
 
 
 
 
 
 
 
 
 
451
 
452
  # Główna aplikacja
453
  if __name__ == "__main__":
@@ -455,11 +553,15 @@ if __name__ == "__main__":
455
  app = FGIIDIAnalyzer()
456
  app.run()
457
  except Exception as e:
458
- st.error(f"💥 Błąd aplikacji: {str(e)}")
459
  st.code(traceback.format_exc())
460
 
461
  # Log error for debugging
462
  with open('error_log.txt', 'w', encoding='utf-8') as f:
463
  f.write(f"Error: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
464
 
465
- st.info("Szczegóły błędu zapisane w error_log.txt")
 
 
 
 
 
4
  from datetime import datetime
5
  import time
6
  import traceback
7
+ import threading
8
+ import queue
9
 
10
  # Sprawdzenie zależności
11
  try:
 
77
  self.report_generator = None
78
  self.file_handler = FileHandler()
79
  self.initialize_session_state()
80
+
81
+ # Queue dla komunikacji między wątkami
82
+ self.status_queue = queue.Queue()
83
+ self.processing_thread = None
84
 
85
  def initialize_session_state(self):
86
  """Inicjalizacja session state"""
 
96
  st.session_state.research_brief = ""
97
  if 'logs' not in st.session_state:
98
  st.session_state.logs = []
99
+ if 'current_file_progress' not in st.session_state:
100
+ st.session_state.current_file_progress = ""
101
+ if 'overall_progress' not in st.session_state:
102
+ st.session_state.overall_progress = 0
103
 
104
  def log_message(self, message, level="INFO"):
105
  """Dodaj wiadomość do logów"""
 
139
 
140
  max_file_size = st.sidebar.selectbox(
141
  "Maksymalny rozmiar części:",
142
+ [10, 15, 20, 25],
143
  index=1,
144
+ help="MB - większe pliki będą dzielone na części (max 25MB dla Whisper)"
145
  )
146
 
147
  auto_compress = st.sidebar.checkbox(
148
  "Auto-kompresja dużych plików",
149
  value=True,
150
+ help="Automatyczna kompresja plików >25MB"
151
  )
152
 
153
  language = st.sidebar.selectbox(
 
173
 
174
  # Reset session
175
  if st.sidebar.button("🔄 Reset sesji", type="secondary"):
176
+ self.cleanup_session()
 
177
  st.rerun()
178
 
179
  return {
 
183
  'language': language
184
  }
185
 
186
+ def cleanup_session(self):
187
+ """Wyczyść sesję i pliki tymczasowe"""
188
+ # Zatrzymaj przetwarzanie jeśli trwa
189
+ if st.session_state.processing_status == 'running':
190
+ st.session_state.processing_status = 'stopped'
191
+
192
+ # Wyczyść pliki tymczasowe
193
+ self.file_handler.cleanup_temp_files()
194
+
195
+ # Wyczyść session state
196
+ for key in list(st.session_state.keys()):
197
+ del st.session_state[key]
198
+
199
+ self.initialize_session_state()
200
+
201
  def render_file_upload(self, settings):
202
  """Renderuj sekcję upload plików"""
203
  st.header("📁 Upload plików audio/video")
 
218
  "Wybierz pliki audio/video:",
219
  type=['mp3', 'wav', 'mp4', 'm4a', 'aac'],
220
  accept_multiple_files=True,
221
+ help="Obsługiwane formaty: MP3, WAV, MP4, M4A, AAC (max 25MB każdy dla Whisper)"
222
  )
223
 
224
  if uploaded_files:
 
231
  total_size += file_size_mb
232
 
233
  # Sprawdź rozmiar pojedynczego pliku
234
+ if file_size_mb > 100: # 100MB limit dla pojedynczego pliku
235
+ st.error(f"❌ {file.name}: Plik za duży ({file_size_mb:.1f}MB). Maksymalnie 100MB.")
236
  continue
237
 
238
  valid_files.append({
239
  'file': file,
240
  'name': file.name,
241
  'size_mb': file_size_mb,
242
+ 'needs_splitting': file_size_mb > settings['max_file_size'],
243
+ 'needs_compression': file_size_mb > 25 # Whisper limit
244
  })
245
 
246
  # Wyświetl informacje o plikach
 
258
  st.write(f"{file_info['size_mb']:.1f}MB")
259
 
260
  with col3:
261
+ if file_info['needs_compression']:
262
+ st.error("Wymaga kompresji")
263
+ elif file_info['needs_splitting']:
264
  st.warning("Będzie podzielony")
265
  else:
266
  st.success("OK")
 
282
 
283
  st.header("🚀 Przetwarzanie")
284
 
285
+ # Estymacja czasu i kosztów
286
+ if st.session_state.processing_status == 'ready':
287
+ total_size = sum(f['size_mb'] for f in st.session_state.uploaded_files)
288
+ estimated_time = total_size * 0.5 # ~30s per MB
289
+ estimated_cost = total_size * 0.1 # ~$0.10 per MB
290
+
291
+ st.info(f"""
292
+ 📊 **Estymacja:**
293
+ - Całkowity rozmiar: {total_size:.1f}MB
294
+ - Szacowany czas: {estimated_time:.1f} minut
295
+ - Szacowany koszt: ${estimated_cost:.2f}
296
+ """)
297
+
298
+ # Przycisk start/stop
299
  if st.session_state.processing_status == 'ready':
300
  if st.button("🎯 Rozpocznij transkrypcję i analizę", type="primary"):
301
  st.session_state.processing_status = 'running'
302
+ st.session_state.current_file_progress = ""
303
+ st.session_state.overall_progress = 0
304
+
305
+ # Uruchom przetwarzanie w osobnym wątku
306
+ self.processing_thread = threading.Thread(
307
+ target=self.process_files_async,
308
+ args=(settings,)
309
+ )
310
+ self.processing_thread.start()
311
+ st.rerun()
312
 
313
  elif st.session_state.processing_status == 'running':
314
  st.info("⏳ Przetwarzanie w toku...")
315
 
316
  if st.button("⏹️ Zatrzymaj", type="secondary"):
317
  st.session_state.processing_status = 'stopped'
318
+ st.warning("Przetwarzanie zostanie zatrzymane...")
319
+ st.rerun()
320
+
321
+ elif st.session_state.processing_status == 'completed':
322
+ st.success("🎉 Przetwarzanie zakończone pomyślnie!")
323
+
324
+ if st.button("🔄 Nowe przetwarzanie", type="primary"):
325
+ st.session_state.processing_status = 'ready'
326
+ st.rerun()
327
+
328
+ elif st.session_state.processing_status == 'error':
329
+ st.error("❌ Przetwarzanie zakończone błędem")
330
+
331
+ if st.button("🔄 Spróbuj ponownie", type="primary"):
332
+ st.session_state.processing_status = 'ready'
333
+ st.rerun()
334
 
335
  # Progress display
336
  if st.session_state.processing_status == 'running':
 
341
  progress_container = st.container()
342
 
343
  with progress_container:
344
+ # Overall progress bar
345
+ st.progress(st.session_state.overall_progress)
346
+
347
+ # Current status
348
+ if st.session_state.current_file_progress:
349
+ st.info(st.session_state.current_file_progress)
350
+
351
+ # Files completed
352
  total_files = len(st.session_state.uploaded_files)
353
  completed_files = len(st.session_state.transcriptions)
354
+ st.write(f"📊 Ukończono: {completed_files}/{total_files} plików")
355
 
356
+ # Auto-refresh podczas przetwarzania
357
+ if st.session_state.processing_status == 'running':
358
+ time.sleep(2)
359
+ st.rerun()
 
 
 
 
360
 
361
+ def process_files_async(self, settings):
362
+ """Asynchroniczne przetwarzanie plików"""
363
  try:
364
+ self.log_message("🚀 Rozpoczynam przetwarzanie plików")
365
 
366
+ total_files = len(st.session_state.uploaded_files)
 
 
367
 
368
+ # ETAP 1: Transkrypcja plików
369
  for i, file_info in enumerate(st.session_state.uploaded_files):
370
  if st.session_state.processing_status != 'running':
371
+ self.log_message("⏹️ Przetwarzanie zatrzymane przez użytkownika")
372
  break
373
 
 
 
 
374
  try:
375
+ # Update progress
376
+ progress = i / (total_files + 1) # +1 for report generation
377
+ st.session_state.overall_progress = progress
378
+ st.session_state.current_file_progress = f"🎙️ Transkrybuję: {file_info['name']}"
379
+
380
+ self.log_message(f"📝 Rozpoczynam transkrypcję: {file_info['name']}")
381
+
382
+ # Przetwórz plik (kompresja/podział jeśli potrzeba)
383
  processed_files = self.file_handler.process_file(
384
  file_info['file'],
385
  settings['max_file_size'],
386
  settings['auto_compress']
387
  )
388
 
389
+ if not processed_files:
390
+ raise Exception("Nie udało się przetworzyć pliku")
 
 
 
391
 
392
+ # Sprawdź rozmiary przed wysłaniem do Whisper
393
+ for pf in processed_files:
394
+ file_size_mb = os.path.getsize(pf) / (1024 * 1024)
395
+ if file_size_mb > 25:
396
+ self.log_message(f"❌ Część {pf} za duża dla Whisper: {file_size_mb:.1f}MB", "ERROR")
397
+ continue
398
 
399
+ # Transkrypcja z retry logic
400
+ transcription = self.transcriber.transcribe_with_retries(
401
+ processed_files[0] if len(processed_files) == 1 else processed_files,
402
+ language=settings['language'],
403
+ max_retries=3
404
+ )
405
 
406
+ if transcription:
407
+ st.session_state.transcriptions[file_info['name']] = transcription
408
+ self.log_message(f"✅ Transkrypcja zakończona: {file_info['name']}")
409
+ else:
410
+ self.log_message(f"❌ Transkrypcja nieudana: {file_info['name']}", "ERROR")
411
+
412
  except Exception as e:
413
+ self.log_message(f"❌ Błąd przetwarzania {file_info['name']}: {str(e)}", "ERROR")
414
+ continue
415
 
416
+ # ETAP 2: Generowanie raportu
417
  if st.session_state.transcriptions and st.session_state.processing_status == 'running':
418
+ st.session_state.current_file_progress = "📄 Generuję raport badawczy..."
419
+ st.session_state.overall_progress = 0.9
420
+
421
+ self.log_message("📄 Rozpoczynam generowanie raportu")
422
 
423
  try:
424
  final_report = self.report_generator.generate_comprehensive_report(
 
428
 
429
  st.session_state.final_report = final_report
430
  st.session_state.processing_status = 'completed'
431
+ st.session_state.overall_progress = 1.0
432
+ st.session_state.current_file_progress = "✅ Zakończono!"
433
 
434
+ self.log_message("🎉 Raport wygenerowany pomyślnie!")
 
435
 
436
  except Exception as e:
437
  self.log_message(f"❌ Błąd generowania raportu: {str(e)}", "ERROR")
 
438
  st.session_state.processing_status = 'error'
439
 
440
+ else:
441
+ if not st.session_state.transcriptions:
442
+ self.log_message("❌ Brak transkrypcji do wygenerowania raportu", "ERROR")
443
+ st.session_state.processing_status = 'error'
444
+
445
  except Exception as e:
446
  self.log_message(f"💥 Błąd krytyczny: {str(e)}", "ERROR")
 
447
  st.session_state.processing_status = 'error'
448
 
449
  def render_results(self):
 
498
  st.subheader("📋 Logi procesu")
499
 
500
  if st.session_state.logs:
501
+ # Clear logs button
502
+ if st.button("🧹 Wyczyść logi"):
503
+ st.session_state.logs = []
504
+ st.rerun()
505
 
506
  # Display logs
507
  logs_text = "\n".join(st.session_state.logs)
508
  st.text_area(
509
+ "Logi systemu:",
510
  value=logs_text,
511
  height=400,
512
  disabled=True
 
516
 
517
  def run(self):
518
  """Główna funkcja aplikacji"""
519
+ try:
520
+ # Sidebar
521
+ settings = self.render_sidebar()
522
+
523
+ # Main content
524
+ st.title("🎙️ FGI/IDI Research Analyzer")
525
+ st.markdown("*Automatyczna transkrypcja i analiza wywiadów fokusowych oraz indywidualnych*")
526
+ st.markdown("---")
527
+
528
+ # File upload section
529
+ files_uploaded = self.render_file_upload(settings)
530
+
531
+ st.markdown("---")
532
+
533
+ # Processing section
534
+ self.render_processing_section(settings)
535
+
536
+ st.markdown("---")
537
+
538
+ # Results section
539
+ self.render_results()
540
+
541
+ except Exception as e:
542
+ st.error(f"💥 Błąd aplikacji: {str(e)}")
543
+ st.code(traceback.format_exc())
544
+
545
+ # Wyczyść sesję przy błędzie
546
+ if st.button("🔄 Reset po błędzie"):
547
+ self.cleanup_session()
548
+ st.rerun()
549
 
550
  # Główna aplikacja
551
  if __name__ == "__main__":
 
553
  app = FGIIDIAnalyzer()
554
  app.run()
555
  except Exception as e:
556
+ st.error(f"💥 Błąd krytyczny: {str(e)}")
557
  st.code(traceback.format_exc())
558
 
559
  # Log error for debugging
560
  with open('error_log.txt', 'w', encoding='utf-8') as f:
561
  f.write(f"Error: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
562
 
563
+ st.info("Szczegóły błędu zapisane w error_log.txt")
564
+
565
+ # Emergency restart
566
+ if st.button("🚨 Awaryjny restart"):
567
+ st.experimental_rerun()