Marek4321 commited on
Commit
60d569b
·
verified ·
1 Parent(s): 90f874e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +109 -374
app.py CHANGED
@@ -1,5 +1,10 @@
1
  import streamlit as st
 
 
 
 
2
 
 
3
  st.set_page_config(
4
  page_title="QualiInsightLab",
5
  page_icon="🎙️",
@@ -7,46 +12,11 @@ st.set_page_config(
7
  initial_sidebar_state="expanded"
8
  )
9
 
10
- # Teraz importy i reszta kodu
11
- import sys
12
- import os
13
- from datetime import datetime
14
- import time
15
- import traceback
16
- import threading
17
- import queue
18
-
19
- # Sprawdzenie zależności BEZ używania st.success/st.error przed set_page_config
20
- try:
21
- import openai
22
- OPENAI_AVAILABLE = True
23
- openai_status = "✅ OpenAI library loaded successfully"
24
- except ImportError as e:
25
- OPENAI_AVAILABLE = False
26
- openai_status = f"❌ OpenAI library missing: {e}"
27
-
28
- # Import modułów (bez używania Streamlit commands)
29
- try:
30
- from transcription import AudioTranscriber
31
- from report_generator import ReportGenerator
32
- from file_handler import FileHandler
33
- from config import NVIDIA_THEME, DEFAULT_SETTINGS
34
- modules_loaded = True
35
- except ImportError as e:
36
- modules_loaded = False
37
- import_error = str(e)
38
-
39
- # Teraz możemy użyć komend Streamlit
40
- if OPENAI_AVAILABLE:
41
- st.success(openai_status)
42
- else:
43
- st.error(openai_status)
44
- st.info("Installing dependencies... Refresh page in 30 seconds.")
45
- st.stop()
46
-
47
- if not modules_loaded:
48
- st.error(f"❌ Błąd importu modułów: {import_error}")
49
- st.stop()
50
 
51
  # Custom CSS - kolorystyka NVIDIA
52
  st.markdown(f"""
@@ -95,11 +65,6 @@ class FGIIDIAnalyzer:
95
  self.report_generator = None
96
  self.file_handler = FileHandler()
97
  self.initialize_session_state()
98
-
99
- # Queue dla komunikacji między wątkami
100
- self.status_queue = queue.Queue()
101
- self.processing_thread = None
102
- self.processing_completed = False
103
 
104
  def initialize_session_state(self):
105
  """Inicjalizacja session state"""
@@ -115,106 +80,19 @@ class FGIIDIAnalyzer:
115
  st.session_state.research_brief = ""
116
  if 'logs' not in st.session_state:
117
  st.session_state.logs = []
118
- if 'current_file_progress' not in st.session_state:
119
- st.session_state.current_file_progress = ""
120
- if 'overall_progress' not in st.session_state:
121
- st.session_state.overall_progress = 0
122
 
123
- def log_message(self, message, level="INFO", force_local=False):
124
- """Dodaj wiadomość do logów - thread-safe"""
125
  timestamp = datetime.now().strftime("%H:%M:%S")
126
  log_entry = f"[{timestamp}] {level}: {message}"
 
127
 
128
- try:
129
- # Sprawdź czy jesteśmy w głównym wątku Streamlit
130
- if not force_local and 'logs' in st.session_state:
131
- st.session_state.logs.append(log_entry)
132
-
133
- # Ograniczenie liczby logów do 100
134
- if len(st.session_state.logs) > 100:
135
- st.session_state.logs = st.session_state.logs[-100:]
136
- else:
137
- # Fallback dla wątków - zapisz do lokalnej listy
138
- if not hasattr(self, 'local_logs'):
139
- self.local_logs = []
140
- self.local_logs.append(log_entry)
141
-
142
- # Wyświetl w konsoli jeśli jesteśmy w wątku
143
- print(log_entry)
144
-
145
- except Exception as e:
146
- # Ostateczny fallback
147
- print(f"LOG ERROR: {log_entry}")
148
- print(f"LOG ERROR DETAILS: {e}")
149
-
150
- def update_progress_safe(self, progress_value, status_message):
151
- """Thread-safe aktualizacja postępu"""
152
- try:
153
- st.session_state.overall_progress = progress_value
154
- st.session_state.current_file_progress = status_message
155
- except:
156
- # Fallback - wyświetl w konsoli
157
- print(f"PROGRESS: {progress_value:.1%} - {status_message}")
158
-
159
- def update_transcriptions_safe(self, filename, transcription):
160
- """Thread-safe aktualizacja transkrypcji"""
161
- try:
162
- st.session_state.transcriptions[filename] = transcription
163
- except:
164
- # Fallback - zapisz do lokalnej zmiennej
165
- if not hasattr(self, 'local_transcriptions'):
166
- self.local_transcriptions = {}
167
- self.local_transcriptions[filename] = transcription
168
- print(f"TRANSCRIPTION: {filename} completed locally")
169
-
170
- def sync_local_data(self):
171
- """Synchronizuj dane lokalne z session_state"""
172
- try:
173
- # Przenies logi lokalne do session_state
174
- if hasattr(self, 'local_logs'):
175
- for log in self.local_logs:
176
- if 'logs' in st.session_state:
177
- st.session_state.logs.append(log)
178
- self.local_logs = []
179
-
180
- # Przenies transkrypcje lokalne do session_state
181
- if hasattr(self, 'local_transcriptions'):
182
- for filename, transcription in self.local_transcriptions.items():
183
- st.session_state.transcriptions[filename] = transcription
184
- self.local_transcriptions = {}
185
-
186
- # Przenies raport lokalny do session_state
187
- if hasattr(self, 'local_final_report'):
188
- st.session_state.final_report = self.local_final_report
189
- st.session_state.processing_status = 'completed'
190
- st.session_state.overall_progress = 1.0
191
- st.session_state.current_file_progress = "✅ Zakończono!"
192
- delattr(self, 'local_final_report')
193
-
194
- # Sprawdź flagę zakończenia
195
- if hasattr(self, 'processing_completed') and self.processing_completed:
196
- st.session_state.processing_status = 'completed'
197
- st.session_state.overall_progress = 1.0
198
- st.session_state.current_file_progress = "✅ Zakończono!"
199
- self.processing_completed = False
200
-
201
- except Exception as e:
202
- print(f"SYNC ERROR: {e}")
203
-
204
- def cleanup_session(self):
205
- """Wyczyść sesję i pliki tymczasowe"""
206
- # Zatrzymaj przetwarzanie jeśli trwa
207
- if st.session_state.processing_status == 'running':
208
- st.session_state.processing_status = 'stopped'
209
 
210
- # Wyczyść pliki tymczasowe
211
- self.file_handler.cleanup_temp_files()
212
-
213
- # Wyczyść session state
214
- for key in list(st.session_state.keys()):
215
- del st.session_state[key]
216
-
217
- self.initialize_session_state()
218
 
219
  def render_sidebar(self):
220
  """Renderuj sidebar z konfiguracją"""
@@ -278,7 +156,8 @@ class FGIIDIAnalyzer:
278
 
279
  # Reset session
280
  if st.sidebar.button("🔄 Reset sesji", type="secondary"):
281
- self.cleanup_session()
 
282
  st.rerun()
283
 
284
  return {
@@ -312,7 +191,7 @@ class FGIIDIAnalyzer:
312
  )
313
 
314
  if uploaded_files:
315
- # Walidacja plików z nową metodą
316
  valid_files = []
317
  total_size = 0
318
 
@@ -320,27 +199,23 @@ class FGIIDIAnalyzer:
320
  file_size_mb = file.size / (1024 * 1024)
321
  total_size += file_size_mb
322
 
323
- # Użyj nowej walidacji dla Whisper
324
- is_valid, message = self.file_handler.validate_file_for_whisper(file)
325
-
326
- if not is_valid:
327
- st.error(f"❌ {file.name}: {message}")
328
  continue
329
 
330
- # Pobierz szczegółowe info o pliku
331
- file_info = self.file_handler.get_file_info(file)
332
- file_info['file'] = file # Dodaj obiekt pliku
333
- valid_files.append(file_info)
 
 
 
334
 
335
  # Wyświetl informacje o plikach
336
  if valid_files:
337
  st.success(f"✅ Załadowano {len(valid_files)} plików ({total_size:.1f}MB)")
338
 
339
- # Pokaż plan przetwarzania
340
- if any(not f['whisper_ready'] for f in valid_files):
341
- plan = self.file_handler.create_processing_plan([f['file'] for f in valid_files])
342
- st.info(plan)
343
-
344
  # Tabela z informacjami o plikach
345
  for i, file_info in enumerate(valid_files):
346
  col1, col2, col3 = st.columns([3, 1, 1])
@@ -352,12 +227,10 @@ class FGIIDIAnalyzer:
352
  st.write(f"{file_info['size_mb']:.1f}MB")
353
 
354
  with col3:
355
- if file_info['too_large']:
356
- st.error("Za duży")
357
  elif file_info['needs_splitting']:
358
  st.warning("Podział")
359
- elif file_info['needs_compression']:
360
- st.warning("Kompresja")
361
  else:
362
  st.success("OK")
363
 
@@ -378,51 +251,24 @@ class FGIIDIAnalyzer:
378
 
379
  st.header("🚀 Przetwarzanie")
380
 
381
- # Estymacja czasu i kosztów
382
- if st.session_state.processing_status == 'ready':
383
- total_size = sum(f['size_mb'] for f in st.session_state.uploaded_files)
384
- estimated_time = total_size * 0.5 # ~30s per MB
385
- estimated_cost = total_size * 0.1 # ~$0.10 per MB
386
-
387
- st.info(f"""
388
- 📊 **Estymacja:**
389
- - Całkowity rozmiar: {total_size:.1f}MB
390
- - Szacowany czas: {estimated_time:.1f} minut
391
- - Szacowany koszt: ${estimated_cost:.2f}
392
- """)
393
-
394
- # Przycisk start/stop
395
  if st.session_state.processing_status == 'ready':
396
  if st.button("🎯 Rozpocznij transkrypcję i analizę", type="primary"):
397
  st.session_state.processing_status = 'running'
398
- st.session_state.current_file_progress = ""
399
- st.session_state.overall_progress = 0
400
-
401
- # Przekaż dane do wątku (kopiuj dane z session_state)
402
- files_copy = st.session_state.uploaded_files.copy()
403
- brief_copy = st.session_state.research_brief
404
-
405
- # Uruchom przetwarzanie w osobnym wątku
406
- self.processing_thread = threading.Thread(
407
- target=self.process_files_async,
408
- args=(settings, files_copy, brief_copy)
409
- )
410
- self.processing_thread.start()
411
- st.rerun()
412
 
413
  elif st.session_state.processing_status == 'running':
414
  st.info("⏳ Przetwarzanie w toku...")
415
-
416
- if st.button("⏹️ Zatrzymaj", type="secondary"):
417
- st.session_state.processing_status = 'stopped'
418
- st.warning("Przetwarzanie zostanie zatrzymane...")
419
- st.rerun()
420
 
421
  elif st.session_state.processing_status == 'completed':
422
  st.success("🎉 Przetwarzanie zakończone pomyślnie!")
423
 
424
  if st.button("🔄 Nowe przetwarzanie", type="primary"):
425
  st.session_state.processing_status = 'ready'
 
 
426
  st.rerun()
427
 
428
  elif st.session_state.processing_status == 'error':
@@ -431,79 +277,24 @@ class FGIIDIAnalyzer:
431
  if st.button("🔄 Spróbuj ponownie", type="primary"):
432
  st.session_state.processing_status = 'ready'
433
  st.rerun()
434
-
435
- # Progress display
436
- if st.session_state.processing_status == 'running':
437
- self.render_progress()
438
-
439
- def render_progress(self):
440
- """Renderuj postęp przetwarzania"""
441
- progress_container = st.container()
442
-
443
- # Synchronizuj dane z wątku
444
- self.sync_local_data()
445
-
446
- with progress_container:
447
- # Overall progress bar
448
- st.progress(st.session_state.overall_progress)
449
-
450
- # Current status
451
- if st.session_state.current_file_progress:
452
- st.info(st.session_state.current_file_progress)
453
-
454
- # Files completed
455
- total_files = len(st.session_state.uploaded_files)
456
- completed_files = len(st.session_state.transcriptions)
457
- st.write(f"📊 Ukończono: {completed_files}/{total_files} plików")
458
-
459
- # Auto-refresh podczas przetwarzania
460
- if st.session_state.processing_status == 'running':
461
- # Sprawdź czy wątek się zakończył
462
- if (hasattr(self, 'processing_thread') and
463
- self.processing_thread is not None and
464
- not self.processing_thread.is_alive()):
465
- # Wątek się zakończył - sprawdź czy mamy raport
466
- if hasattr(self, 'local_final_report'):
467
- st.session_state.final_report = self.local_final_report
468
- st.session_state.processing_status = 'completed'
469
- st.session_state.overall_progress = 1.0
470
- st.session_state.current_file_progress = "✅ Zakończono!"
471
- delattr(self, 'local_final_report')
472
- st.rerun()
473
- elif len(st.session_state.transcriptions) > 0:
474
- # Mamy transkrypcje ale brak raportu - możliwy błąd
475
- st.session_state.processing_status = 'error'
476
- st.rerun()
477
-
478
- time.sleep(2)
479
- st.rerun()
480
 
481
- def process_files_async(self, settings, uploaded_files, research_brief):
482
- """Asynchroniczne przetwarzanie plików - thread-safe z przekazanymi danymi"""
483
  try:
484
- self.log_message("🚀 Rozpoczynam przetwarzanie plików", force_local=True)
485
 
486
- total_files = len(uploaded_files)
 
 
487
 
488
- # ETAP 1: Transkrypcja plików
489
- for i, file_info in enumerate(uploaded_files):
490
- # Sprawdź status w bezpieczny sposób
491
- try:
492
- if st.session_state.processing_status != 'running':
493
- self.log_message("⏹️ Przetwarzanie zatrzymane przez użytkownika", force_local=True)
494
- return
495
- except:
496
- # Jeśli nie można sprawdzić statusu, kontynuuj (może być w middle of processing)
497
- pass
498
 
499
  try:
500
- # Update progress
501
- progress = i / (total_files + 1) # +1 for report generation
502
- self.update_progress_safe(progress, f"🎙️ Transkrybuję: {file_info['name']}")
503
-
504
- self.log_message(f"📝 Rozpoczynam transkrypcję: {file_info['name']}", force_local=True)
505
-
506
- # Przetwórz plik (kompresja/podział jeśli potrzeba)
507
  processed_files = self.file_handler.process_file(
508
  file_info['file'],
509
  settings['max_file_size'],
@@ -513,98 +304,56 @@ class FGIIDIAnalyzer:
513
  if not processed_files:
514
  raise Exception("Nie udało się przetworzyć pliku")
515
 
516
- # Walidacja rozmiaru wszystkich części
517
- valid_parts = []
518
- for pf in processed_files:
519
- file_size_mb = os.path.getsize(pf) / (1024 * 1024)
520
- if file_size_mb > 25:
521
- self.log_message(f"⚠️ Część {pf} za duża: {file_size_mb:.1f}MB", "WARNING", force_local=True)
522
- else:
523
- valid_parts.append(pf)
524
-
525
- if not valid_parts:
526
- raise Exception("Wszystkie części za duże dla Whisper API")
527
-
528
- # Transkrypcja z retry logic
529
  transcription = self.transcriber.transcribe_files(
530
- valid_parts,
531
  language=settings['language']
532
  )
533
 
534
- if transcription:
535
- self.update_transcriptions_safe(file_info['name'], transcription)
536
- self.log_message(f"✅ Transkrypcja zakończona: {file_info['name']}", force_local=True)
537
- else:
538
- self.log_message(f"❌ Transkrypcja nieudana: {file_info['name']}", "ERROR", force_local=True)
539
-
 
540
  except Exception as e:
541
- self.log_message(f"❌ Błąd przetwarzania {file_info['name']}: {str(e)}", "ERROR", force_local=True)
 
542
  continue
543
 
544
- # ETAP 2: Generowanie raportu
545
- try:
546
- # Sprawdź czy mamy transkrypcje (lokalnie lub w session_state)
547
- transcriptions_to_use = {}
548
 
549
- # Zbierz transkrypcje z session_state
550
  try:
551
- transcriptions_to_use.update(st.session_state.transcriptions)
552
- except:
553
- pass
554
-
555
- # Dodaj lokalne transkrypcje jeśli są
556
- if hasattr(self, 'local_transcriptions'):
557
- transcriptions_to_use.update(self.local_transcriptions)
558
-
559
- if transcriptions_to_use:
560
- self.update_progress_safe(0.9, "📄 Generuję raport badawczy...")
561
- self.log_message("📄 Rozpoczynam generowanie raportu", force_local=True)
562
-
563
  final_report = self.report_generator.generate_comprehensive_report(
564
- transcriptions_to_use,
565
- research_brief
566
  )
567
 
568
- # Zapisz raport
569
- try:
570
- st.session_state.final_report = final_report
571
- st.session_state.processing_status = 'completed'
572
- st.session_state.overall_progress = 1.0
573
- st.session_state.current_file_progress = "✅ Zakończono!"
574
- except:
575
- # Fallback - zapisz lokalnie i oznacz flagę
576
- self.local_final_report = final_report
577
- self.processing_completed = True
578
- print("REPORT: Generated and saved locally")
579
 
580
- self.update_progress_safe(1.0, "✅ Zakończono!")
581
- self.log_message("🎉 Raport wygenerowany pomyślnie!", force_local=True)
582
 
583
- else:
584
- self.log_message("❌ Brak transkrypcji do wygenerowania raportu", "ERROR", force_local=True)
585
- try:
586
- st.session_state.processing_status = 'error'
587
- except:
588
- print("ERROR: No transcriptions available")
589
-
590
- except Exception as e:
591
- self.log_message(f"❌ Błąd generowania raportu: {str(e)}", "ERROR", force_local=True)
592
- try:
593
  st.session_state.processing_status = 'error'
594
- except:
595
- print(f"REPORT ERROR: {e}")
 
596
 
597
  except Exception as e:
598
- self.log_message(f"💥 Błąd krytyczny: {str(e)}", "ERROR", force_local=True)
599
- try:
600
- st.session_state.processing_status = 'error'
601
- except:
602
- print(f"CRITICAL ERROR: {e}")
603
-
604
- finally:
605
- # Oznacz zakończenie przetwarzania
606
- self.processing_completed = True
607
- print("THREAD: Processing completed - setting flag")
608
 
609
  def render_results(self):
610
  """Renderuj wyniki"""
@@ -663,10 +412,10 @@ class FGIIDIAnalyzer:
663
  st.session_state.logs = []
664
  st.rerun()
665
 
666
- # Display logs
667
- logs_text = "\n".join(st.session_state.logs)
668
  st.text_area(
669
- "Logi systemu:",
670
  value=logs_text,
671
  height=400,
672
  disabled=True
@@ -676,36 +425,26 @@ class FGIIDIAnalyzer:
676
 
677
  def run(self):
678
  """Główna funkcja aplikacji"""
679
- try:
680
- # Sidebar
681
- settings = self.render_sidebar()
682
-
683
- # Main content
684
- st.title("🎙️ QualiInsightLab")
685
- st.markdown("*Automatyczna transkrypcja i analiza wywiadów fokusowych oraz indywidualnych*")
686
- st.markdown("---")
687
-
688
- # File upload section
689
- files_uploaded = self.render_file_upload(settings)
690
-
691
- st.markdown("---")
692
-
693
- # Processing section
694
- self.render_processing_section(settings)
695
-
696
- st.markdown("---")
697
-
698
- # Results section
699
- self.render_results()
700
-
701
- except Exception as e:
702
- st.error(f"💥 Błąd aplikacji: {str(e)}")
703
- st.code(traceback.format_exc())
704
-
705
- # Wyczyść sesję przy błędzie
706
- if st.button("🔄 Reset po błędzie"):
707
- self.cleanup_session()
708
- st.rerun()
709
 
710
  # Główna aplikacja
711
  if __name__ == "__main__":
@@ -713,15 +452,11 @@ if __name__ == "__main__":
713
  app = FGIIDIAnalyzer()
714
  app.run()
715
  except Exception as e:
716
- st.error(f"💥 Błąd krytyczny: {str(e)}")
717
  st.code(traceback.format_exc())
718
 
719
  # Log error for debugging
720
  with open('error_log.txt', 'w', encoding='utf-8') as f:
721
  f.write(f"Error: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
722
 
723
- st.info("Szczegóły błędu zapisane w error_log.txt")
724
-
725
- # Emergency restart
726
- if st.button("🚨 Awaryjny restart"):
727
- st.rerun()
 
1
  import streamlit as st
2
+ import os
3
+ from datetime import datetime
4
+ import time
5
+ import traceback
6
 
7
+ # Konfiguracja strony MUSI być pierwsza
8
  st.set_page_config(
9
  page_title="QualiInsightLab",
10
  page_icon="🎙️",
 
12
  initial_sidebar_state="expanded"
13
  )
14
 
15
+ # Import modułów
16
+ from transcription import AudioTranscriber
17
+ from report_generator import ReportGenerator
18
+ from file_handler import FileHandler
19
+ from config import NVIDIA_THEME, DEFAULT_SETTINGS
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  # Custom CSS - kolorystyka NVIDIA
22
  st.markdown(f"""
 
65
  self.report_generator = None
66
  self.file_handler = FileHandler()
67
  self.initialize_session_state()
 
 
 
 
 
68
 
69
  def initialize_session_state(self):
70
  """Inicjalizacja session state"""
 
80
  st.session_state.research_brief = ""
81
  if 'logs' not in st.session_state:
82
  st.session_state.logs = []
 
 
 
 
83
 
84
+ def log_message(self, message, level="INFO"):
85
+ """Dodaj wiadomość do logów"""
86
  timestamp = datetime.now().strftime("%H:%M:%S")
87
  log_entry = f"[{timestamp}] {level}: {message}"
88
+ st.session_state.logs.append(log_entry)
89
 
90
+ # Ograniczenie liczby logów do 100
91
+ if len(st.session_state.logs) > 100:
92
+ st.session_state.logs = st.session_state.logs[-100:]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
+ # Wyświetl też w konsoli dla debug
95
+ print(log_entry)
 
 
 
 
 
 
96
 
97
  def render_sidebar(self):
98
  """Renderuj sidebar z konfiguracją"""
 
156
 
157
  # Reset session
158
  if st.sidebar.button("🔄 Reset sesji", type="secondary"):
159
+ for key in list(st.session_state.keys()):
160
+ del st.session_state[key]
161
  st.rerun()
162
 
163
  return {
 
191
  )
192
 
193
  if uploaded_files:
194
+ # Walidacja plików
195
  valid_files = []
196
  total_size = 0
197
 
 
199
  file_size_mb = file.size / (1024 * 1024)
200
  total_size += file_size_mb
201
 
202
+ # Sprawdź rozmiar pojedynczego pliku
203
+ if file_size_mb > 100: # 100MB limit dla pojedynczego pliku
204
+ st.error(f"❌ {file.name}: Plik za duży ({file_size_mb:.1f}MB). Maksymalnie 100MB.")
 
 
205
  continue
206
 
207
+ valid_files.append({
208
+ 'file': file,
209
+ 'name': file.name,
210
+ 'size_mb': file_size_mb,
211
+ 'needs_splitting': file_size_mb > settings['max_file_size'],
212
+ 'needs_compression': file_size_mb > 25 # Whisper limit
213
+ })
214
 
215
  # Wyświetl informacje o plikach
216
  if valid_files:
217
  st.success(f"✅ Załadowano {len(valid_files)} plików ({total_size:.1f}MB)")
218
 
 
 
 
 
 
219
  # Tabela z informacjami o plikach
220
  for i, file_info in enumerate(valid_files):
221
  col1, col2, col3 = st.columns([3, 1, 1])
 
227
  st.write(f"{file_info['size_mb']:.1f}MB")
228
 
229
  with col3:
230
+ if file_info['needs_compression']:
231
+ st.warning("Kompresja")
232
  elif file_info['needs_splitting']:
233
  st.warning("Podział")
 
 
234
  else:
235
  st.success("OK")
236
 
 
251
 
252
  st.header("🚀 Przetwarzanie")
253
 
254
+ # Przycisk start
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  if st.session_state.processing_status == 'ready':
256
  if st.button("🎯 Rozpocznij transkrypcję i analizę", type="primary"):
257
  st.session_state.processing_status = 'running'
258
+ # Przetwarzaj synchronicznie - PROSTSZE I DZIAŁA
259
+ self.process_files(settings)
 
 
 
 
 
 
 
 
 
 
 
 
260
 
261
  elif st.session_state.processing_status == 'running':
262
  st.info("⏳ Przetwarzanie w toku...")
263
+ # Podczas przetwarzania nie pokazuj przycisku stop - może powodować problemy
 
 
 
 
264
 
265
  elif st.session_state.processing_status == 'completed':
266
  st.success("🎉 Przetwarzanie zakończone pomyślnie!")
267
 
268
  if st.button("🔄 Nowe przetwarzanie", type="primary"):
269
  st.session_state.processing_status = 'ready'
270
+ st.session_state.transcriptions = {}
271
+ st.session_state.final_report = None
272
  st.rerun()
273
 
274
  elif st.session_state.processing_status == 'error':
 
277
  if st.button("🔄 Spróbuj ponownie", type="primary"):
278
  st.session_state.processing_status = 'ready'
279
  st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
 
281
+ def process_files(self, settings):
282
+ """Główna logika przetwarzania plików - SYNCHRONICZNA"""
283
  try:
284
+ self.log_message("🚀 Rozpoczynam przetwarzanie plików")
285
 
286
+ # Container dla live updates
287
+ status_container = st.empty()
288
+ progress_container = st.empty()
289
 
290
+ # 1. Transkrypcja wszystkich plików
291
+ for i, file_info in enumerate(st.session_state.uploaded_files):
292
+
293
+ status_container.info(f"🎙️ Transkrybuję: {file_info['name']} ({i+1}/{len(st.session_state.uploaded_files)})")
294
+ self.log_message(f"📝 Rozpoczynam transkrypcję: {file_info['name']}")
 
 
 
 
 
295
 
296
  try:
297
+ # Przetwórz plik (podział jeśli potrzeba)
 
 
 
 
 
 
298
  processed_files = self.file_handler.process_file(
299
  file_info['file'],
300
  settings['max_file_size'],
 
304
  if not processed_files:
305
  raise Exception("Nie udało się przetworzyć pliku")
306
 
307
+ # Transkrypcja
 
 
 
 
 
 
 
 
 
 
 
 
308
  transcription = self.transcriber.transcribe_files(
309
+ processed_files,
310
  language=settings['language']
311
  )
312
 
313
+ st.session_state.transcriptions[file_info['name']] = transcription
314
+ self.log_message(f"✅ Zakończono transkrypcję: {file_info['name']}")
315
+
316
+ # Update progress
317
+ progress = (i + 1) / len(st.session_state.uploaded_files)
318
+ progress_container.progress(progress)
319
+
320
  except Exception as e:
321
+ self.log_message(f"❌ Błąd transkrypcji {file_info['name']}: {str(e)}", "ERROR")
322
+ st.error(f"Błąd przy {file_info['name']}: {str(e)}")
323
  continue
324
 
325
+ # 2. Generowanie raportu
326
+ if st.session_state.transcriptions:
327
+ status_container.info("📄 Generuję raport badawczy...")
328
+ self.log_message("📄 Rozpoczynam generowanie raportu")
329
 
 
330
  try:
 
 
 
 
 
 
 
 
 
 
 
 
331
  final_report = self.report_generator.generate_comprehensive_report(
332
+ st.session_state.transcriptions,
333
+ st.session_state.research_brief
334
  )
335
 
336
+ st.session_state.final_report = final_report
337
+ st.session_state.processing_status = 'completed'
 
 
 
 
 
 
 
 
 
338
 
339
+ status_container.success("✅ Przetwarzanie zakończone!")
340
+ self.log_message("🎉 Raport wygenerowany pomyślnie!")
341
 
342
+ # Automatyczny refresh żeby pokazać wyniki
343
+ st.rerun()
344
+
345
+ except Exception as e:
346
+ self.log_message(f"❌ Błąd generowania raportu: {str(e)}", "ERROR")
347
+ st.error(f"Błąd generowania raportu: {str(e)}")
 
 
 
 
348
  st.session_state.processing_status = 'error'
349
+ else:
350
+ st.session_state.processing_status = 'error'
351
+ self.log_message("❌ Brak transkrypcji do wygenerowania raportu", "ERROR")
352
 
353
  except Exception as e:
354
+ self.log_message(f"💥 Błąd krytyczny: {str(e)}", "ERROR")
355
+ st.error(f"Błąd krytyczny: {str(e)}")
356
+ st.session_state.processing_status = 'error'
 
 
 
 
 
 
 
357
 
358
  def render_results(self):
359
  """Renderuj wyniki"""
 
412
  st.session_state.logs = []
413
  st.rerun()
414
 
415
+ # Display logs - pokazuj najnowsze na górze
416
+ logs_text = "\n".join(reversed(st.session_state.logs))
417
  st.text_area(
418
+ "Logi systemu (najnowsze na górze):",
419
  value=logs_text,
420
  height=400,
421
  disabled=True
 
425
 
426
  def run(self):
427
  """Główna funkcja aplikacji"""
428
+ # Sidebar
429
+ settings = self.render_sidebar()
430
+
431
+ # Main content
432
+ st.title("🎙️ QualiInsightLab")
433
+ st.markdown("*Automatyczna transkrypcja i analiza wywiadów fokusowych oraz indywidualnych*")
434
+ st.markdown("---")
435
+
436
+ # File upload section
437
+ files_uploaded = self.render_file_upload(settings)
438
+
439
+ st.markdown("---")
440
+
441
+ # Processing section
442
+ self.render_processing_section(settings)
443
+
444
+ st.markdown("---")
445
+
446
+ # Results section
447
+ self.render_results()
 
 
 
 
 
 
 
 
 
 
448
 
449
  # Główna aplikacja
450
  if __name__ == "__main__":
 
452
  app = FGIIDIAnalyzer()
453
  app.run()
454
  except Exception as e:
455
+ st.error(f"💥 Błąd aplikacji: {str(e)}")
456
  st.code(traceback.format_exc())
457
 
458
  # Log error for debugging
459
  with open('error_log.txt', 'w', encoding='utf-8') as f:
460
  f.write(f"Error: {str(e)}\n\nTraceback:\n{traceback.format_exc()}")
461
 
462
+ st.info("Szczegóły błędu zapisane w error_log.txt")