143 lines
5.2 KiB
Python
143 lines
5.2 KiB
Python
"""Tab 3 - Processing: Instructions/Notes editieren, KI-Zusammenfassung anstoßen.
|
|
|
|
Ausgegraut, solange kein Transkript existiert (siehe MainWindow-Tab-Aktivierungslogik).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
from PySide6.QtCore import Signal
|
|
from PySide6.QtWidgets import QHBoxLayout, QLabel, QMessageBox, QPushButton, QTextEdit, QVBoxLayout, QWidget
|
|
|
|
from notarius.gui.controller import AppController
|
|
from notarius.gui.format_utils import format_duration, format_timestamp
|
|
from notarius.keyring_store import get_api_key
|
|
from notarius.models.recording import Recording
|
|
from notarius.summarization.worker import SummarizationWorker
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ProcessingTab(QWidget):
|
|
# MainWindow öffnet daraufhin automatisch Tab 4 und prüft die Tab-Aktivierung neu.
|
|
summarization_finished = Signal()
|
|
|
|
def __init__(self, controller: AppController, parent: QWidget | None = None) -> None:
|
|
super().__init__(parent)
|
|
self._controller = controller
|
|
self._recording: Recording | None = None
|
|
self._worker = SummarizationWorker()
|
|
|
|
self._header_label = QLabel()
|
|
self._header_label.setWordWrap(True)
|
|
|
|
self._notes_edit = QTextEdit()
|
|
self._notes_edit.setPlaceholderText(
|
|
"Zusatzinfos für die KI: Zuordnung SPEAKER_00 -> Name, Projektkontext, "
|
|
"Teilnehmer, Kanal, Datum, ..."
|
|
)
|
|
self._notes_save_button = QPushButton("Save")
|
|
self._notes_save_button.clicked.connect(self._on_notes_save_clicked)
|
|
|
|
self._summarize_button = QPushButton("Summarize")
|
|
self._summarize_button.clicked.connect(self._on_summarize_clicked)
|
|
self._status_label = QLabel("idle")
|
|
|
|
action_row = QHBoxLayout()
|
|
action_row.addWidget(self._summarize_button)
|
|
action_row.addWidget(self._status_label)
|
|
action_row.addStretch(1)
|
|
|
|
layout = QVBoxLayout(self)
|
|
layout.addWidget(self._header_label)
|
|
layout.addWidget(QLabel("<b>Instructions and Notes</b>"))
|
|
layout.addWidget(self._notes_edit, stretch=1)
|
|
layout.addWidget(self._notes_save_button)
|
|
layout.addLayout(action_row)
|
|
|
|
self.set_recording(None)
|
|
|
|
def set_recording(self, recording: Recording | None) -> None:
|
|
self._recording = recording
|
|
has_recording = recording is not None
|
|
self.setEnabled(has_recording)
|
|
if not has_recording:
|
|
return
|
|
|
|
self._header_label.setText(
|
|
f"<b>Titel:</b> {recording.meta.title}<br>"
|
|
f"<b>Aufzeichnungszeitpunkt:</b> {format_timestamp(recording.meta.created_at)}<br>"
|
|
f"<b>Länge:</b> {format_duration(recording.meta.duration_seconds)}<br>"
|
|
f"<b>Sprecher:</b> {', '.join(recording.meta.speakers) or '-'}"
|
|
)
|
|
|
|
if recording.prompt_notes_path.exists():
|
|
self._notes_edit.setPlainText(recording.prompt_notes_path.read_text(encoding="utf-8"))
|
|
else:
|
|
self._notes_edit.clear()
|
|
|
|
self._status_label.setText("ready" if recording.has_summary else "idle")
|
|
self._summarize_button.setText("Summarize")
|
|
self._summarize_button.setEnabled(True)
|
|
|
|
def _on_notes_save_clicked(self) -> None:
|
|
if self._recording is None:
|
|
return
|
|
self._recording.prompt_notes_path.write_text(self._notes_edit.toPlainText(), encoding="utf-8")
|
|
|
|
def _on_summarize_clicked(self) -> None:
|
|
if self._worker.is_running:
|
|
self._worker.cancel()
|
|
return
|
|
|
|
if self._recording is None:
|
|
return
|
|
|
|
api_key = get_api_key()
|
|
if not api_key:
|
|
QMessageBox.information(
|
|
self, "Kein API-Key", "Bitte zuerst in Tab 5 (Settings) einen Anthropic-API-Key hinterlegen."
|
|
)
|
|
return
|
|
|
|
if self._recording.has_summary:
|
|
answer = QMessageBox.question(
|
|
self, "Summary überschreiben?", "Es existiert bereits eine Summary. Überschreiben?"
|
|
)
|
|
if answer != QMessageBox.StandardButton.Yes:
|
|
return
|
|
|
|
transcript_markdown = self._recording.transcript_path.read_text(encoding="utf-8")
|
|
notes = self._notes_edit.toPlainText()
|
|
|
|
self._summarize_button.setText("Stop")
|
|
self._status_label.setText("processing")
|
|
|
|
self._worker.start(
|
|
api_key,
|
|
self._controller.config.claude_model,
|
|
transcript_markdown,
|
|
notes,
|
|
self._on_summary_ok,
|
|
self._on_summary_error,
|
|
self._on_summary_cancelled,
|
|
)
|
|
|
|
def _on_summary_ok(self, summary_markdown: str) -> None:
|
|
if self._recording is None:
|
|
return
|
|
self._recording.summary_path.write_text(summary_markdown, encoding="utf-8")
|
|
self._summarize_button.setText("Summarize")
|
|
self._status_label.setText("ready")
|
|
self.summarization_finished.emit()
|
|
|
|
def _on_summary_error(self, error_message: str) -> None:
|
|
self._summarize_button.setText("Summarize")
|
|
self._status_label.setText("error")
|
|
QMessageBox.critical(self, "Zusammenfassung fehlgeschlagen", error_message)
|
|
|
|
def _on_summary_cancelled(self) -> None:
|
|
self._summarize_button.setText("Summarize")
|
|
self._status_label.setText("idle")
|