Update app.py
Browse files
app.py
CHANGED
|
@@ -24,7 +24,7 @@ from config import NVIDIA_THEME, DEFAULT_SETTINGS
|
|
| 24 |
|
| 25 |
# Konfiguracja strony
|
| 26 |
st.set_page_config(
|
| 27 |
-
page_title="QualiInsightLab
|
| 28 |
page_icon="🎙️",
|
| 29 |
layout="wide",
|
| 30 |
initial_sidebar_state="expanded"
|
|
@@ -101,19 +101,36 @@ class FGIIDIAnalyzer:
|
|
| 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"""
|
| 106 |
timestamp = datetime.now().strftime("%H:%M:%S")
|
| 107 |
log_entry = f"[{timestamp}] {level}: {message}"
|
| 108 |
-
st.session_state.logs.append(log_entry)
|
| 109 |
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
|
| 114 |
def render_sidebar(self):
|
| 115 |
"""Renderuj sidebar z konfiguracją"""
|
| 116 |
-
st.sidebar.title("🎙️
|
| 117 |
st.sidebar.markdown("---")
|
| 118 |
|
| 119 |
# API Keys
|
|
@@ -183,7 +200,43 @@ class FGIIDIAnalyzer:
|
|
| 183 |
'language': language
|
| 184 |
}
|
| 185 |
|
| 186 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
"""Wyczyść sesję i pliki tymczasowe"""
|
| 188 |
# Zatrzymaj przetwarzanie jeśli trwa
|
| 189 |
if st.session_state.processing_status == 'running':
|
|
@@ -346,6 +399,9 @@ class FGIIDIAnalyzer:
|
|
| 346 |
"""Renderuj postęp przetwarzania"""
|
| 347 |
progress_container = st.container()
|
| 348 |
|
|
|
|
|
|
|
|
|
|
| 349 |
with progress_container:
|
| 350 |
# Overall progress bar
|
| 351 |
st.progress(st.session_state.overall_progress)
|
|
@@ -365,25 +421,30 @@ class FGIIDIAnalyzer:
|
|
| 365 |
st.rerun()
|
| 366 |
|
| 367 |
def process_files_async(self, settings):
|
| 368 |
-
"""Asynchroniczne przetwarzanie plików"""
|
| 369 |
try:
|
| 370 |
-
self.log_message("🚀 Rozpoczynam przetwarzanie plików")
|
| 371 |
|
| 372 |
total_files = len(st.session_state.uploaded_files)
|
| 373 |
|
| 374 |
# ETAP 1: Transkrypcja plików
|
| 375 |
for i, file_info in enumerate(st.session_state.uploaded_files):
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 379 |
|
| 380 |
try:
|
| 381 |
# Update progress
|
| 382 |
progress = i / (total_files + 1) # +1 for report generation
|
| 383 |
-
|
| 384 |
-
st.session_state.current_file_progress = f"🎙️ Transkrybuję: {file_info['name']}"
|
| 385 |
|
| 386 |
-
self.log_message(f"📝 Rozpoczynam transkrypcję: {file_info['name']}")
|
| 387 |
|
| 388 |
# Przetwórz plik (kompresja/podział jeśli potrzeba)
|
| 389 |
processed_files = self.file_handler.process_file(
|
|
@@ -400,7 +461,7 @@ class FGIIDIAnalyzer:
|
|
| 400 |
for pf in processed_files:
|
| 401 |
file_size_mb = os.path.getsize(pf) / (1024 * 1024)
|
| 402 |
if file_size_mb > 25:
|
| 403 |
-
self.log_message(f"⚠️ Część {pf} za duża: {file_size_mb:.1f}MB", "WARNING")
|
| 404 |
else:
|
| 405 |
valid_parts.append(pf)
|
| 406 |
|
|
@@ -414,47 +475,102 @@ class FGIIDIAnalyzer:
|
|
| 414 |
)
|
| 415 |
|
| 416 |
if transcription:
|
| 417 |
-
|
| 418 |
-
self.log_message(f"✅ Transkrypcja zakończona: {file_info['name']}")
|
| 419 |
else:
|
| 420 |
-
self.log_message(f"❌ Transkrypcja nieudana: {file_info['name']}", "ERROR")
|
| 421 |
|
| 422 |
except Exception as e:
|
| 423 |
-
self.log_message(f"❌ Błąd przetwarzania {file_info['name']}: {str(e)}", "ERROR")
|
| 424 |
continue
|
| 425 |
|
| 426 |
# ETAP 2: Generowanie raportu
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
self.log_message("📄 Rozpoczynam generowanie raportu")
|
| 432 |
|
|
|
|
| 433 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 434 |
final_report = self.report_generator.generate_comprehensive_report(
|
| 435 |
-
|
| 436 |
-
|
| 437 |
)
|
| 438 |
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 443 |
|
| 444 |
-
self.
|
|
|
|
| 445 |
|
| 446 |
-
|
| 447 |
-
self.log_message(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 448 |
st.session_state.processing_status = 'error'
|
|
|
|
|
|
|
| 449 |
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
|
| 453 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 454 |
|
| 455 |
except Exception as e:
|
| 456 |
-
|
| 457 |
-
st.session_state.processing_status = 'error'
|
| 458 |
|
| 459 |
def render_results(self):
|
| 460 |
"""Renderuj wyniki"""
|
|
@@ -531,7 +647,7 @@ class FGIIDIAnalyzer:
|
|
| 531 |
settings = self.render_sidebar()
|
| 532 |
|
| 533 |
# Main content
|
| 534 |
-
st.title("🎙️
|
| 535 |
st.markdown("*Automatyczna transkrypcja i analiza wywiadów fokusowych oraz indywidualnych*")
|
| 536 |
st.markdown("---")
|
| 537 |
|
|
|
|
| 24 |
|
| 25 |
# Konfiguracja strony
|
| 26 |
st.set_page_config(
|
| 27 |
+
page_title="QualiInsightLab",
|
| 28 |
page_icon="🎙️",
|
| 29 |
layout="wide",
|
| 30 |
initial_sidebar_state="expanded"
|
|
|
|
| 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", force_local=False):
|
| 105 |
+
"""Dodaj wiadomość do logów - thread-safe"""
|
| 106 |
timestamp = datetime.now().strftime("%H:%M:%S")
|
| 107 |
log_entry = f"[{timestamp}] {level}: {message}"
|
|
|
|
| 108 |
|
| 109 |
+
try:
|
| 110 |
+
# Sprawdź czy jesteśmy w głównym wątku Streamlit
|
| 111 |
+
if not force_local and 'logs' in st.session_state:
|
| 112 |
+
st.session_state.logs.append(log_entry)
|
| 113 |
+
|
| 114 |
+
# Ograniczenie liczby logów do 100
|
| 115 |
+
if len(st.session_state.logs) > 100:
|
| 116 |
+
st.session_state.logs = st.session_state.logs[-100:]
|
| 117 |
+
else:
|
| 118 |
+
# Fallback dla wątków - zapisz do lokalnej listy
|
| 119 |
+
if not hasattr(self, 'local_logs'):
|
| 120 |
+
self.local_logs = []
|
| 121 |
+
self.local_logs.append(log_entry)
|
| 122 |
+
|
| 123 |
+
# Wyświetl w konsoli jeśli jesteśmy w wątku
|
| 124 |
+
print(log_entry)
|
| 125 |
+
|
| 126 |
+
except Exception as e:
|
| 127 |
+
# Ostateczny fallback
|
| 128 |
+
print(f"LOG ERROR: {log_entry}")
|
| 129 |
+
print(f"LOG ERROR DETAILS: {e}")
|
| 130 |
|
| 131 |
def render_sidebar(self):
|
| 132 |
"""Renderuj sidebar z konfiguracją"""
|
| 133 |
+
st.sidebar.title("🎙️ QualiInsightLab")
|
| 134 |
st.sidebar.markdown("---")
|
| 135 |
|
| 136 |
# API Keys
|
|
|
|
| 200 |
'language': language
|
| 201 |
}
|
| 202 |
|
| 203 |
+
def update_progress_safe(self, progress_value, status_message):
|
| 204 |
+
"""Thread-safe aktualizacja postępu"""
|
| 205 |
+
try:
|
| 206 |
+
st.session_state.overall_progress = progress_value
|
| 207 |
+
st.session_state.current_file_progress = status_message
|
| 208 |
+
except:
|
| 209 |
+
# Fallback - wyświetl w konsoli
|
| 210 |
+
print(f"PROGRESS: {progress_value:.1%} - {status_message}")
|
| 211 |
+
|
| 212 |
+
def update_transcriptions_safe(self, filename, transcription):
|
| 213 |
+
"""Thread-safe aktualizacja transkrypcji"""
|
| 214 |
+
try:
|
| 215 |
+
st.session_state.transcriptions[filename] = transcription
|
| 216 |
+
except:
|
| 217 |
+
# Fallback - zapisz do lokalnej zmiennej
|
| 218 |
+
if not hasattr(self, 'local_transcriptions'):
|
| 219 |
+
self.local_transcriptions = {}
|
| 220 |
+
self.local_transcriptions[filename] = transcription
|
| 221 |
+
print(f"TRANSCRIPTION: {filename} completed locally")
|
| 222 |
+
|
| 223 |
+
def sync_local_data(self):
|
| 224 |
+
"""Synchronizuj dane lokalne z session_state"""
|
| 225 |
+
try:
|
| 226 |
+
# Przenies logi lokalne do session_state
|
| 227 |
+
if hasattr(self, 'local_logs'):
|
| 228 |
+
for log in self.local_logs:
|
| 229 |
+
st.session_state.logs.append(log)
|
| 230 |
+
self.local_logs = []
|
| 231 |
+
|
| 232 |
+
# Przenies transkrypcje lokalne do session_state
|
| 233 |
+
if hasattr(self, 'local_transcriptions'):
|
| 234 |
+
for filename, transcription in self.local_transcriptions.items():
|
| 235 |
+
st.session_state.transcriptions[filename] = transcription
|
| 236 |
+
self.local_transcriptions = {}
|
| 237 |
+
|
| 238 |
+
except Exception as e:
|
| 239 |
+
print(f"SYNC ERROR: {e}")
|
| 240 |
"""Wyczyść sesję i pliki tymczasowe"""
|
| 241 |
# Zatrzymaj przetwarzanie jeśli trwa
|
| 242 |
if st.session_state.processing_status == 'running':
|
|
|
|
| 399 |
"""Renderuj postęp przetwarzania"""
|
| 400 |
progress_container = st.container()
|
| 401 |
|
| 402 |
+
# Synchronizuj dane z wątku
|
| 403 |
+
self.sync_local_data()
|
| 404 |
+
|
| 405 |
with progress_container:
|
| 406 |
# Overall progress bar
|
| 407 |
st.progress(st.session_state.overall_progress)
|
|
|
|
| 421 |
st.rerun()
|
| 422 |
|
| 423 |
def process_files_async(self, settings):
|
| 424 |
+
"""Asynchroniczne przetwarzanie plików - thread-safe"""
|
| 425 |
try:
|
| 426 |
+
self.log_message("🚀 Rozpoczynam przetwarzanie plików", force_local=True)
|
| 427 |
|
| 428 |
total_files = len(st.session_state.uploaded_files)
|
| 429 |
|
| 430 |
# ETAP 1: Transkrypcja plików
|
| 431 |
for i, file_info in enumerate(st.session_state.uploaded_files):
|
| 432 |
+
# Sprawdź status w bezpieczny sposób
|
| 433 |
+
try:
|
| 434 |
+
if st.session_state.processing_status != 'running':
|
| 435 |
+
self.log_message("⏹️ Przetwarzanie zatrzymane przez użytkownika", force_local=True)
|
| 436 |
+
return
|
| 437 |
+
except:
|
| 438 |
+
# Jeśli nie można sprawdzić statusu, zakończ
|
| 439 |
+
print("Cannot access session_state status - stopping")
|
| 440 |
+
return
|
| 441 |
|
| 442 |
try:
|
| 443 |
# Update progress
|
| 444 |
progress = i / (total_files + 1) # +1 for report generation
|
| 445 |
+
self.update_progress_safe(progress, f"🎙️ Transkrybuję: {file_info['name']}")
|
|
|
|
| 446 |
|
| 447 |
+
self.log_message(f"📝 Rozpoczynam transkrypcję: {file_info['name']}", force_local=True)
|
| 448 |
|
| 449 |
# Przetwórz plik (kompresja/podział jeśli potrzeba)
|
| 450 |
processed_files = self.file_handler.process_file(
|
|
|
|
| 461 |
for pf in processed_files:
|
| 462 |
file_size_mb = os.path.getsize(pf) / (1024 * 1024)
|
| 463 |
if file_size_mb > 25:
|
| 464 |
+
self.log_message(f"⚠️ Część {pf} za duża: {file_size_mb:.1f}MB", "WARNING", force_local=True)
|
| 465 |
else:
|
| 466 |
valid_parts.append(pf)
|
| 467 |
|
|
|
|
| 475 |
)
|
| 476 |
|
| 477 |
if transcription:
|
| 478 |
+
self.update_transcriptions_safe(file_info['name'], transcription)
|
| 479 |
+
self.log_message(f"✅ Transkrypcja zakończona: {file_info['name']}", force_local=True)
|
| 480 |
else:
|
| 481 |
+
self.log_message(f"❌ Transkrypcja nieudana: {file_info['name']}", "ERROR", force_local=True)
|
| 482 |
|
| 483 |
except Exception as e:
|
| 484 |
+
self.log_message(f"❌ Błąd przetwarzania {file_info['name']}: {str(e)}", "ERROR", force_local=True)
|
| 485 |
continue
|
| 486 |
|
| 487 |
# ETAP 2: Generowanie raportu
|
| 488 |
+
try:
|
| 489 |
+
# Sprawdź czy mamy transkrypcje (lokalnie lub w session_state)
|
| 490 |
+
transcriptions_to_use = {}
|
|
|
|
|
|
|
| 491 |
|
| 492 |
+
# Zbierz transkrypcje z session_state
|
| 493 |
try:
|
| 494 |
+
transcriptions_to_use.update(st.session_state.transcriptions)
|
| 495 |
+
except:
|
| 496 |
+
pass
|
| 497 |
+
|
| 498 |
+
# Dodaj lokalne transkrypcje jeśli są
|
| 499 |
+
if hasattr(self, 'local_transcriptions'):
|
| 500 |
+
transcriptions_to_use.update(self.local_transcriptions)
|
| 501 |
+
|
| 502 |
+
if transcriptions_to_use:
|
| 503 |
+
self.update_progress_safe(0.9, "📄 Generuję raport badawczy...")
|
| 504 |
+
self.log_message("📄 Rozpoczynam generowanie raportu", force_local=True)
|
| 505 |
+
|
| 506 |
+
# Pobierz brief z session_state
|
| 507 |
+
try:
|
| 508 |
+
brief = st.session_state.research_brief
|
| 509 |
+
except:
|
| 510 |
+
brief = ""
|
| 511 |
+
|
| 512 |
final_report = self.report_generator.generate_comprehensive_report(
|
| 513 |
+
transcriptions_to_use,
|
| 514 |
+
brief
|
| 515 |
)
|
| 516 |
|
| 517 |
+
# Zapisz raport
|
| 518 |
+
try:
|
| 519 |
+
st.session_state.final_report = final_report
|
| 520 |
+
st.session_state.processing_status = 'completed'
|
| 521 |
+
except:
|
| 522 |
+
# Fallback - zapisz lokalnie
|
| 523 |
+
self.local_final_report = final_report
|
| 524 |
+
print("REPORT: Generated and saved locally")
|
| 525 |
|
| 526 |
+
self.update_progress_safe(1.0, "✅ Zakończono!")
|
| 527 |
+
self.log_message("🎉 Raport wygenerowany pomyślnie!", force_local=True)
|
| 528 |
|
| 529 |
+
else:
|
| 530 |
+
self.log_message("❌ Brak transkrypcji do wygenerowania raportu", "ERROR", force_local=True)
|
| 531 |
+
try:
|
| 532 |
+
st.session_state.processing_status = 'error'
|
| 533 |
+
except:
|
| 534 |
+
print("ERROR: No transcriptions available")
|
| 535 |
+
|
| 536 |
+
except Exception as e:
|
| 537 |
+
self.log_message(f"❌ Błąd generowania raportu: {str(e)}", "ERROR", force_local=True)
|
| 538 |
+
try:
|
| 539 |
st.session_state.processing_status = 'error'
|
| 540 |
+
except:
|
| 541 |
+
print(f"REPORT ERROR: {e}")
|
| 542 |
|
| 543 |
+
except Exception as e:
|
| 544 |
+
self.log_message(f"💥 Błąd krytyczny: {str(e)}", "ERROR", force_local=True)
|
| 545 |
+
try:
|
| 546 |
+
st.session_state.processing_status = 'error'
|
| 547 |
+
except:
|
| 548 |
+
print(f"CRITICAL ERROR: {e}")
|
| 549 |
+
|
| 550 |
+
def sync_local_data(self):
|
| 551 |
+
"""Synchronizuj dane lokalne z session_state"""
|
| 552 |
+
try:
|
| 553 |
+
# Przenies logi lokalne do session_state
|
| 554 |
+
if hasattr(self, 'local_logs'):
|
| 555 |
+
for log in self.local_logs:
|
| 556 |
+
if 'logs' in st.session_state:
|
| 557 |
+
st.session_state.logs.append(log)
|
| 558 |
+
self.local_logs = []
|
| 559 |
+
|
| 560 |
+
# Przenies transkrypcje lokalne do session_state
|
| 561 |
+
if hasattr(self, 'local_transcriptions'):
|
| 562 |
+
for filename, transcription in self.local_transcriptions.items():
|
| 563 |
+
st.session_state.transcriptions[filename] = transcription
|
| 564 |
+
self.local_transcriptions = {}
|
| 565 |
+
|
| 566 |
+
# Przenies raport lokalny do session_state
|
| 567 |
+
if hasattr(self, 'local_final_report'):
|
| 568 |
+
st.session_state.final_report = self.local_final_report
|
| 569 |
+
st.session_state.processing_status = 'completed'
|
| 570 |
+
delattr(self, 'local_final_report')
|
| 571 |
|
| 572 |
except Exception as e:
|
| 573 |
+
print(f"SYNC ERROR: {e}")
|
|
|
|
| 574 |
|
| 575 |
def render_results(self):
|
| 576 |
"""Renderuj wyniki"""
|
|
|
|
| 647 |
settings = self.render_sidebar()
|
| 648 |
|
| 649 |
# Main content
|
| 650 |
+
st.title("🎙️ QualiInsightLab")
|
| 651 |
st.markdown("*Automatyczna transkrypcja i analiza wywiadów fokusowych oraz indywidualnych*")
|
| 652 |
st.markdown("---")
|
| 653 |
|