From cebe18665495907e534cd52873da44aca041a087 Mon Sep 17 00:00:00 2001 From: Tehila Git Date: Sun, 23 Nov 2025 14:40:40 +0200 Subject: [PATCH 1/2] fix sound-gui #364 --- GUI/src/vast/views/sound/map_background.png | Bin 208934 -> 0 bytes GUI/src/vast/views/sound/recordings_tab.py | 517 +++++ .../vast/views/sound/sound_analytics_view.py | 817 +++++++ GUI/src/vast/views/sound/sound_graphic.py | 38 + GUI/src/vast/views/sound/sound_main.py | 13 + GUI/src/vast/views/sound/sound_view.py | 2018 +---------------- GUI/src/vast/views/sound/utils.py | 16 + 7 files changed, 1445 insertions(+), 1974 deletions(-) delete mode 100644 GUI/src/vast/views/sound/map_background.png create mode 100644 GUI/src/vast/views/sound/recordings_tab.py create mode 100644 GUI/src/vast/views/sound/sound_analytics_view.py create mode 100644 GUI/src/vast/views/sound/sound_graphic.py create mode 100644 GUI/src/vast/views/sound/sound_main.py create mode 100644 GUI/src/vast/views/sound/utils.py diff --git a/GUI/src/vast/views/sound/map_background.png b/GUI/src/vast/views/sound/map_background.png deleted file mode 100644 index 2f71da42e61d15cc275a7884abdc0975743e92e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 208934 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%0AMb$ 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

-%_6Cox!9EL){LgX^!U!-RHpT5a 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

)9!aU{ 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 L^iX&h319!&1~mKL diff --git a/GUI/src/vast/views/sound/recordings_tab.py b/GUI/src/vast/views/sound/recordings_tab.py new file mode 100644 index 000000000..27159a271 --- /dev/null +++ b/GUI/src/vast/views/sound/recordings_tab.py @@ -0,0 +1,517 @@ +from PyQt6.QtWidgets import ( + QWidget, QVBoxLayout, QHBoxLayout, QLabel, QFrame, + QComboBox, QPushButton, QLineEdit, QDateEdit, + QTableWidget, QTableWidgetItem, QHeaderView, + QAbstractItemView, QMessageBox, QSizePolicy, QApplication +) +from PyQt6.QtCore import Qt, QDate +from PyQt6.QtGui import QColor, QCursor, QFont + +from .utils import normalize_minio_url, MINIO_BASE + +import requests +import os + + +class RecordingsTab(QWidget): + def __init__(self, mic_ids=None, recording_type="audio", parent=None, api=None): + super().__init__(parent) + self.mic_ids = mic_ids if mic_ids else [] + self.recording_type = recording_type + self.api = api + + if recording_type == "ultrasound": + self.api_url = "http://db_api_service:8001/api/files/plant-predictions/" + else: + self.api_url = "http://db_api_service:8001/api/files/audio-aggregates/" + + layout = QVBoxLayout(self) + layout.setContentsMargins(20, 20, 20, 20) + layout.setSpacing(15) + + filter_frame = self._create_filter_frame() + + list_label = QLabel("Available Recordings") + list_label.setStyleSheet( + "font-size: 16px; font-weight: bold; color: #333; padding: 5px;" + ) + + self.file_table = self._create_table() + + self.status_label = QLabel("Ready") + self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.status_label.setStyleSheet(""" + font-size: 13px; color: #666; padding: 8px; + background-color: #f6f8fa; border-radius: 6px; + border: 1px solid #d1d5da; + """) + + layout.addWidget(filter_frame) + layout.addWidget(list_label) + layout.addWidget(self.file_table, 1) + layout.addWidget(self.status_label) + + self.refresh_button.clicked.connect(self.update_list) + self.update_list() + + # ---------------- Filters UI ---------------- + def _create_filter_frame(self): + filter_frame = QFrame() + filter_frame.setStyleSheet(""" + QFrame { background-color: #ffffff; border-radius: 12px; padding: 15px; } + """) + filters_layout = QVBoxLayout(filter_frame) + filters_layout.setSpacing(12) + + filter_row = QHBoxLayout() + filter_row.setSpacing(8) + filter_row.setContentsMargins(0, 0, 0, 0) + + type_label = QLabel("Type:") + type_label.setStyleSheet("font-weight: bold; color: #333; font-size: 11px;") + filter_row.addWidget(type_label) + + self.noise_filter = QComboBox() + self.noise_filter.setMaximumWidth(180) + self.noise_filter.setStyleSheet(""" + QComboBox { + padding: 6px 10px; + border: 1px solid #d1d5da; + border-radius: 4px; + background: white; + font-size: 12px; + } + QComboBox:hover { border: 1px solid #4A90E2; } + """) + + if self.recording_type == "ultrasound": + self.noise_filter.addItems([ + "All signals", "Drought-stressed plant", + "Empty Pot", "Greenhouse Noises" + ]) + else: + self.noise_filter.addItems([ + "All types", "predatory_animals", "non_predatory_animals", + "birds", "fire", "footsteps", "insects", "screaming", + "shotgun", "stormy_weather", "streaming_water", "vehicle", "Other" + ]) + + filter_row.addWidget(self.noise_filter) + + date_label = QLabel(" From:") + date_label.setStyleSheet("font-weight: bold; color: #333; font-size: 11px;") + filter_row.addWidget(date_label) + + today = QDate.currentDate() + first_day = QDate(today.year(), today.month(), 1) + + self.date_from = QDateEdit() + self.date_from.setCalendarPopup(True) + self.date_from.setDate(first_day) + self.date_from.setMaximumWidth(120) + self.date_from.setStyleSheet(""" + QDateEdit { + padding: 6px 8px; + border: 1px solid #d1d5da; + border-radius: 4px; + background: white; + font-size: 12px; + } + """) + filter_row.addWidget(self.date_from) + + filter_row.addWidget(QLabel("→")) + + self.date_to = QDateEdit() + self.date_to.setCalendarPopup(True) + self.date_to.setDate(today) + self.date_to.setMaximumWidth(120) + self.date_to.setStyleSheet(self.date_from.styleSheet()) + filter_row.addWidget(self.date_to) + + self.search_box = QLineEdit() + self.search_box.setPlaceholderText("Search filename...") + self.search_box.setMaximumWidth(200) + self.search_box.setStyleSheet(""" + QLineEdit { + padding: 6px 10px; + border: 1px solid #d1d5da; + border-radius: 4px; + background: white; + font-size: 12px; + } + QLineEdit:focus { border: 1px solid #4A90E2; } + """) + filter_row.addWidget(self.search_box) + + filter_row.addStretch() + + filter_row.addWidget(QLabel("sort by:")) + self.sort_by = QComboBox() + self.sort_by.addItems(["date", "name", "device"]) + self.sort_by.setMaximumWidth(130) + self.sort_by.setStyleSheet(""" + QComboBox { + padding: 6px 10px; + border: 1px solid #d1d5da; + border-radius: 4px; + background: white; + font-size: 12px; + } + """) + filter_row.addWidget(self.sort_by) + + self.refresh_button = QPushButton("🔄 Refresh") + self.refresh_button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) + self.refresh_button.setStyleSheet(""" + QPushButton { + background-color: #4A90E2; + color: white; + border-radius: 6px; + padding: 8px 16px; + font-weight: bold; + font-size: 12px; + } + QPushButton:hover { background-color: #357ABD; } + """) + filter_row.addWidget(self.refresh_button) + + filters_layout.addLayout(filter_row) + + return filter_frame + + # ---------------- Table UI ---------------- + def _create_table(self): + table = QTableWidget() + + if self.recording_type == "ultrasound": + table.setColumnCount(6) + table.setHorizontalHeaderLabels([ + "File", "Device", "Predicted Label", "Confidence", "Watering Status", "Format" + ]) + else: + table.setColumnCount(5) + table.setHorizontalHeaderLabels([ + "File", "Device", "Predicted Label", "Probability", "Actions" + ]) + + header = table.horizontalHeader() + header.setStretchLastSection(False) + + # Set column proportions + if self.recording_type == "audio": + header.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch) # File - takes remaining space + header.setSectionResizeMode(1, QHeaderView.ResizeMode.ResizeToContents) # Device + header.setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents) # Label + header.setSectionResizeMode(3, QHeaderView.ResizeMode.ResizeToContents) # Probability + header.setSectionResizeMode(4, QHeaderView.ResizeMode.Fixed) # Actions + table.setColumnWidth(4, 280) # Fixed width for actions column + else: + for i in range(table.columnCount()): + header.setSectionResizeMode(i, QHeaderView.ResizeMode.Stretch) + + table.setStyleSheet(""" + QTableWidget { + background: #ffffff; + border: 2px solid #d1d5da; + border-radius: 10px; + gridline-color: #e1e4e8; + font-size: 13px; + } + QTableWidget::item { + padding: 10px 8px; + border-bottom: 1px solid #f0f0f0; + } + QTableWidget::item:hover { background-color: #f8f9fa; } + QTableWidget::item:selected { background-color: #e3f2fd; color: #0366d6; } + QHeaderView::section { + background-color: #f6f8fa; + padding: 12px 10px; + border: none; + border-bottom: 2px solid #d1d5da; + font-weight: bold; + font-size: 12px; + color: #24292e; + text-transform: uppercase; + letter-spacing: 0.5px; + } + """) + + table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows) + table.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection) + table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers) + table.verticalHeader().setVisible(False) + table.setAlternatingRowColors(False) + + return table + + def _map_ultrasound_label(self, raw: str) -> str: + if not raw: + return "Unknown" + lower = raw.lower() + if "tomato" in lower or "tobacco" in lower: + return "Drought-stressed plant" + return raw + + # ---------------- Data fetch ---------------- + def update_list(self): + self.file_table.setRowCount(0) + self.file_table.verticalHeader().setDefaultSectionSize(70) + self.status_label.setText("Loading...") + + params = { + "date_from": self.date_from.date().toString("yyyy-MM-dd"), + "date_to": self.date_to.date().toString("yyyy-MM-dd"), + "search": self.search_box.text().strip(), + "sort_by": self.sort_by.currentText(), + "limit": 100 + } + + filter_value = self.noise_filter.currentText() + if self.recording_type == "ultrasound": + if filter_value in ("Empty Pot", "Greenhouse Noises"): + params["predicted_class"] = filter_value + else: + if filter_value not in ("All types", "All signals"): + params["type"] = filter_value + + if self.mic_ids: + params["device_ids"] = ",".join(self.mic_ids) + + try: + if not self.api or not hasattr(self.api, 'http'): + self.status_label.setText("⚠ API connection not available") + QMessageBox.warning( + self, + "Authentication Required", + "Please login first to access recordings." + ) + return + + response = self.api.http.get(self.api_url, params=params, timeout=10) + response.raise_for_status() + data = response.json() + + print(f"[DEBUG] Successfully fetched {len(data)} records from {self.api_url}") + for f in data: + row = self.file_table.rowCount() + self.file_table.insertRow(row) + self.file_table.setRowHeight(row, 70) + + filename = f.get("filename") or f.get("file", "") + is_compressed = f.get("is_compressed", False) + + text_color = QColor("#888888") if is_compressed else QColor("#000000") + + if self.recording_type == "ultrasound": + device_id = f.get("device_id", "N/A") + pred_class_raw = f.get("predicted_class", "Unknown") + pred_class = self._map_ultrasound_label(pred_class_raw) + confidence = f.get("confidence", 0) + watering_status = f.get("watering_status", "N/A") + url = normalize_minio_url(f.get("url", "")) + + format_str = "OPUS (Compressed)" if is_compressed else "WAV (Original)" + + item0 = QTableWidgetItem(filename) + item0.setForeground(text_color) + self.file_table.setItem(row, 0, item0) + + item1 = QTableWidgetItem(device_id) + item1.setForeground(text_color) + self.file_table.setItem(row, 1, item1) + + item2 = QTableWidgetItem(pred_class) + item2.setForeground(text_color) + self.file_table.setItem(row, 2, item2) + + item3 = QTableWidgetItem(f"{confidence:.2%}") + item3.setForeground(text_color) + self.file_table.setItem(row, 3, item3) + + item4 = QTableWidgetItem(watering_status) + item4.setForeground(text_color) + self.file_table.setItem(row, 4, item4) + + item5 = QTableWidgetItem(format_str) + item5.setForeground(text_color) + self.file_table.setItem(row, 5, item5) + else: + device_id = f.get("device_id", "N/A") + label = f.get("predicted_label", "Unknown") + prob = f.get("probability", 0) + url = normalize_minio_url(f.get("url", "")) + + item0 = QTableWidgetItem(filename) + item0.setForeground(text_color) + item0.setFont(QFont("Segoe UI", 10)) + self.file_table.setItem(row, 0, item0) + + item1 = QTableWidgetItem(device_id) + item1.setForeground(text_color) + item1.setFont(QFont("Segoe UI", 10)) + self.file_table.setItem(row, 1, item1) + + item2 = QTableWidgetItem(label) + item2.setForeground(text_color) + item2.setFont(QFont("Segoe UI", 10, QFont.Weight.Bold)) + self.file_table.setItem(row, 2, item2) + + item3 = QTableWidgetItem(f"{prob:.1%}") + item3.setForeground(text_color) + item3.setFont(QFont("Segoe UI", 10)) + item3.setTextAlignment(Qt.AlignmentFlag.AlignCenter) + self.file_table.setItem(row, 3, item3) + + # Actions column - improved layout + control_widget = QWidget() + control_layout = QHBoxLayout(control_widget) + control_layout.setContentsMargins(8, 8, 8, 8) + control_layout.setSpacing(6) + + # Copy button - compact + copy_btn = QPushButton("📋") + copy_btn.setFixedSize(36, 36) + copy_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) + copy_btn.setStyleSheet(""" + QPushButton { + background-color: #6c757d; + color: white; + border: none; + border-radius: 6px; + font-size: 16px; + padding: 0; + } + QPushButton:hover { + background-color: #5a6268; + transform: scale(1.05); + } + QPushButton:pressed { + background-color: #4e555b; + } + """) + copy_btn.setToolTip("Copy URL to clipboard") + + def copy_link(): + clipboard = QApplication.clipboard() + clipboard.setText(url) + self.status_label.setText(f"✓ Copied: {filename[:50]}...") + + copy_btn.clicked.connect(copy_link) + control_layout.addWidget(copy_btn) + + # Download button - prominent + download_btn = QPushButton("💾 Download") + download_btn.setFixedHeight(36) + download_btn.setMinimumWidth(120) + download_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) + download_btn.setStyleSheet(""" + QPushButton { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #17a2b8, stop:1 #138496); + color: white; + border: none; + border-radius: 6px; + font-weight: bold; + font-size: 12px; + padding: 0 16px; + } + QPushButton:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #1ab5cc, stop:1 #17a2b8); + } + QPushButton:pressed { + background: #138496; + } + """) + download_btn.setToolTip("Download file to /app/downloads") + download_btn.clicked.connect( + lambda checked=False, u=url, fname=filename: self.download_audio(u, fname) + ) + control_layout.addWidget(download_btn) + + control_layout.addStretch() + + self.file_table.setCellWidget(row, 4, control_widget) + + if self.file_table.rowCount() == 0: + self.file_table.insertRow(0) + empty_item = QTableWidgetItem("No recordings found") + empty_item.setForeground(QColor("#999")) + self.file_table.setItem(0, 0, empty_item) + self.file_table.setSpan(0, 0, 1, 5) + + self.status_label.setText(f"✓ Loaded {len(data)} recordings") + + except requests.exceptions.HTTPError as e: + if e.response.status_code == 401: + self.status_label.setText("⚠ Authentication required") + QMessageBox.warning( + self, "Authentication Error", + "API requires authentication. Please check your credentials." + ) + else: + self.status_label.setText(f"⚠ HTTP Error {e.response.status_code}") + QMessageBox.warning( + self, "HTTP Error", + f"Server returned error {e.response.status_code}:\n{str(e)}" + ) + except requests.exceptions.Timeout: + self.status_label.setText("⚠ Request timeout") + QMessageBox.warning(self, "Timeout", "Request timed out. Please try again.") + except requests.exceptions.ConnectionError: + self.status_label.setText("⚠ Connection error") + QMessageBox.warning( + self, "Connection Error", + "Could not connect to server. Check your connection." + ) + except Exception as e: + self.status_label.setText("⚠ Error loading data") + QMessageBox.warning(self, "Error", f"Failed to load recordings:\n{str(e)}") + + # ---------------- Download helper ---------------- + def download_audio(self, url, filename): + if not url: + QMessageBox.warning(self, "No URL", "Audio file URL not available") + return + + try: + downloads_dir = "/app/downloads" + os.makedirs(downloads_dir, exist_ok=True) + + safe_filename = filename.replace("/", "_").replace("\\", "_") + save_path = os.path.join(downloads_dir, safe_filename) + + playback_url = url + if url.startswith("http://localhost") or url.startswith("http://127.0.0.1"): + parts = url.split("/", 3) + if len(parts) > 3: + path = parts[3] + playback_url = f"http://minio-hot:9000/{path}" + + self.status_label.setText(f"Downloading {safe_filename[:40]}...") + + session = self.api.http if (self.api and getattr(self.api, "http", None)) else requests + resp = session.get(playback_url, timeout=15) + resp.raise_for_status() + + with open(save_path, 'wb') as f: + f.write(resp.content) + + file_size = os.path.getsize(save_path) / (1024 * 1024) + + self.status_label.setText(f"✓ Downloaded: {safe_filename[:40]}...") + QMessageBox.information( + self, + "Download Complete", + f"✅ File downloaded successfully!\n\n" + f"📁 Location: {save_path}\n" + f"📊 Size: {file_size:.2f} MB\n\n" + f"💡 To access on your computer:\n" + f"The file is saved in Docker volume 'downloads'\n" + f"Map this volume to your local machine in docker-compose.yml" + ) + + except Exception as e: + self.status_label.setText("⚠ Download failed") + QMessageBox.warning(self, "Download Error", f"Failed to download file:\n{e}") \ No newline at end of file diff --git a/GUI/src/vast/views/sound/sound_analytics_view.py b/GUI/src/vast/views/sound/sound_analytics_view.py new file mode 100644 index 000000000..d4f7d654d --- /dev/null +++ b/GUI/src/vast/views/sound/sound_analytics_view.py @@ -0,0 +1,817 @@ +from PyQt6.QtWidgets import ( + QWidget, QVBoxLayout, QHBoxLayout, QLabel, QFrame, QGridLayout, + QComboBox, QPushButton, QSizePolicy, QCheckBox, QScrollArea +) +from PyQt6.QtCore import Qt, QTimer, QSize +from PyQt6.QtGui import QFont + +from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas +from matplotlib.figure import Figure + +import numpy as np +from datetime import datetime, timedelta + +from dashboard_api import DashboardApi + + +class SoundAnalyticsView(QWidget): + """Sound detection dashboard with filtering by time range and sound type""" + + SOUND_TYPES = [ + "non_predatory_animals", + "predatory_animals", + "birds", + "fire", + "footsteps", + "insects", + "screaming", + "shotgun", + "stormy_weather", + "streaming_water", + "vehicle" + ] + + CYAN_PALETTE = [ + '#003366', '#004d99', '#0066cc', '#1a80e5', + '#3399ff', '#53A0E5', '#66b3ff', '#80ccff', + '#99e6ff', '#b3f0ff', '#ccf7ff' + ] + + PRIMARY_CYAN = '#53A0E5' + ACCENT_CYAN = '#3399ff' + + LIGHT_THEME = { + 'bg': '#f8f9fa', + 'card': '#ffffff', + 'text': '#333333', + 'border': '#e0e0e0', + 'primary': PRIMARY_CYAN, + 'accent': ACCENT_CYAN + } + + DARK_THEME = { + 'bg': '#1e1e1e', + 'card': '#2d2d2d', + 'text': '#e0e0e0', + 'border': '#444444', + 'primary': '#64B5F6', + 'accent': ACCENT_CYAN + } + + def __init__(self, api: DashboardApi, parent=None): + super().__init__(parent) + self.api = api + + print(f"[INIT] API object: {self.api}", flush=True) + print(f"[INIT] API has http: {hasattr(self.api, 'http')}", flush=True) + + try: + test_query = "SELECT 1 as test" + result = self.api.run_query(test_query) + print(f"[INIT] DB test result: {result}", flush=True) + except Exception as e: + print(f"[INIT] DB connection error: {e}", flush=True) + + self.current_time_range = 'day' + self.current_sound_types = [] + self.is_dark_theme = False + self.current_theme = self.LIGHT_THEME.copy() + + self.setWindowTitle("Sound Detection Analytics") + + main_layout = QVBoxLayout() + main_layout.setContentsMargins(10, 10, 10, 10) + main_layout.setSpacing(10) + + content_frame = QFrame() + content_layout = QVBoxLayout() + content_layout.setContentsMargins(12, 12, 12, 12) + content_layout.setSpacing(12) + + # ---------------- Filters ---------------- + filter_frame = QFrame() + filter_frame.setStyleSheet(""" + QFrame { + background-color: white; + border: 1px solid #e8e8e8; + border-radius: 8px; + } + """) + filter_frame.setMaximumHeight(450) + filter_layout = QVBoxLayout() + filter_layout.setContentsMargins(12, 10, 12, 10) + filter_layout.setSpacing(15) + + time_row = QHBoxLayout() + time_label = QLabel("Time Range:") + time_label.setFont(QFont("Arial", 10, QFont.Weight.Bold)) + time_row.addWidget(time_label) + self.time_filter = QComboBox() + self.time_filter.addItems(['1 Day', '1 Week', '1 Month']) + self.time_filter.setCurrentText('1 Day') + self.time_filter.currentTextChanged.connect(self._on_filter_changed) + self.time_filter.setMinimumWidth(140) + time_row.addWidget(self.time_filter) + time_row.addStretch() + filter_layout.addLayout(time_row) + + sound_header_row = QHBoxLayout() + sound_label = QLabel("Sound Types (select multiple):") + sound_label.setFont(QFont("Arial", 10, QFont.Weight.Bold)) + sound_header_row.addWidget(sound_label) + + self.selection_label = QLabel("All sounds selected") + self.selection_label.setStyleSheet( + f"color: {self.PRIMARY_CYAN}; font-weight: bold;" + ) + sound_header_row.addWidget(self.selection_label) + sound_header_row.addStretch() + + clear_btn = QPushButton("Clear All") + clear_btn.setMaximumWidth(100) + clear_btn.clicked.connect(self._clear_sound_selection) + sound_header_row.addWidget(clear_btn) + + apply_btn = QPushButton("Apply Filter") + apply_btn.setMaximumWidth(100) + apply_btn.setStyleSheet(f""" + QPushButton {{ + background-color: {self.PRIMARY_CYAN}; + color: white; + font-weight: bold; + }} + QPushButton:hover {{ + background-color: {self.CYAN_PALETTE[2]}; + }} + """) + apply_btn.clicked.connect(self._refresh_data) + sound_header_row.addWidget(apply_btn) + filter_layout.addLayout(sound_header_row) + + checkbox_container = QFrame() + checkbox_container.setObjectName("checkboxContainer") + checkbox_container.setStyleSheet(f""" + QFrame#checkboxContainer {{ + background-color: white; + border: 2px solid {self.PRIMARY_CYAN}; + border-radius: 6px; + max-height: 350px; + }} + """) + checkbox_layout = QGridLayout() + checkbox_layout.setSpacing(5) + checkbox_layout.setContentsMargins(10, 10, 10, 10) + + self.sound_checkboxes = {} + for idx, sound_name in enumerate(self.SOUND_TYPES): + checkbox = QCheckBox(sound_name) + checkbox.stateChanged.connect(self._on_sound_checkbox_changed) + self.sound_checkboxes[sound_name] = checkbox + row = idx // 3 + col = idx % 3 + checkbox_layout.addWidget(checkbox, row, col) + + checkbox_container.setLayout(checkbox_layout) + filter_layout.addWidget(checkbox_container) + filter_frame.setLayout(filter_layout) + content_layout.addWidget(filter_frame) + + # ---------------- Activity calendar ---------------- + calendar_frame = self._create_activity_calendar() + content_layout.addWidget(calendar_frame) + + # ---------------- Charts grid ---------------- + grid = QGridLayout() + grid.setSpacing(12) + grid.setRowStretch(0, 1) + grid.setRowStretch(1, 1) + grid.setRowStretch(2, 1) + grid.setColumnStretch(0, 1) + grid.setColumnStretch(1, 1) + + def make_chart_frame(title, canvas): + frame = self._create_chart_frame(title, canvas) + frame.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) + frame.setMinimumHeight(320) + frame.setMaximumHeight(320) + return frame + + self.dist_canvas = self._create_canvas(figsize=(6, 5)) + grid.addWidget(make_chart_frame("Sound Distribution (Count)", self.dist_canvas), 0, 0) + + self.timeline_canvas = self._create_canvas(figsize=(6, 5)) + grid.addWidget(make_chart_frame("Detection Timeline", self.timeline_canvas), 0, 1) + + self.heatmap_canvas = self._create_canvas(figsize=(6, 5)) + grid.addWidget(make_chart_frame("Sound Heatmap - Activity Patterns", self.heatmap_canvas), 1, 0) + + self.correlation_canvas = self._create_canvas(figsize=(6, 5)) + grid.addWidget(make_chart_frame("Correlation Explorer", self.correlation_canvas), 1, 1) + + self.confidence_canvas = self._create_canvas(figsize=(6, 5)) + grid.addWidget(make_chart_frame("Model Health Monitor", self.confidence_canvas), 2, 0) + + stats_frame = self._create_stats_frame() + stats_frame.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) + stats_frame.setMinimumHeight(320) + stats_frame.setMaximumHeight(320) + grid.addWidget(stats_frame, 2, 1) + + content_layout.addLayout(grid, stretch=10) + content_frame.setLayout(content_layout) + + scroll_area = QScrollArea() + scroll_area.setWidgetResizable(True) + scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn) + scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + scroll_area.setWidget(content_frame) + + main_layout.addWidget(scroll_area) + self.setLayout(main_layout) + + self.refresh_timer = QTimer() + self.refresh_timer.timeout.connect(self._refresh_data) + self.refresh_timer.start(30000) + + self._refresh_data() + + # ---------------- Activity calendar ---------------- + def _create_activity_calendar(self) -> QFrame: + frame = QFrame() + frame.setStyleSheet(""" + QFrame { + background-color: white; + border: 1px solid #e8e8e8; + border-radius: 8px; + } + """) + frame.setMaximumHeight(120) + + layout = QVBoxLayout() + layout.setContentsMargins(12, 12, 12, 12) + layout.setSpacing(8) + + title = QLabel("Activity Calendar (Last 30 Days)") + title.setFont(QFont("Arial", 10, QFont.Weight.Bold)) + layout.addWidget(title) + + calendar_grid = QHBoxLayout() + calendar_grid.setSpacing(2) + + today = datetime.now().date() + for i in range(30): + date = today - timedelta(days=29-i) + day_box = QFrame() + day_box.setMinimumSize(QSize(20, 20)) + day_box.setMaximumSize(QSize(20, 20)) + + intensity = np.random.rand() + color = self._get_intensity_color(intensity) + + day_box.setStyleSheet(f""" + QFrame {{ + background-color: {color}; + border: 1px solid #ddd; + border-radius: 2px; + }} + """) + + day_label = QLabel(str(date.day)) + day_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + day_label.setFont(QFont("Arial", 6)) + day_layout = QVBoxLayout() + day_layout.setContentsMargins(0, 0, 0, 0) + day_layout.addWidget(day_label) + day_box.setLayout(day_layout) + + calendar_grid.addWidget(day_box) + + calendar_grid.addStretch() + layout.addLayout(calendar_grid) + frame.setLayout(layout) + return frame + + def _get_intensity_color(self, intensity: float) -> str: + if intensity < 0.2: + return self.CYAN_PALETTE[0] + elif intensity < 0.4: + return self.CYAN_PALETTE[2] + elif intensity < 0.6: + return self.CYAN_PALETTE[4] + elif intensity < 0.8: + return self.CYAN_PALETTE[7] + else: + return self.CYAN_PALETTE[10] + + # ---------------- Generic canvas / frames ---------------- + def _create_canvas(self, figsize=(5.5, 4.5)): + canvas = FigureCanvas(Figure(figsize=figsize, dpi=90)) + canvas.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) + return canvas + + def _create_chart_frame(self, title: str, widget: QWidget) -> QFrame: + frame = QFrame() + frame.setStyleSheet(""" + QFrame { + background-color: white; + border: 1px solid #e8e8e8; + border-radius: 8px; + } + """) + + layout = QVBoxLayout() + layout.setContentsMargins(12, 12, 12, 12) + layout.setSpacing(8) + + title_label = QLabel(title) + title_label.setFont(QFont("Arial", 11, QFont.Weight.Bold)) + title_label.setStyleSheet(f"color: {self.PRIMARY_CYAN}; margin-bottom: 4px;") + layout.addWidget(title_label) + + layout.addWidget(widget, 1) + frame.setLayout(layout) + return frame + + def _create_stats_frame(self) -> QFrame: + frame = QFrame() + frame.setStyleSheet(""" + QFrame { + background-color: white; + border: 1px solid #e8e8e8; + border-radius: 8px; + } + """) + + layout = QVBoxLayout() + layout.setContentsMargins(12, 12, 12, 12) + layout.setSpacing(10) + + title_label = QLabel("Statistics") + title_label.setFont(QFont("Arial", 11, QFont.Weight.Bold)) + title_label.setStyleSheet(f"color: {self.PRIMARY_CYAN}; margin-bottom: 6px;") + layout.addWidget(title_label) + + stats_grid = QGridLayout() + stats_grid.setSpacing(10) + stats_grid.setRowStretch(0, 1) + stats_grid.setRowStretch(1, 1) + stats_grid.setColumnStretch(0, 1) + stats_grid.setColumnStretch(1, 1) + + self.stat_boxes = {} + + stat_names = [ + ("Total Files", "total_files"), + ("Unknown Type", "unknown_count"), + ("Avg Confidence", "avg_confidence"), + ("Avg Processing", "avg_processing_ms") + ] + + for idx, (label, key) in enumerate(stat_names): + row = idx // 2 + col = idx % 2 + box = self._create_stat_box(label) + self.stat_boxes[key] = box + stats_grid.addWidget(box, row, col) + + layout.addLayout(stats_grid, 1) + frame.setLayout(layout) + return frame + + def _create_stat_box(self, label: str) -> QFrame: + box = QFrame() + box.setStyleSheet(""" + QFrame { + background-color: #fafafa; + border: 2px solid #e8e8e8; + border-radius: 8px; + } + """) + + layout = QVBoxLayout() + layout.setContentsMargins(12, 12, 12, 12) + layout.setSpacing(8) + layout.setAlignment(Qt.AlignmentFlag.AlignCenter) + + label_widget = QLabel(label) + label_widget.setFont(QFont("Arial", 9, QFont.Weight.Bold)) + label_widget.setStyleSheet("color: #666;") + label_widget.setAlignment(Qt.AlignmentFlag.AlignCenter) + layout.addWidget(label_widget) + + value_widget = QLabel("--") + value_widget.setFont(QFont("Arial", 22, QFont.Weight.Bold)) + value_widget.setStyleSheet(f"color: {self.PRIMARY_CYAN};") + value_widget.setAlignment(Qt.AlignmentFlag.AlignCenter) + layout.addWidget(value_widget) + + box.setLayout(layout) + box._label = label_widget + box._value = value_widget + return box + + # ---------------- Filters logic ---------------- + def _on_sound_checkbox_changed(self): + selected = [] + for sound_name, checkbox in self.sound_checkboxes.items(): + if checkbox.isChecked(): + selected.append(sound_name) + + self.current_sound_types = selected + + if not selected: + self.selection_label.setText("All sounds selected") + elif len(selected) == 1: + self.selection_label.setText(f"1 sound type selected: {selected[0]}") + else: + self.selection_label.setText(f"{len(selected)} sound types selected") + + def _clear_sound_selection(self): + for checkbox in self.sound_checkboxes.values(): + checkbox.setChecked(False) + + self.current_sound_types = [] + self.selection_label.setText("All sounds selected") + self.time_filter.setCurrentText('1 Day') + self.current_time_range = 'day' + self._refresh_data() + + def _on_filter_changed(self): + time_map = {'1 Day': 'day', '1 Week': 'week', '1 Month': 'month'} + self.current_time_range = time_map.get(self.time_filter.currentText(), 'day') + self._refresh_data() + + # ---------------- Refresh data (main entry) ---------------- + def _refresh_data(self): + try: + self._clear_canvas(self.dist_canvas) + self._clear_canvas(self.timeline_canvas) + self._clear_canvas(self.confidence_canvas) + + self._update_distribution_chart() + self._update_timeline_chart() + self._update_heatmap_chart() + self._update_correlation_chart() + self._update_confidence_chart() + self._update_stats_boxes() + except Exception as e: + print(f"[SoundAnalyticsView] Refresh error: {e}", flush=True) + + # ---------------- Charts ---------------- + def _update_distribution_chart(self): + try: + sound_filter = self.current_sound_types if self.current_sound_types else None + data = self.api.get_audio_distribution( + self.current_time_range, + limit=15, + sound_types=sound_filter + ) + + print(f"[DEBUG] Distribution data: {len(data) if data else 0} items", flush=True) + + if not data: + self._show_no_data(self.dist_canvas) + return + + labels = [d['head_pred_label'] for d in data] + counts = [d['count'] for d in data] + + self.dist_canvas.figure.clear() + ax = self.dist_canvas.figure.add_subplot(111) + + colors = [ + self.CYAN_PALETTE[i % len(self.CYAN_PALETTE)] + for i in range(len(labels)) + ] + bars = ax.bar(range(len(labels)), counts, color=colors, edgecolor='black', linewidth=0.5) + + ax.set_xticks(range(len(labels))) + ax.set_xticklabels(labels, rotation=45, ha='right', fontsize=8) + ax.set_ylabel('Count', fontsize=9, fontweight='bold') + ax.grid(True, alpha=0.3, linestyle='--', axis='y') + + for bar in bars: + height = bar.get_height() + ax.text( + bar.get_x() + bar.get_width()/2., height, + f'{int(height)}', + ha='center', va='bottom', fontsize=8, fontweight='bold' + ) + + self.dist_canvas.figure.tight_layout() + self.dist_canvas.draw() + print("[DEBUG] Distribution chart drawn successfully", flush=True) + + except Exception as e: + print(f"[ERROR] Distribution chart error: {e}", flush=True) + import traceback + traceback.print_exc() + self._show_no_data(self.dist_canvas) + + def _update_timeline_chart(self): + try: + sound_filter = self.current_sound_types if self.current_sound_types else None + data = self.api.get_audio_timeline( + self.current_time_range, + sound_types=sound_filter + ) + + print(f"[DEBUG] Timeline data: {len(data) if data else 0} items", flush=True) + + if not data: + self._show_no_data(self.timeline_canvas) + return + + timeline_dict = {} + for row in data: + time_bucket = row['time_bucket'] + count = row['count'] + if time_bucket not in timeline_dict: + timeline_dict[time_bucket] = 0 + timeline_dict[time_bucket] += count + + sorted_times = sorted(timeline_dict.keys()) + times = [str(t)[:16] for t in sorted_times] + counts = [timeline_dict[t] for t in sorted_times] + + self.timeline_canvas.figure.clear() + ax = self.timeline_canvas.figure.add_subplot(111) + + ax.plot( + times, counts, + marker='o', linewidth=2, markersize=5, color=self.ACCENT_CYAN + ) + ax.fill_between( + range(len(times)), counts, + alpha=0.2, color=self.PRIMARY_CYAN + ) + ax.set_xlabel('Time', fontsize=9, fontweight='bold') + ax.set_ylabel('Detections', fontsize=9, fontweight='bold') + ax.grid(True, alpha=0.3, linestyle='--') + ax.tick_params(labelsize=8) + + self.timeline_canvas.figure.autofmt_xdate(rotation=45, ha='right') + self.timeline_canvas.figure.tight_layout() + self.timeline_canvas.draw() + print("[DEBUG] Timeline chart drawn successfully", flush=True) + + except Exception as e: + print(f"[ERROR] Timeline chart error: {e}", flush=True) + import traceback + traceback.print_exc() + self._show_no_data(self.timeline_canvas) + + def _update_confidence_chart(self): + try: + sound_filter = self.current_sound_types if self.current_sound_types else None + data = self.api.get_model_health_metrics( + self.current_time_range, + sound_types=sound_filter + ) + + if not data: + self._show_no_data(self.confidence_canvas) + return + + times = [str(d["time_bucket"])[:16] for d in data] + avg_conf = [d["avg_confidence"] * 100 for d in data] + avg_proc = [d["avg_processing_ms"] for d in data] + + fig = self.confidence_canvas.figure + fig.clear() + + ax1 = fig.add_subplot(111) + ax1.set_title( + "Model Performance Trends", + fontsize=10, fontweight="bold", color=self.PRIMARY_CYAN + ) + ax1.plot( + times, avg_conf, + color=self.ACCENT_CYAN, marker="o", + linewidth=2, label="Avg Confidence %" + ) + ax1.fill_between( + range(len(avg_conf)), avg_conf, + alpha=0.15, color=self.PRIMARY_CYAN + ) + ax1.set_ylabel("Confidence (%)", fontsize=9, fontweight="bold") + ax1.set_ylim(0, 100) + ax1.tick_params(axis='x', rotation=45, labelsize=8) + ax1.grid(True, alpha=0.3, linestyle="--") + + ax2 = ax1.twinx() + proc_color = self.CYAN_PALETTE[7] + ax2.plot( + times, avg_proc, + color=proc_color, marker="^", + linestyle="--", linewidth=2, + label="Avg Processing (ms)" + ) + ax2.set_ylabel( + "Processing Time (ms)", + fontsize=9, fontweight="bold", color=proc_color + ) + ax2.tick_params(axis='y', labelcolor=proc_color) + + lines, labels = ax1.get_legend_handles_labels() + lines2, labels2 = ax2.get_legend_handles_labels() + ax1.legend( + lines + lines2, labels + labels2, + loc="upper left", fontsize=8 + ) + + fig.tight_layout() + self.confidence_canvas.draw() + + except Exception as e: + print(f"[SoundAnalyticsView] Model Health Monitor chart error: {e}", flush=True) + self._show_no_data(self.confidence_canvas) + + def _update_stats_boxes(self): + try: + sound_filter = self.current_sound_types if self.current_sound_types else None + stats = self.api.get_audio_stats( + self.current_time_range, + sound_types=sound_filter + ) + + if stats: + total = stats.get('total_files', 0) or 0 + self.stat_boxes['total_files']._value.setText(str(total)) + + unknown = stats.get('unknown_count', 0) or 0 + self.stat_boxes['unknown_count']._value.setText(str(unknown)) + + avg_conf = stats.get('avg_confidence') + if avg_conf is not None and avg_conf > 0: + self.stat_boxes['avg_confidence']._value.setText(f"{avg_conf:.1%}") + else: + self.stat_boxes['avg_confidence']._value.setText("--") + + avg_proc = stats.get('avg_processing_ms') + if avg_proc is not None and avg_proc > 0: + self.stat_boxes['avg_processing_ms']._value.setText(f"{avg_proc:.0f}ms") + else: + self.stat_boxes['avg_processing_ms']._value.setText("--") + else: + for key in self.stat_boxes: + self.stat_boxes[key]._value.setText("--") + except Exception as e: + print(f"[SoundAnalyticsView] Stats update error: {e}", flush=True) + + def _clear_canvas(self, canvas): + canvas.figure.clear() + + def _show_no_data(self, canvas): + ax = canvas.figure.add_subplot(111) + ax.text( + 0.5, 0.5, 'No Data Available', + ha='center', va='center', fontsize=14, fontweight='bold', + transform=ax.transAxes, color='#999' + ) + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + ax.axis('off') + canvas.draw() + + def closeEvent(self, event): + self.refresh_timer.stop() + super().closeEvent(event) + + def _update_heatmap_chart(self): + try: + sound_filter = self.current_sound_types if self.current_sound_types else None + data = self.api.get_audio_heatmap( + self.current_time_range, + sound_types=sound_filter + ) + + print(f"[DEBUG] Heatmap data: {len(data) if data else 0} items", flush=True) + + if not data: + self._show_no_data(self.heatmap_canvas) + return + + heatmap_data = np.zeros((24, 7)) + + for row in data: + hour = int(row['hour_of_day']) + day = int(row['day_of_week']) + count = row['count'] + heatmap_data[hour, day] += count + + self.heatmap_canvas.figure.clear() + ax = self.heatmap_canvas.figure.add_subplot(111) + + im = ax.imshow(heatmap_data, cmap='GnBu', aspect='auto', interpolation='nearest') + + ax.set_xticks(range(7)) + ax.set_xticklabels(['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], fontsize=8) + ax.set_yticks(range(0, 24, 2)) + ax.set_yticklabels([f'{h:02d}:00' for h in range(0, 24, 2)], fontsize=7) + + ax.set_xlabel('Day of Week', fontsize=9, fontweight='bold') + ax.set_ylabel('Hour of Day', fontsize=9, fontweight='bold') + + cbar = self.heatmap_canvas.figure.colorbar(im, ax=ax, pad=0.02) + cbar.set_label('Detections', fontsize=8) + cbar.ax.tick_params(labelsize=7) + + for i in range(24): + for j in range(7): + if heatmap_data[i, j] > 0: + text_color = 'white' if heatmap_data[i, j] > heatmap_data.max() * 0.5 else 'black' + ax.text( + j, i, int(heatmap_data[i, j]), + ha="center", va="center", + color=text_color, fontsize=6, fontweight='bold' + ) + + self.heatmap_canvas.figure.tight_layout() + self.heatmap_canvas.draw() + print("[DEBUG] Heatmap chart drawn successfully", flush=True) + + except Exception as e: + print(f"[ERROR] Heatmap chart error: {e}", flush=True) + import traceback + traceback.print_exc() + self._show_no_data(self.heatmap_canvas) + + def _update_correlation_chart(self): + try: + sound_filter = self.current_sound_types if self.current_sound_types else None + data = self.api.get_audio_correlations( + self.current_time_range, + sound_types=sound_filter + ) + + print(f"[DEBUG] Correlation data: {len(data) if data else 0} items", flush=True) + + if not data or len(data) < 1: + self._show_no_data(self.correlation_canvas) + return + + time_buckets = sorted(list(set(row['time_bucket'] for row in data))) + sound_types = sorted(list(set(row['sound_type'] for row in data))) + + if len(time_buckets) < 2 or len(sound_types) < 2: + self._show_no_data(self.correlation_canvas) + return + + n_times = len(time_buckets) + n_sounds = len(sound_types) + data_matrix = np.zeros((n_times, n_sounds)) + + time_idx = {t: i for i, t in enumerate(time_buckets)} + sound_idx = {s: i for i, s in enumerate(sound_types)} + + for row in data: + t_idx = time_idx[row['time_bucket']] + s_idx = sound_idx[row['sound_type']] + data_matrix[t_idx, s_idx] = row['detection_count'] + + corr_matrix = np.corrcoef(data_matrix.T) + corr_matrix = np.nan_to_num(corr_matrix, nan=0.0) + + self.correlation_canvas.figure.clear() + ax = self.correlation_canvas.figure.add_subplot(111) + + im = ax.imshow(corr_matrix, cmap='Blues', aspect='auto', vmin=-1, vmax=1) + + ax.set_xticks(range(len(sound_types))) + ax.set_yticks(range(len(sound_types))) + ax.set_xticklabels(sound_types, rotation=45, ha='right', fontsize=7) + ax.set_yticklabels(sound_types, fontsize=7) + + for i in range(len(sound_types)): + for j in range(len(sound_types)): + value = corr_matrix[i, j] + text_color = 'white' if value > 0.5 else 'black' + ax.text( + j, i, f'{value:.2f}', + ha='center', va='center', + color=text_color, fontsize=6, fontweight='bold' + ) + + cbar = self.correlation_canvas.figure.colorbar( + im, ax=ax, fraction=0.046, pad=0.04 + ) + cbar.set_label( + 'Correlation Strength', + rotation=270, labelpad=15, fontsize=8 + ) + + ax.set_title( + 'Sound Type Correlations\nDarker = Stronger Co-occurrence', + fontsize=9, fontweight='bold', pad=10 + ) + + self.correlation_canvas.figure.tight_layout() + self.correlation_canvas.draw() + print("[DEBUG] Correlation chart drawn successfully", flush=True) + + except Exception as e: + print(f"[ERROR] Correlation chart error: {e}", flush=True) + import traceback + traceback.print_exc() + self._show_no_data(self.correlation_canvas) diff --git a/GUI/src/vast/views/sound/sound_graphic.py b/GUI/src/vast/views/sound/sound_graphic.py new file mode 100644 index 000000000..59027cbe1 --- /dev/null +++ b/GUI/src/vast/views/sound/sound_graphic.py @@ -0,0 +1,38 @@ +from PyQt6.QtWidgets import QWidget, QVBoxLayout, QLabel, QHBoxLayout, QPushButton, QSizePolicy +from PyQt6.QtWebEngineWidgets import QWebEngineView +from PyQt6.QtCore import QUrl + +class SoundGraphic(QWidget): + def __init__(self, api, parent=None): + super().__init__(parent) + self.api = api + self.setup_ui() + + def setup_ui(self): + layout = QVBoxLayout(self) + + header = QHBoxLayout() + title = QLabel("🌿 Ultrasonic Plant Predictions Dashboard") + header.addWidget(title) + header.addStretch() + refresh_btn = QPushButton("🔄 Refresh") + refresh_btn.clicked.connect(self.refresh_dashboard) + header.addWidget(refresh_btn) + + layout.addLayout(header) + + self.web_view = QWebEngineView() + self.web_view.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) + self.web_view.setUrl(QUrl("http://grafana:3000/d/ultrasonic-predictions")) + layout.addWidget(self.web_view) + + self.status = QLabel("Loading...") + layout.addWidget(self.status) + + self.web_view.loadFinished.connect(self.on_load_finished) + + def refresh_dashboard(self): + self.web_view.reload() + + def on_load_finished(self, ok): + self.status.setText("Loaded" if ok else "Failed to load") diff --git a/GUI/src/vast/views/sound/sound_main.py b/GUI/src/vast/views/sound/sound_main.py new file mode 100644 index 000000000..d2d462317 --- /dev/null +++ b/GUI/src/vast/views/sound/sound_main.py @@ -0,0 +1,13 @@ +from PyQt6.QtWidgets import QApplication +from sound_view import SoundView +from dashboard_api import DashboardApi +import sys + +if __name__ == "__main__": + app = QApplication(sys.argv) + + api = DashboardApi() + win = SoundView(api=api) + win.show() + + sys.exit(app.exec()) diff --git a/GUI/src/vast/views/sound/sound_view.py b/GUI/src/vast/views/sound/sound_view.py index f543d8b0a..ac217c783 100644 --- a/GUI/src/vast/views/sound/sound_view.py +++ b/GUI/src/vast/views/sound/sound_view.py @@ -1,2000 +1,70 @@ -from PyQt6.QtWidgets import ( - QWidget, QVBoxLayout, QHBoxLayout, QLabel, QGridLayout, - QComboBox, QPushButton, QLineEdit, QDateEdit, - QFrame, QMessageBox, QTabWidget, QTableWidget, - QTableWidgetItem, QHeaderView, QAbstractItemView, - QStackedWidget, QSizePolicy, QCheckBox, QToolBar, QScrollArea -) -from PyQt6.QtCore import Qt, QDate, QUrl, QTimer, pyqtSignal, QSize -from PyQt6.QtGui import QPixmap, QColor, QCursor, QPainter, QFont -from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput -from PyQt6.QtWebEngineWidgets import QWebEngineView -from dashboard_api import DashboardApi -import matplotlib.pyplot as plt -from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas -from matplotlib.figure import Figure -import numpy as np -from datetime import datetime, timedelta -from vast.dashboard_api import DashboardApi -import requests -import os -import math -import tempfile +from PyQt6.QtWidgets import QWidget, QVBoxLayout, QSizePolicy, QTabWidget +from .recordings_tab import RecordingsTab +from .sound_graphic import SoundGraphic +from .sound_analytics_view import SoundAnalyticsView -MINIO_BASE = os.getenv("MINIO_PUBLIC_BASE", "http://minio-hot:9000") - -def normalize_minio_url(url: str) -> str: - if not url: - return "" - if url.startswith("http://") or url.startswith("https://"): - return url - url = url.lstrip("/") - if url.startswith("sounds/"): - url = "sound/" + url - return f"{MINIO_BASE.rstrip('/')}/{url}" - - -# ========================================================== -# Audio Waveform Visualizer -# ========================================================== -class AudioWaveform(QWidget): - def __init__(self, parent=None): - super().__init__(parent) - self.setMinimumHeight(180) - self.setMaximumHeight(220) - self.bars = [] - self.animation_offset = 0 - self.is_playing = False - - self.timer = QTimer(self) - self.timer.timeout.connect(self.animate) - - self.setStyleSheet(""" - background: qlineargradient(x1:0, y1:0, x2:1, y2:1, - stop:0 #0f0c29, stop:0.5 #302b63, stop:1 #24243e); - border: none; - border-radius: 12px; - """) - - self.default_text = "🎵 Press Play to Visualize Audio 🎵" - - def start_animation(self): - self.is_playing = True - self.bars = [0.2 + (i % 5) * 0.15 for i in range(100)] - self.timer.start(40) - - def stop_animation(self): - self.is_playing = False - self.timer.stop() - self.bars = [] - self.update() - - def animate(self): - if not self.is_playing: - return - - self.animation_offset = (self.animation_offset + 2) % 360 - - for i in range(len(self.bars)): - wave1 = math.sin((self.animation_offset + i * 8) * math.pi / 180) - wave2 = math.cos((self.animation_offset + i * 12) * math.pi / 180) - self.bars[i] = 0.25 + abs(wave1 * 0.35) + abs(wave2 * 0.25) - - self.update() - - def paintEvent(self, event): - painter = QPainter(self) - painter.setRenderHint(QPainter.RenderHint.Antialiasing) - - width = self.width() - height = self.height() - - if not self.bars: - painter.setPen(QColor(150, 180, 230, 200)) - font = painter.font() - font.setPointSize(14) - font.setBold(True) - painter.setFont(font) - painter.drawText(0, 0, width, height, - Qt.AlignmentFlag.AlignCenter, - self.default_text) - return - - bar_width = width / len(self.bars) - center_y = height / 2 - - gradient_colors = [ - (QColor(102, 126, 234), QColor(118, 75, 162)), - (QColor(67, 233, 123), QColor(56, 249, 215)), - (QColor(251, 200, 212), QColor(151, 149, 240)), - (QColor(250, 208, 196), QColor(255, 209, 255)) - ] - - for i, amplitude in enumerate(self.bars): - x = i * bar_width - bar_height = amplitude * (height - 30) - y_top = center_y - bar_height / 2 - y_bottom = center_y + bar_height / 2 - - gradient_idx = (i // 25) % len(gradient_colors) - color1, color2 = gradient_colors[gradient_idx] - - from PyQt6.QtGui import QLinearGradient - gradient = QLinearGradient(x, y_top, x, y_bottom) - gradient.setColorAt(0, color1) - gradient.setColorAt(1, color2) - - painter.setPen(Qt.PenStyle.NoPen) - painter.setBrush(gradient) - - bar_rect_width = max(2, bar_width - 1.5) - painter.drawRoundedRect( - int(x + 0.75), int(y_top), - int(bar_rect_width), int(bar_height), - 3, 3 - ) - - glow = QColor(255, 255, 255, 30) - painter.setBrush(glow) - painter.drawRoundedRect( - int(x + bar_width * 0.2), int(y_top + 2), - int(bar_rect_width * 0.3), int(bar_height * 0.4), - 2, 2 - ) - - -# ========================================================== -# Microphone Button Widget -# ========================================================== -class MicrophoneButton(QPushButton): - def __init__(self, mic_id: str, mic_name: str, mic_type: str, parent=None): - super().__init__(parent) - self.mic_id = mic_id - self.mic_name = mic_name - self.mic_type = mic_type - self.is_selected = False - - if mic_type == "audio": - self.setFixedSize(70, 70) - self.shape_style = "border-radius: 35px;" - else: - self.setFixedSize(70, 70) - self.shape_style = "border-radius: 8px;" - - self.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) - - self.base_color = "#000000" - self.selected_color = "#0078d4" - self.hover_color = "#222222" - self.disabled_color = "#888888" - - self.update_style() - self.setText(f"{mic_id.upper()}") - self.setToolTip(f"{mic_name}
Type: {mic_type}
Click to select") - - def update_style(self): - if not self.isEnabled(): - color = self.disabled_color - border = "#555" - elif self.is_selected: - color = self.selected_color - border = "#1e90ff" - else: - color = self.base_color - border = "white" - - self.setStyleSheet(f""" - QPushButton {{ - background-color: {color}; - color: white; - border: 3px solid {border}; - {self.shape_style} - font-size: 15px; - font-weight: bold; - padding: 4px; - }} - QPushButton:hover {{ - background-color: {self.hover_color}; - }} - """) - - def set_selected(self, selected: bool): - self.is_selected = selected - self.update_style() - - def set_disabled_state(self, disabled: bool): - self.setEnabled(not disabled) - self.update_style() - - -# ========================================================== -# Interactive Map with Image -# ========================================================== -class ImageMapView(QWidget): - def __init__(self, parent=None, api=None): - super().__init__(parent) - self.api = api - self.main_layout = QVBoxLayout(self) - self.main_layout.setContentsMargins(20, 20, 20, 20) - self.main_layout.setSpacing(15) - - self.selected_mics = [] - self.selected_type = None - self.mic_buttons = {} - - self.stacked_widget = QStackedWidget() - - self.map_page = QWidget() - layout = QVBoxLayout(self.map_page) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(15) - - control_panel = QFrame() - control_panel.setStyleSheet(""" - QFrame { - background-color: white; - border: 2px solid #d1d5da; - border-radius: 8px; - padding: 10px; - } - """) - control_layout = QHBoxLayout(control_panel) - - self.selection_label = QLabel("Select microphones to view recordings") - self.selection_label.setStyleSheet("font-weight: bold; color: #333;") - - self.view_button = QPushButton("View Selected Recordings") - self.view_button.setEnabled(False) - self.view_button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) - self.view_button.setStyleSheet(""" - QPushButton { - background-color: #28a745; - color: white; - border-radius: 6px; - padding: 10px 20px; - font-weight: bold; - } - QPushButton:hover:enabled { - background-color: #218838; - } - QPushButton:disabled { - background-color: #CCCCCC; - } - """) - self.view_button.clicked.connect(self.view_selected_recordings) - - self.clear_button = QPushButton("✕ Clear Selection") - self.clear_button.setEnabled(False) - self.clear_button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) - self.clear_button.setStyleSheet(""" - QPushButton { - background-color: #dc3545; - color: white; - border-radius: 6px; - padding: 10px 20px; - font-weight: bold; - } - QPushButton:hover:enabled { - background-color: #c82333; - } - QPushButton:disabled { - background-color: #CCCCCC; - } - """) - self.clear_button.clicked.connect(self.clear_selection) - - control_layout.addWidget(self.selection_label) - control_layout.addStretch() - control_layout.addWidget(self.clear_button) - control_layout.addWidget(self.view_button) - - subtitle = QLabel("Click on microphones to select them (same type only)") - subtitle.setAlignment(Qt.AlignmentFlag.AlignCenter) - subtitle.setStyleSheet("font-size: 14px; color: #666; padding: 5px;") - - self.map_container = QWidget() - self.map_container.setMinimumSize(800, 600) - self.map_container.setStyleSheet(""" - background-color: #e8f4f8; - border: 3px solid #4A90E2; - border-radius: 15px; - """) - - self.background_label = QLabel(self.map_container) - self.background_label.setGeometry(0, 0, 800, 600) - self.background_label.setAlignment(Qt.AlignmentFlag.AlignCenter) - - self._load_map_image() - - self.microphones = [ - {"id": "MIC-01", "name": "Environment Mic", "type": "audio", "position": (200, 150)}, - {"id": "MIC-02", "name": "Plant Ultrasound", "type": "ultrasound", "position": (500, 300)}, - {"id": "MIC-03", "name": "Environment Mic", "type": "audio", "position": (350, 450)} - ] - - for mic in self.microphones: - btn = MicrophoneButton(mic["id"], mic["name"], mic["type"], self.map_container) - btn.move(mic["position"][0], mic["position"][1]) - btn.clicked.connect(lambda checked, m=mic, b=btn: self.on_microphone_clicked(m, b)) - self.mic_buttons[mic["id"]] = btn - - legend = QLabel("🎤 circle = Audio Sensor • 🔊 square = Ultrasound Sensor") - legend.setAlignment(Qt.AlignmentFlag.AlignCenter) - legend.setStyleSheet(""" - font-size: 14px; - font-weight: 500; - padding: 10px; - background-color: white; - border: 2px solid #ddd; - border-radius: 8px; - color: #333; - """) - - layout.addWidget(control_panel) - layout.addWidget(subtitle) - layout.addWidget(self.map_container, 1) - layout.addWidget(legend) - - self.stacked_widget.addWidget(self.map_page) - self.recordings_page = None - - self.main_layout.addWidget(self.stacked_widget) - - def _load_map_image(self): - possible_paths = [ - "map_background.png", - "./map_background.png", - "../map_background.png", - os.path.join(os.getcwd(), "map_background.png"), - os.path.join(os.path.dirname(__file__), "map_background.png") - ] - - image_loaded = False - for path in possible_paths: - if os.path.exists(path): - pixmap = QPixmap(path) - if not pixmap.isNull(): - scaled = pixmap.scaled(800, 600, Qt.AspectRatioMode.KeepAspectRatio, - Qt.TransformationMode.SmoothTransformation) - self.background_label.setPixmap(scaled) - image_loaded = True - break - - if not image_loaded: - self.background_label.setStyleSheet("font-size: 16px; color: #888; background-color: #d0e8f2;") - self.background_label.setText("Sensor Locations\n\n(Map image not found)") - - def on_microphone_clicked(self, mic_data, button): - mic_id = mic_data["id"] - mic_type = mic_data["type"] - - if mic_id in self.selected_mics: - self.selected_mics.remove(mic_id) - button.set_selected(False) - - if not self.selected_mics: - self.selected_type = None - self.enable_all_buttons() - - self.update_selection_display() - return - - if self.selected_type is None: - self.selected_type = mic_type - self.disable_other_type_buttons(mic_type) - - if mic_type == self.selected_type: - self.selected_mics.append(mic_id) - button.set_selected(True) - self.update_selection_display() - - def disable_other_type_buttons(self, allowed_type): - for mic in self.microphones: - if mic["type"] != allowed_type: - self.mic_buttons[mic["id"]].set_disabled_state(True) - - def enable_all_buttons(self): - for btn in self.mic_buttons.values(): - btn.set_disabled_state(False) - - def clear_selection(self): - self.selected_mics = [] - self.selected_type = None - for btn in self.mic_buttons.values(): - btn.set_selected(False) - btn.set_disabled_state(False) - self.update_selection_display() - - def update_selection_display(self): - count = len(self.selected_mics) - if count == 0: - self.selection_label.setText("Select microphones to view recordings") - self.view_button.setEnabled(False) - self.clear_button.setEnabled(False) - else: - type_text = "Audio" if self.selected_type == "audio" else "Ultrasound" - self.selection_label.setText( - f"Selected {count} {type_text} microphone(s): {', '.join([m.upper() for m in self.selected_mics])}" - ) - self.view_button.setEnabled(True) - self.clear_button.setEnabled(True) - - def view_selected_recordings(self): - if not self.selected_mics: - return - - selected_mic_data = [mic for mic in self.microphones if mic["id"] in self.selected_mics] - - if self.recordings_page: - self.stacked_widget.removeWidget(self.recordings_page) - self.recordings_page.deleteLater() - - self.recordings_page = QWidget() - recordings_layout = QVBoxLayout(self.recordings_page) - recordings_layout.setContentsMargins(0, 0, 0, 0) - recordings_layout.setSpacing(0) - - header_container = QWidget() - color = "#4A90E2" if self.selected_type == "audio" else "#50C878" - header_container.setStyleSheet(f"background-color: {color};") - header_layout = QHBoxLayout(header_container) - header_layout.setContentsMargins(10, 10, 10, 10) - - back_button = QPushButton("← Back to Map") - back_button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) - back_button.setStyleSheet(""" - QPushButton { - background-color: rgba(255, 255, 255, 0.2); - color: white; - border: 2px solid white; - border-radius: 6px; - padding: 8px 16px; - font-weight: bold; - } - QPushButton:hover { background-color: rgba(255, 255, 255, 0.3); } - """) - back_button.clicked.connect(self.show_map) - - mic_names = ", ".join([m["name"] for m in selected_mic_data]) - header = QLabel(f"Recordings: {mic_names}") - header.setAlignment(Qt.AlignmentFlag.AlignCenter) - header.setStyleSheet("font-size: 20px; font-weight: bold; color: white;") - - header_layout.addWidget(back_button) - header_layout.addWidget(header, 1) - header_layout.addStretch() - - type_text = "AUDIO" if self.selected_type == "audio" else "ULTRASOUND" - mic_ids = ", ".join([m["id"].upper() for m in selected_mic_data]) - subtitle = QLabel(f"Type: {type_text} • Microphones: {mic_ids}") - subtitle.setAlignment(Qt.AlignmentFlag.AlignCenter) - subtitle.setStyleSheet(""" - font-size: 13px; color: white; padding: 5px; - background-color: rgba(0, 0, 0, 0.2); - """) - - recordings_layout.addWidget(header_container) - recordings_layout.addWidget(subtitle) - - mic_ids_list = [m["id"] for m in selected_mic_data] - sound_tab = RecordingsTab( - mic_ids=mic_ids_list, - recording_type=self.selected_type, - parent=self, - api=self.api, - ) - recordings_layout.addWidget(sound_tab) - - self.stacked_widget.addWidget(self.recordings_page) - self.stacked_widget.setCurrentWidget(self.recordings_page) - - def show_map(self): - self.stacked_widget.setCurrentWidget(self.map_page) - - -# ========================================================== -# Recordings Tab -# ========================================================== -class RecordingsTab(QWidget): - def __init__(self, mic_ids=None, recording_type="audio", parent=None, api=None): - super().__init__(parent) - self.mic_ids = mic_ids if mic_ids else [] - self.recording_type = recording_type - self.api = api - - if recording_type == "ultrasound": - self.api_url = "http://db_api_service:8001/api/files/plant-predictions/" - else: - self.api_url = "http://db_api_service:8001/api/files/audio-aggregates/" - - layout = QVBoxLayout(self) - layout.setContentsMargins(20, 20, 20, 20) - layout.setSpacing(15) - - filter_frame = self._create_filter_frame() - - list_label = QLabel("Available Recordings") - list_label.setStyleSheet("font-size: 16px; font-weight: bold; color: #333; padding: 5px;") - - self.file_table = self._create_table() - - waveform_container = self._create_waveform_container() - - self.status_label = QLabel("Ready") - self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter) - self.status_label.setStyleSheet(""" - font-size: 13px; color: #666; padding: 8px; - background-color: #f6f8fa; border-radius: 6px; border: 1px solid #d1d5da; - """) - - self.player = QMediaPlayer() - self.audio_output = QAudioOutput() - self.audio_output.setVolume(1.0) - self.player.setAudioOutput(self.audio_output) - self.player.playbackStateChanged.connect(self.on_playback_state_changed) - self._current_temp_file = None - self._current_play_btn = None - self._current_stop_btn = None - - layout.addWidget(filter_frame) - layout.addWidget(list_label) - layout.addWidget(self.file_table, 1) - - if self.recording_type == "audio": - layout.addWidget(waveform_container) - - layout.addWidget(self.status_label) - - self.refresh_button.clicked.connect(self.update_list) - self.update_list() - - def _create_filter_frame(self): - filter_frame = QFrame() - filter_frame.setStyleSheet(""" - QFrame { background-color: #ffffff; border-radius: 12px; padding: 15px; } - """) - filters_layout = QVBoxLayout(filter_frame) - filters_layout.setSpacing(12) - - filter_row = QHBoxLayout() - filter_row.setSpacing(8) - filter_row.setContentsMargins(0, 0, 0, 0) - - type_label = QLabel("Type:") - type_label.setStyleSheet("font-weight: bold; color: #333; font-size: 11px;") - filter_row.addWidget(type_label) - - self.noise_filter = QComboBox() - self.noise_filter.setMaximumWidth(180) - self.noise_filter.setStyleSheet(""" - QComboBox { - padding: 6px 10px; - border: 1px solid #d1d5da; - border-radius: 4px; - background: white; - font-size: 12px; - } - QComboBox:hover { border: 1px solid #4A90E2; } - """) - - if self.recording_type == "ultrasound": - self.noise_filter.addItems([ - "All signals", "Drought-stressed plant", - "Empty Pot", "Greenhouse Noises" - ]) - else: - self.noise_filter.addItems([ - "All types", "predatory_animals", "non_predatory_animals", - "birds", "fire", "footsteps", "insects", "screaming", - "shotgun", "stormy_weather", "streaming_water", "vehicle", "Other" - ]) - - filter_row.addWidget(self.noise_filter) - - date_label = QLabel(" From:") - date_label.setStyleSheet("font-weight: bold; color: #333; font-size: 11px;") - filter_row.addWidget(date_label) - - today = QDate.currentDate() - first_day = QDate(today.year(), today.month(), 1) - - self.date_from = QDateEdit() - self.date_from.setCalendarPopup(True) - self.date_from.setDate(first_day) - self.date_from.setMaximumWidth(120) - self.date_from.setStyleSheet(""" - QDateEdit { - padding: 6px 8px; - border: 1px solid #d1d5da; - border-radius: 4px; - background: white; - font-size: 12px; - } - """) - filter_row.addWidget(self.date_from) - - filter_row.addWidget(QLabel("→")) - - self.date_to = QDateEdit() - self.date_to.setCalendarPopup(True) - self.date_to.setDate(today) - self.date_to.setMaximumWidth(120) - self.date_to.setStyleSheet(self.date_from.styleSheet()) - filter_row.addWidget(self.date_to) - - self.search_box = QLineEdit() - self.search_box.setPlaceholderText("Search filename...") - self.search_box.setMaximumWidth(200) - self.search_box.setStyleSheet(""" - QLineEdit { - padding: 6px 10px; - border: 1px solid #d1d5da; - border-radius: 4px; - background: white; - font-size: 12px; - } - QLineEdit:focus { border: 1px solid #4A90E2; } - """) - filter_row.addWidget(self.search_box) - - filter_row.addStretch() - - filter_row.addWidget(QLabel("sort by:")) - self.sort_by = QComboBox() - self.sort_by.addItems(["date", "name", "device"]) - self.sort_by.setMaximumWidth(130) - self.sort_by.setStyleSheet(""" - QComboBox { - padding: 6px 10px; - border: 1px solid #d1d5da; - border-radius: 4px; - background: white; - font-size: 12px; - } - """) - filter_row.addWidget(self.sort_by) - - self.refresh_button = QPushButton("🔄 Refresh") - self.refresh_button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) - self.refresh_button.setStyleSheet(""" - QPushButton { - background-color: #4A90E2; - color: white; - border-radius: 6px; - padding: 8px 16px; - font-weight: bold; - font-size: 12px; - } - QPushButton:hover { background-color: #357ABD; } - """) - filter_row.addWidget(self.refresh_button) - - filters_layout.addLayout(filter_row) - - return filter_frame - - def _create_table(self): - table = QTableWidget() - - if self.recording_type == "ultrasound": - table.setColumnCount(6) - table.setHorizontalHeaderLabels([ - "File", "Device", "Predicted Label", "Confidence", "Watering Status", "Format" - ]) - else: - table.setColumnCount(6) - table.setHorizontalHeaderLabels([ - "File", "Device", "Predicted Label", "Probability", "Format", "Actions" - ]) - - header = table.horizontalHeader() - header.setStretchLastSection(False) - for i in range(table.columnCount()): - header.setSectionResizeMode(i, QHeaderView.ResizeMode.Stretch) - - table.setStyleSheet(""" - QTableWidget { - background: #ffffff; - border: 2px solid #d1d5da; - border-radius: 10px; - gridline-color: #e1e4e8; - font-size: 14px; - } - QTableWidget::item { padding: 8px; } - QTableWidget::item:hover { background-color: #f6f8fa; } - QTableWidget::item:selected { background-color: #d6eaff; color: #0366d6; } - QHeaderView::section { - background-color: #f6f8fa; - padding: 10px; - border: 1px solid #d1d5da; - font-weight: bold; - color: #24292e; - } - """) - - table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows) - table.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection) - table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers) - table.verticalHeader().setVisible(False) - - return table - - def _create_waveform_container(self): - waveform_container = QFrame() - waveform_container.setStyleSheet(""" - QFrame { - background-color: transparent; - border: none; - padding: 0px; - } - """) - - waveform_layout = QVBoxLayout(waveform_container) - waveform_layout.setSpacing(5) - - self.waveform = AudioWaveform() - self.waveform.setMinimumHeight(100) - self.waveform.setMaximumHeight(120) - - waveform_layout.addWidget(self.waveform) - - return waveform_container - - def on_playback_state_changed(self, state): - if state == QMediaPlayer.PlaybackState.PlayingState: - self.waveform.start_animation() - elif state == QMediaPlayer.PlaybackState.StoppedState: - self.waveform.stop_animation() - if self.status_label.text().startswith("Playing:"): - self.status_label.setText("Finished") - if hasattr(self, '_current_play_btn') and self._current_play_btn: - self._reset_button_pair(self._current_play_btn, self._current_stop_btn) - self._current_play_btn = None - self._current_stop_btn = None - - def _map_ultrasound_label(self, raw: str) -> str: - if not raw: - return "Unknown" - lower = raw.lower() - if "tomato" in lower or "tobacco" in lower: - return "Drought-stressed plant" - return raw - - def update_list(self): - self.file_table.setRowCount(0) - self.file_table.verticalHeader().setDefaultSectionSize(60) - self.status_label.setText("Loading...") - - params = { - "date_from": self.date_from.date().toString("yyyy-MM-dd"), - "date_to": self.date_to.date().toString("yyyy-MM-dd"), - "search": self.search_box.text().strip(), - "sort_by": self.sort_by.currentText(), - "limit": 100 - } - - filter_value = self.noise_filter.currentText() - if self.recording_type == "ultrasound": - if filter_value in ("Empty Pot", "Greenhouse Noises"): - params["predicted_class"] = filter_value - else: - if filter_value not in ("All types", "All signals"): - params["type"] = filter_value - - if self.mic_ids: - params["device_ids"] = ",".join(self.mic_ids) - - try: - # Check if API is available and authenticated - if not self.api or not hasattr(self.api, 'http'): - self.status_label.setText("⚠ API connection not available") - QMessageBox.warning( - self, - "Authentication Required", - "Please login first to access recordings." - ) - return - - # Use the authenticated session - response = self.api.http.get(self.api_url, params=params, timeout=10) - response.raise_for_status() - data = response.json() - - print(f"[DEBUG] Successfully fetched {len(data)} records from {self.api_url}") - for f in data: - row = self.file_table.rowCount() - self.file_table.insertRow(row) - self.file_table.setRowHeight(row, 60) - - filename = f.get("filename") or f.get("file", "") - is_compressed = f.get("is_compressed", False) - - text_color = QColor("#888888") if is_compressed else QColor("#000000") - - if self.recording_type == "ultrasound": - device_id = f.get("device_id", "N/A") - pred_class_raw = f.get("predicted_class", "Unknown") - pred_class = self._map_ultrasound_label(pred_class_raw) - confidence = f.get("confidence", 0) - watering_status = f.get("watering_status", "N/A") - url = normalize_minio_url(f.get("url", "")) - - format_str = "OPUS (Compressed)" if is_compressed else "WAV (Original)" - - item0 = QTableWidgetItem(filename) - item0.setForeground(text_color) - self.file_table.setItem(row, 0, item0) - - item1 = QTableWidgetItem(device_id) - item1.setForeground(text_color) - self.file_table.setItem(row, 1, item1) - - item2 = QTableWidgetItem(pred_class) - item2.setForeground(text_color) - self.file_table.setItem(row, 2, item2) - - item3 = QTableWidgetItem(f"{confidence:.2%}") - item3.setForeground(text_color) - self.file_table.setItem(row, 3, item3) - - item4 = QTableWidgetItem(watering_status) - item4.setForeground(text_color) - self.file_table.setItem(row, 4, item4) - - item5 = QTableWidgetItem(format_str) - item5.setForeground(text_color) - self.file_table.setItem(row, 5, item5) - else: - device_id = f.get("device_id", "N/A") - label = f.get("predicted_label", "Unknown") - prob = f.get("probability", 0) - url = normalize_minio_url(f.get("url", "")) - - format_str = "OPUS (Compressed)" if is_compressed else "WAV (Original)" - - item0 = QTableWidgetItem(filename) - item0.setForeground(text_color) - self.file_table.setItem(row, 0, item0) - - item1 = QTableWidgetItem(device_id) - item1.setForeground(text_color) - self.file_table.setItem(row, 1, item1) - - item2 = QTableWidgetItem(label) - item2.setForeground(text_color) - self.file_table.setItem(row, 2, item2) - - item3 = QTableWidgetItem(f"{prob:.2%}") - item3.setForeground(text_color) - self.file_table.setItem(row, 3, item3) - - item4 = QTableWidgetItem(format_str) - item4.setForeground(text_color) - self.file_table.setItem(row, 4, item4) - - if self.recording_type == "audio": - control_widget = QWidget() - control_layout = QHBoxLayout(control_widget) - control_layout.setContentsMargins(2, 2, 2, 2) - control_layout.setSpacing(6) - - play_btn = QPushButton("▶") - play_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) - play_btn.setFixedSize(35, 30) - - if is_compressed: - play_btn.setStyleSheet(""" - QPushButton { - background-color: #888888; - color: white; - border-radius: 4px; - font-weight: bold; - } - QPushButton:hover:enabled { background-color: #666666; } - QPushButton:disabled { background-color: #cccccc; color: #888888; } - """) - play_btn.setToolTip("Compressed OPUS file - may have compatibility issues") - else: - play_btn.setStyleSheet(""" - QPushButton { - background-color: #0078d4; - color: white; - border-radius: 4px; - font-weight: bold; - } - QPushButton:hover:enabled { background-color: #005fa3; } - QPushButton:disabled { background-color: #cccccc; color: #888888; } - """) - - stop_btn = QPushButton("⏹") - stop_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) - stop_btn.setFixedSize(35, 30) - stop_btn.setEnabled(False) - stop_btn.setStyleSheet(""" - QPushButton { - background-color: #6c757d; - color: white; - border-radius: 4px; - font-weight: bold; - } - QPushButton:disabled { background-color: #b0b0b0; } - QPushButton:hover:enabled { background-color: #c82333; } - """) - - play_btn.setProperty("row", row) - stop_btn.setProperty("row", row) - - play_btn.clicked.connect( - lambda checked=False, u=url, fname=filename, pb=play_btn, sb=stop_btn, compressed=is_compressed: - self.play_row_audio(u, fname, pb, sb, compressed) - ) - stop_btn.clicked.connect( - lambda checked=False, pb=play_btn, sb=stop_btn: - self.stop_row_audio(pb, sb) - ) - - control_layout.addWidget(play_btn) - control_layout.addWidget(stop_btn) - - self.file_table.setCellWidget(row, 5, control_widget) - - if self.file_table.rowCount() == 0: - self.file_table.insertRow(0) - empty_item = QTableWidgetItem("No recordings found") - empty_item.setForeground(QColor("#999")) - self.file_table.setItem(0, 0, empty_item) - self.file_table.setSpan(0, 0, 1, 6) - - self.status_label.setText(f"✓ Loaded {len(data)} recordings") - - except requests.exceptions.HTTPError as e: - if e.response.status_code == 401: - self.status_label.setText("⚠ Authentication required") - QMessageBox.warning(self, "Authentication Error", - "API requires authentication. Please check your credentials.") - else: - self.status_label.setText(f"⚠ HTTP Error {e.response.status_code}") - QMessageBox.warning(self, "HTTP Error", - f"Server returned error {e.response.status_code}:\n{str(e)}") - except requests.exceptions.Timeout: - self.status_label.setText("⚠ Request timeout") - QMessageBox.warning(self, "Timeout", "Request timed out. Please try again.") - except requests.exceptions.ConnectionError: - self.status_label.setText("⚠ Connection error") - QMessageBox.warning(self, "Connection Error", - "Could not connect to server. Check your connection.") - except Exception as e: - self.status_label.setText("⚠ Error loading data") - QMessageBox.warning(self, "Error", f"Failed to load recordings:\n{str(e)}") - - def play_row_audio(self, url, filename, play_btn, stop_btn, is_compressed=False): - if not url: - QMessageBox.warning(self, "No URL", "Audio file URL not available") - return - - self.player.stop() - self.waveform.stop_animation() - - try: - if self._current_temp_file: - if os.path.exists(self._current_temp_file): - os.remove(self._current_temp_file) - except Exception: - pass - self._current_temp_file = None - - playback_url = url - if url.startswith("http://localhost") or url.startswith("http://127.0.0.1"): - parts = url.split("/", 3) - if len(parts) > 3: - path = parts[3] - playback_url = f"http://minio-hot:9000/{path}" - - if is_compressed: - reply = QMessageBox.question( - self, - "Compressed File", - "This is a compressed OPUS file. Playback may not work properly.\n\nContinue anyway?", - QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No - ) - if reply == QMessageBox.StandardButton.No: - return - - try: - session = self.api.http if (self.api and getattr(self.api, "http", None)) else requests - resp = session.get(playback_url, timeout=15) - resp.raise_for_status() - suffix = ".ogg" if is_compressed else ".wav" - tmp = tempfile.NamedTemporaryFile(delete=False, suffix=suffix) - tmp.write(resp.content) - tmp.flush() - tmp_path = tmp.name - tmp.close() - self._current_temp_file = tmp_path - except requests.exceptions.RequestException as e: - self.status_label.setText("⚠ Unable to download file") - QMessageBox.warning(self, "Download Error", f"Could not download audio file:\n{e}") - return - except Exception as e: - self.status_label.setText("⚠ Error downloading file") - QMessageBox.warning(self, "Error", f"Failed to download audio file:\n{e}") - return - - try: - self._reset_all_buttons() - - play_btn.setEnabled(False) - play_btn.setStyleSheet(""" - QPushButton { - background-color: #888888; - color: white; - border-radius: 4px; - font-weight: bold; - } - """) - - stop_btn.setEnabled(True) - stop_btn.setStyleSheet(""" - QPushButton { - background-color: #dc3545; - color: white; - border-radius: 4px; - font-weight: bold; - } - QPushButton:hover { background-color: #c82333; } - """) - - self.player.setSource(QUrl.fromLocalFile(self._current_temp_file)) - self.player.play() - self.waveform.start_animation() - self.status_label.setText(f"Playing: {filename}") - - self._current_play_btn = play_btn - self._current_stop_btn = stop_btn - - except Exception as e: - self.status_label.setText("⚠ Playback error") - QMessageBox.warning(self, "Playback Error", f"Playback failed:\n{e}") - self._reset_all_buttons() - - def stop_row_audio(self, play_btn, stop_btn): - self.player.stop() - self.waveform.stop_animation() - self.status_label.setText("⏹ Stopped") - self._reset_button_pair(play_btn, stop_btn) - - def _reset_button_pair(self, play_btn, stop_btn): - if play_btn.toolTip() and "Compressed" in play_btn.toolTip(): - play_btn.setStyleSheet(""" - QPushButton { - background-color: #888888; - color: white; - border-radius: 4px; - font-weight: bold; - } - QPushButton:hover:enabled { background-color: #666666; } - QPushButton:disabled { background-color: #cccccc; color: #888888; } - """) - else: - play_btn.setStyleSheet(""" - QPushButton { - background-color: #0078d4; - color: white; - border-radius: 4px; - font-weight: bold; - } - QPushButton:hover:enabled { background-color: #005fa3; } - QPushButton:disabled { background-color: #cccccc; color: #888888; } - """) - play_btn.setEnabled(True) - - stop_btn.setEnabled(False) - stop_btn.setStyleSheet(""" - QPushButton { - background-color: #6c757d; - color: white; - border-radius: 4px; - font-weight: bold; - } - QPushButton:disabled { background-color: #b0b0b0; } - QPushButton:hover:enabled { background-color: #c82333; } - """) - - def _reset_all_buttons(self): - if self.recording_type != "audio": - return - - actions_col = 5 - for row in range(self.file_table.rowCount()): - widget = self.file_table.cellWidget(row, actions_col) - if widget: - layout = widget.layout() - if layout and layout.count() >= 2: - play_btn = layout.itemAt(0).widget() - stop_btn = layout.itemAt(1).widget() - if play_btn and stop_btn and isinstance(play_btn, QPushButton): - self._reset_button_pair(play_btn, stop_btn) - - -# ========================================================== -# Sound Analytics View - NEW TAB from first document -# ========================================================== -class SoundAnalyticsView(QWidget): - """Sound detection dashboard with filtering by time range and sound type""" - - SOUND_TYPES = [ - "non_predatory_animals", - "predatory_animals", - "birds", - "fire", - "footsteps", - "insects", - "screaming", - "shotgun", - "stormy_weather", - "streaming_water", - "vehicle" - ] - - CYAN_PALETTE = [ - '#003366', '#004d99', '#0066cc', '#1a80e5', - '#3399ff', '#53A0E5', '#66b3ff', '#80ccff', - '#99e6ff', '#b3f0ff', '#ccf7ff' - ] - - PRIMARY_CYAN = '#53A0E5' - ACCENT_CYAN = '#3399ff' - - LIGHT_THEME = { - 'bg': '#f8f9fa', - 'card': '#ffffff', - 'text': '#333333', - 'border': '#e0e0e0', - 'primary': PRIMARY_CYAN, - 'accent': ACCENT_CYAN - } - - DARK_THEME = { - 'bg': '#1e1e1e', - 'card': '#2d2d2d', - 'text': '#e0e0e0', - 'border': '#444444', - 'primary': '#64B5F6', - 'accent': ACCENT_CYAN - } - - def __init__(self, api: DashboardApi, parent=None): - super().__init__(parent) - self.api = api - - # בדיקה ראשונית - print(f"[INIT] API object: {self.api}", flush=True) - print(f"[INIT] API has http: {hasattr(self.api, 'http')}", flush=True) - - # נסה לבדוק connection - try: - test_query = "SELECT 1 as test" - result = self.api.run_query(test_query) - print(f"[INIT] DB test result: {result}", flush=True) - except Exception as e: - print(f"[INIT] DB connection error: {e}", flush=True) - - self.current_time_range = 'day' - self.current_sound_types = [] - self.is_dark_theme = False - self.current_theme = self.LIGHT_THEME.copy() - - self.setWindowTitle("Sound Detection Analytics") - self.setMinimumSize(QSize(1350, 1000)) - - main_layout = QVBoxLayout() - main_layout.setContentsMargins(0, 0, 0, 0) - main_layout.setSpacing(0) - - content_frame = QFrame() - content_layout = QVBoxLayout() - content_layout.setContentsMargins(12, 12, 12, 12) - content_layout.setSpacing(12) - - filter_frame = QFrame() - filter_frame.setStyleSheet(""" - QFrame { - background-color: white; - border: 1px solid #e8e8e8; - border-radius: 8px; - } - """) - filter_frame.setMaximumHeight(450) - filter_layout = QVBoxLayout() - filter_layout.setContentsMargins(12, 10, 12, 10) - filter_layout.setSpacing(15) - - time_row = QHBoxLayout() - time_label = QLabel("Time Range:") - time_label.setFont(QFont("Arial", 10, QFont.Weight.Bold)) - time_row.addWidget(time_label) - self.time_filter = QComboBox() - self.time_filter.addItems(['1 Day', '1 Week', '1 Month']) - self.time_filter.setCurrentText('1 Day') - self.time_filter.currentTextChanged.connect(self._on_filter_changed) - self.time_filter.setMinimumWidth(140) - time_row.addWidget(self.time_filter) - time_row.addStretch() - filter_layout.addLayout(time_row) - - sound_header_row = QHBoxLayout() - sound_label = QLabel("Sound Types (select multiple):") - sound_label.setFont(QFont("Arial", 10, QFont.Weight.Bold)) - sound_header_row.addWidget(sound_label) - - self.selection_label = QLabel("All sounds selected") - self.selection_label.setStyleSheet(f"color: {self.PRIMARY_CYAN}; font-weight: bold;") - sound_header_row.addWidget(self.selection_label) - sound_header_row.addStretch() - - clear_btn = QPushButton("Clear All") - clear_btn.setMaximumWidth(100) - clear_btn.clicked.connect(self._clear_sound_selection) - sound_header_row.addWidget(clear_btn) - - apply_btn = QPushButton("Apply Filter") - apply_btn.setMaximumWidth(100) - apply_btn.setStyleSheet(f""" - QPushButton {{ - background-color: {self.PRIMARY_CYAN}; - color: white; - font-weight: bold; - }} - QPushButton:hover {{ - background-color: {self.CYAN_PALETTE[2]}; - }} - """) - apply_btn.clicked.connect(self._refresh_data) - sound_header_row.addWidget(apply_btn) - filter_layout.addLayout(sound_header_row) - - checkbox_container = QFrame() - checkbox_container.setObjectName("checkboxContainer") - checkbox_container.setStyleSheet(f""" - QFrame#checkboxContainer {{ - background-color: white; - border: 2px solid {self.PRIMARY_CYAN}; - border-radius: 6px; - max-height: 350px; - }} - """) - checkbox_layout = QGridLayout() - checkbox_layout.setSpacing(5) - checkbox_layout.setContentsMargins(10, 10, 10, 10) - - self.sound_checkboxes = {} - for idx, sound_name in enumerate(self.SOUND_TYPES): - checkbox = QCheckBox(sound_name) - checkbox.stateChanged.connect(self._on_sound_checkbox_changed) - self.sound_checkboxes[sound_name] = checkbox - row = idx // 3 - col = idx % 3 - checkbox_layout.addWidget(checkbox, row, col) - - checkbox_container.setLayout(checkbox_layout) - filter_layout.addWidget(checkbox_container) - filter_frame.setLayout(filter_layout) - content_layout.addWidget(filter_frame) - - calendar_frame = self._create_activity_calendar() - content_layout.addWidget(calendar_frame) - - grid = QGridLayout() - grid.setSpacing(12) - grid.setRowStretch(0, 1) - grid.setRowStretch(1, 1) - grid.setRowStretch(2, 1) - grid.setColumnStretch(0, 1) - grid.setColumnStretch(1, 1) - - def make_chart_frame(title, canvas): - frame = self._create_chart_frame(title, canvas) - frame.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) - frame.setMinimumHeight(320) - frame.setMaximumHeight(320) - return frame - - self.dist_canvas = self._create_canvas(figsize=(6, 5)) - grid.addWidget(make_chart_frame("Sound Distribution (Count)", self.dist_canvas), 0, 0) - - self.timeline_canvas = self._create_canvas(figsize=(6, 5)) - grid.addWidget(make_chart_frame("Detection Timeline", self.timeline_canvas), 0, 1) - - self.heatmap_canvas = self._create_canvas(figsize=(6, 5)) - grid.addWidget(make_chart_frame("Sound Heatmap - Activity Patterns", self.heatmap_canvas), 1, 0) - - self.correlation_canvas = self._create_canvas(figsize=(6, 5)) - grid.addWidget(make_chart_frame("Correlation Explorer", self.correlation_canvas), 1, 1) - - self.confidence_canvas = self._create_canvas(figsize=(6, 5)) - grid.addWidget(make_chart_frame("Model Health Monitor", self.confidence_canvas), 2, 0) - - stats_frame = self._create_stats_frame() - stats_frame.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) - stats_frame.setMinimumHeight(320) - stats_frame.setMaximumHeight(320) - grid.addWidget(stats_frame, 2, 1) - - content_layout.addLayout(grid, stretch=10) - content_frame.setLayout(content_layout) - - scroll_area = QScrollArea() - scroll_area.setWidgetResizable(True) - scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn) - scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) - scroll_area.setWidget(content_frame) - - main_layout.addWidget(scroll_area) - self.setLayout(main_layout) - - self.refresh_timer = QTimer() - self.refresh_timer.timeout.connect(self._refresh_data) - self.refresh_timer.start(30000) - - self._refresh_data() - - def _create_activity_calendar(self) -> QFrame: - frame = QFrame() - frame.setStyleSheet(""" - QFrame { - background-color: white; - border: 1px solid #e8e8e8; - border-radius: 8px; - } - """) - frame.setMaximumHeight(120) - - layout = QVBoxLayout() - layout.setContentsMargins(12, 12, 12, 12) - layout.setSpacing(8) - - title = QLabel("Activity Calendar (Last 30 Days)") - title.setFont(QFont("Arial", 10, QFont.Weight.Bold)) - layout.addWidget(title) - - calendar_grid = QHBoxLayout() - calendar_grid.setSpacing(2) - - today = datetime.now().date() - for i in range(30): - date = today - timedelta(days=29-i) - day_box = QFrame() - day_box.setMinimumSize(QSize(20, 20)) - day_box.setMaximumSize(QSize(20, 20)) - - intensity = np.random.rand() - color = self._get_intensity_color(intensity) - - day_box.setStyleSheet(f""" - QFrame {{ - background-color: {color}; - border: 1px solid #ddd; - border-radius: 2px; - }} - """) - - day_label = QLabel(str(date.day)) - day_label.setAlignment(Qt.AlignmentFlag.AlignCenter) - day_label.setFont(QFont("Arial", 6)) - day_layout = QVBoxLayout() - day_layout.setContentsMargins(0, 0, 0, 0) - day_layout.addWidget(day_label) - day_box.setLayout(day_layout) - - calendar_grid.addWidget(day_box) - - calendar_grid.addStretch() - layout.addLayout(calendar_grid) - frame.setLayout(layout) - return frame - - def _get_intensity_color(self, intensity: float) -> str: - if intensity < 0.2: - return self.CYAN_PALETTE[0] - elif intensity < 0.4: - return self.CYAN_PALETTE[2] - elif intensity < 0.6: - return self.CYAN_PALETTE[4] - elif intensity < 0.8: - return self.CYAN_PALETTE[7] - else: - return self.CYAN_PALETTE[10] - - def _create_canvas(self, figsize=(5.5, 4.5)): - canvas = FigureCanvas(Figure(figsize=figsize, dpi=90)) - canvas.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) - return canvas - - def _create_chart_frame(self, title: str, widget: QWidget) -> QFrame: - frame = QFrame() - frame.setStyleSheet(""" - QFrame { - background-color: white; - border: 1px solid #e8e8e8; - border-radius: 8px; - } - """) - - layout = QVBoxLayout() - layout.setContentsMargins(12, 12, 12, 12) - layout.setSpacing(8) - - title_label = QLabel(title) - title_label.setFont(QFont("Arial", 11, QFont.Weight.Bold)) - title_label.setStyleSheet(f"color: {self.PRIMARY_CYAN}; margin-bottom: 4px;") - layout.addWidget(title_label) - - layout.addWidget(widget, 1) - frame.setLayout(layout) - return frame - - def _create_stats_frame(self) -> QFrame: - frame = QFrame() - frame.setStyleSheet(""" - QFrame { - background-color: white; - border: 1px solid #e8e8e8; - border-radius: 8px; - } - """) - - layout = QVBoxLayout() - layout.setContentsMargins(12, 12, 12, 12) - layout.setSpacing(10) - - title_label = QLabel("Statistics") - title_label.setFont(QFont("Arial", 11, QFont.Weight.Bold)) - title_label.setStyleSheet(f"color: {self.PRIMARY_CYAN}; margin-bottom: 6px;") - layout.addWidget(title_label) - - stats_grid = QGridLayout() - stats_grid.setSpacing(10) - stats_grid.setRowStretch(0, 1) - stats_grid.setRowStretch(1, 1) - stats_grid.setColumnStretch(0, 1) - stats_grid.setColumnStretch(1, 1) - - self.stat_boxes = {} - - stat_names = [ - ("Total Files", "total_files"), - ("Unknown Type", "unknown_count"), - ("Avg Confidence", "avg_confidence"), - ("Avg Processing", "avg_processing_ms") - ] - - for idx, (label, key) in enumerate(stat_names): - row = idx // 2 - col = idx % 2 - box = self._create_stat_box(label) - self.stat_boxes[key] = box - stats_grid.addWidget(box, row, col) - - layout.addLayout(stats_grid, 1) - frame.setLayout(layout) - return frame - - def _create_stat_box(self, label: str) -> QFrame: - box = QFrame() - box.setStyleSheet(""" - QFrame { - background-color: #fafafa; - border: 2px solid #e8e8e8; - border-radius: 8px; - } - """) - - layout = QVBoxLayout() - layout.setContentsMargins(12, 12, 12, 12) - layout.setSpacing(8) - layout.setAlignment(Qt.AlignmentFlag.AlignCenter) - - label_widget = QLabel(label) - label_widget.setFont(QFont("Arial", 9, QFont.Weight.Bold)) - label_widget.setStyleSheet("color: #666;") - label_widget.setAlignment(Qt.AlignmentFlag.AlignCenter) - layout.addWidget(label_widget) - - value_widget = QLabel("--") - value_widget.setFont(QFont("Arial", 22, QFont.Weight.Bold)) - value_widget.setStyleSheet(f"color: {self.PRIMARY_CYAN};") - value_widget.setAlignment(Qt.AlignmentFlag.AlignCenter) - layout.addWidget(value_widget) - - box.setLayout(layout) - box._label = label_widget - box._value = value_widget - return box - - def _on_sound_checkbox_changed(self): - selected = [] - for sound_name, checkbox in self.sound_checkboxes.items(): - if checkbox.isChecked(): - selected.append(sound_name) - - self.current_sound_types = selected - - if not selected: - self.selection_label.setText("All sounds selected") - elif len(selected) == 1: - self.selection_label.setText(f"1 sound type selected: {selected[0]}") - else: - self.selection_label.setText(f"{len(selected)} sound types selected") - - def _clear_sound_selection(self): - for checkbox in self.sound_checkboxes.values(): - checkbox.setChecked(False) - - self.current_sound_types = [] - self.selection_label.setText("All sounds selected") - self.time_filter.setCurrentText('1 Day') - self.current_time_range = 'day' - self._refresh_data() - - def _on_filter_changed(self): - time_map = {'1 Day': 'day', '1 Week': 'week', '1 Month': 'month'} - self.current_time_range = time_map.get(self.time_filter.currentText(), 'day') - self._refresh_data() - - def _refresh_data(self): - try: - sound_filter = self.current_sound_types if self.current_sound_types else None - - self._clear_canvas(self.dist_canvas) - self._clear_canvas(self.timeline_canvas) - self._clear_canvas(self.confidence_canvas) - - self._update_distribution_chart() - self._update_timeline_chart() - self._update_heatmap_chart() - self._update_correlation_chart() - self._update_confidence_chart() - self._update_stats_boxes() - except Exception as e: - print(f"[SoundAnalyticsView] Refresh error: {e}", flush=True) - - def _update_distribution_chart(self): - try: - sound_filter = self.current_sound_types if self.current_sound_types else None - data = self.api.get_audio_distribution( - self.current_time_range, - limit=15, - sound_types=sound_filter - ) - - print(f"[DEBUG] Distribution data: {len(data) if data else 0} items", flush=True) - - if not data: - self._show_no_data(self.dist_canvas) - return - - labels = [d['head_pred_label'] for d in data] - counts = [d['count'] for d in data] - - # נקה את הקנבס - self.dist_canvas.figure.clear() - ax = self.dist_canvas.figure.add_subplot(111) - - colors = [self.CYAN_PALETTE[i % len(self.CYAN_PALETTE)] for i in range(len(labels))] - bars = ax.bar(range(len(labels)), counts, color=colors, edgecolor='black', linewidth=0.5) - - ax.set_xticks(range(len(labels))) - ax.set_xticklabels(labels, rotation=45, ha='right', fontsize=8) - ax.set_ylabel('Count', fontsize=9, fontweight='bold') - ax.grid(True, alpha=0.3, linestyle='--', axis='y') - - for bar in bars: - height = bar.get_height() - ax.text(bar.get_x() + bar.get_width()/2., height, - f'{int(height)}', - ha='center', va='bottom', fontsize=8, fontweight='bold') - - self.dist_canvas.figure.tight_layout() - self.dist_canvas.draw() # ⭐ זה החסר! - print("[DEBUG] Distribution chart drawn successfully", flush=True) - - except Exception as e: - print(f"[ERROR] Distribution chart error: {e}", flush=True) - import traceback - traceback.print_exc() - self._show_no_data(self.dist_canvas) - - def _update_timeline_chart(self): - try: - sound_filter = self.current_sound_types if self.current_sound_types else None - data = self.api.get_audio_timeline( - self.current_time_range, - sound_types=sound_filter - ) - - print(f"[DEBUG] Timeline data: {len(data) if data else 0} items", flush=True) - - if not data: - self._show_no_data(self.timeline_canvas) - return - - timeline_dict = {} - for row in data: - time_bucket = row['time_bucket'] - count = row['count'] - if time_bucket not in timeline_dict: - timeline_dict[time_bucket] = 0 - timeline_dict[time_bucket] += count - - sorted_times = sorted(timeline_dict.keys()) - times = [str(t)[:16] for t in sorted_times] - counts = [timeline_dict[t] for t in sorted_times] - - # נקה את הקנבס - self.timeline_canvas.figure.clear() - ax = self.timeline_canvas.figure.add_subplot(111) - - ax.plot(times, counts, marker='o', linewidth=2, markersize=5, color=self.ACCENT_CYAN) - ax.fill_between(range(len(times)), counts, alpha=0.2, color=self.PRIMARY_CYAN) - ax.set_xlabel('Time', fontsize=9, fontweight='bold') - ax.set_ylabel('Detections', fontsize=9, fontweight='bold') - ax.grid(True, alpha=0.3, linestyle='--') - ax.tick_params(labelsize=8) - - self.timeline_canvas.figure.autofmt_xdate(rotation=45, ha='right') - self.timeline_canvas.figure.tight_layout() - self.timeline_canvas.draw() # ⭐ זה החסר! - print("[DEBUG] Timeline chart drawn successfully", flush=True) - - except Exception as e: - print(f"[ERROR] Timeline chart error: {e}", flush=True) - import traceback - traceback.print_exc() - self._show_no_data(self.timeline_canvas) - - def _update_confidence_chart(self): - try: - sound_filter = self.current_sound_types if self.current_sound_types else None - data = self.api.get_model_health_metrics( - self.current_time_range, - sound_types=sound_filter - ) - - if not data: - self._show_no_data(self.confidence_canvas) - return - - times = [str(d["time_bucket"])[:16] for d in data] - avg_conf = [d["avg_confidence"] * 100 for d in data] - avg_proc = [d["avg_processing_ms"] for d in data] - - fig = self.confidence_canvas.figure - fig.clear() - - ax1 = fig.add_subplot(111) - ax1.set_title("Model Performance Trends", fontsize=10, fontweight="bold", color=self.PRIMARY_CYAN) - ax1.plot(times, avg_conf, color=self.ACCENT_CYAN, marker="o", linewidth=2, label="Avg Confidence %") - ax1.fill_between(range(len(avg_conf)), avg_conf, alpha=0.15, color=self.PRIMARY_CYAN) - ax1.set_ylabel("Confidence (%)", fontsize=9, fontweight="bold") - ax1.set_ylim(0, 100) - ax1.tick_params(axis='x', rotation=45, labelsize=8) - ax1.grid(True, alpha=0.3, linestyle="--") - - ax2 = ax1.twinx() - proc_color = self.CYAN_PALETTE[7] - ax2.plot(times, avg_proc, color=proc_color, marker="^", linestyle="--", linewidth=2, label="Avg Processing (ms)") - ax2.set_ylabel("Processing Time (ms)", fontsize=9, fontweight="bold", color=proc_color) - ax2.tick_params(axis='y', labelcolor=proc_color) - - lines, labels = ax1.get_legend_handles_labels() - lines2, labels2 = ax2.get_legend_handles_labels() - ax1.legend(lines + lines2, labels + labels2, loc="upper left", fontsize=8) - - fig.tight_layout() - self.confidence_canvas.draw() - - except Exception as e: - print(f"[SoundAnalyticsView] Model Health Monitor chart error: {e}", flush=True) - self._show_no_data(self.confidence_canvas) - - def _update_stats_boxes(self): - try: - sound_filter = self.current_sound_types if self.current_sound_types else None - stats = self.api.get_audio_stats( - self.current_time_range, - sound_types=sound_filter - ) - - if stats: - total = stats.get('total_files', 0) or 0 - self.stat_boxes['total_files']._value.setText(str(total)) - - unknown = stats.get('unknown_count', 0) or 0 - self.stat_boxes['unknown_count']._value.setText(str(unknown)) - - avg_conf = stats.get('avg_confidence') - if avg_conf is not None and avg_conf > 0: - self.stat_boxes['avg_confidence']._value.setText(f"{avg_conf:.1%}") - else: - self.stat_boxes['avg_confidence']._value.setText("--") - - avg_proc = stats.get('avg_processing_ms') - if avg_proc is not None and avg_proc > 0: - self.stat_boxes['avg_processing_ms']._value.setText(f"{avg_proc:.0f}ms") - else: - self.stat_boxes['avg_processing_ms']._value.setText("--") - else: - for key in self.stat_boxes: - self.stat_boxes[key]._value.setText("--") - except Exception as e: - print(f"[SoundAnalyticsView] Stats update error: {e}", flush=True) - - def _clear_canvas(self, canvas): - canvas.figure.clear() - - def _show_no_data(self, canvas): - ax = canvas.figure.add_subplot(111) - ax.text(0.5, 0.5, 'No Data Available', - ha='center', va='center', fontsize=14, fontweight='bold', - transform=ax.transAxes, color='#999') - ax.set_xlim(0, 1) - ax.set_ylim(0, 1) - ax.axis('off') - canvas.draw() - - def closeEvent(self, event): - self.refresh_timer.stop() - super().closeEvent(event) - - def _update_heatmap_chart(self): - try: - sound_filter = self.current_sound_types if self.current_sound_types else None - data = self.api.get_audio_heatmap( - self.current_time_range, - sound_types=sound_filter - ) - - print(f"[DEBUG] Heatmap data: {len(data) if data else 0} items", flush=True) - - if not data: - self._show_no_data(self.heatmap_canvas) - return - - heatmap_data = np.zeros((24, 7)) - - for row in data: - hour = int(row['hour_of_day']) - day = int(row['day_of_week']) - count = row['count'] - heatmap_data[hour, day] += count - - # נקה את הקנבס - self.heatmap_canvas.figure.clear() - ax = self.heatmap_canvas.figure.add_subplot(111) - - im = ax.imshow(heatmap_data, cmap='GnBu', aspect='auto', interpolation='nearest') - - ax.set_xticks(range(7)) - ax.set_xticklabels(['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], fontsize=8) - ax.set_yticks(range(0, 24, 2)) - ax.set_yticklabels([f'{h:02d}:00' for h in range(0, 24, 2)], fontsize=7) - - ax.set_xlabel('Day of Week', fontsize=9, fontweight='bold') - ax.set_ylabel('Hour of Day', fontsize=9, fontweight='bold') - - cbar = self.heatmap_canvas.figure.colorbar(im, ax=ax, pad=0.02) - cbar.set_label('Detections', fontsize=8) - cbar.ax.tick_params(labelsize=7) - - for i in range(24): - for j in range(7): - if heatmap_data[i, j] > 0: - text_color = 'white' if heatmap_data[i, j] > heatmap_data.max() * 0.5 else 'black' - ax.text(j, i, int(heatmap_data[i, j]), - ha="center", va="center", color=text_color, fontsize=6, fontweight='bold') - - self.heatmap_canvas.figure.tight_layout() - self.heatmap_canvas.draw() # ⭐ זה החסר! - print("[DEBUG] Heatmap chart drawn successfully", flush=True) - - except Exception as e: - print(f"[ERROR] Heatmap chart error: {e}", flush=True) - import traceback - traceback.print_exc() - self._show_no_data(self.heatmap_canvas) - - def _update_correlation_chart(self): - try: - sound_filter = self.current_sound_types if self.current_sound_types else None - data = self.api.get_audio_correlations( - self.current_time_range, - sound_types=sound_filter - ) - - print(f"[DEBUG] Correlation data: {len(data) if data else 0} items", flush=True) - - if not data or len(data) < 1: - self._show_no_data(self.correlation_canvas) - return - - time_buckets = sorted(list(set(row['time_bucket'] for row in data))) - sound_types = sorted(list(set(row['sound_type'] for row in data))) - - if len(time_buckets) < 2 or len(sound_types) < 2: - self._show_no_data(self.correlation_canvas) - return - - n_times = len(time_buckets) - n_sounds = len(sound_types) - data_matrix = np.zeros((n_times, n_sounds)) - - time_idx = {t: i for i, t in enumerate(time_buckets)} - sound_idx = {s: i for i, s in enumerate(sound_types)} - - for row in data: - t_idx = time_idx[row['time_bucket']] - s_idx = sound_idx[row['sound_type']] - data_matrix[t_idx, s_idx] = row['detection_count'] - - corr_matrix = np.corrcoef(data_matrix.T) - corr_matrix = np.nan_to_num(corr_matrix, nan=0.0) - - # נקה את הקנבס - self.correlation_canvas.figure.clear() - ax = self.correlation_canvas.figure.add_subplot(111) - - im = ax.imshow(corr_matrix, cmap='Blues', aspect='auto', vmin=-1, vmax=1) - - ax.set_xticks(range(len(sound_types))) - ax.set_yticks(range(len(sound_types))) - ax.set_xticklabels(sound_types, rotation=45, ha='right', fontsize=7) - ax.set_yticklabels(sound_types, fontsize=7) - - for i in range(len(sound_types)): - for j in range(len(sound_types)): - value = corr_matrix[i, j] - text_color = 'white' if value > 0.5 else 'black' - ax.text(j, i, f'{value:.2f}', - ha='center', va='center', - color=text_color, fontsize=6, fontweight='bold') - - cbar = self.correlation_canvas.figure.colorbar(im, ax=ax, fraction=0.046, pad=0.04) - cbar.set_label('Correlation Strength', rotation=270, labelpad=15, fontsize=8) - - ax.set_title('Sound Type Correlations\nDarker = Stronger Co-occurrence', - fontsize=9, fontweight='bold', pad=10) - - self.correlation_canvas.figure.tight_layout() - self.correlation_canvas.draw() # ⭐ זה החסר! - print("[DEBUG] Correlation chart drawn successfully", flush=True) - - except Exception as e: - print(f"[ERROR] Correlation chart error: {e}", flush=True) - import traceback - traceback.print_exc() - self._show_no_data(self.correlation_canvas) - -# ========================================================== -# Sound2 View - Displays Grafana dashboard -# ========================================================== -class Sound2View(QWidget): - def __init__(self, api: DashboardApi, parent=None): - super().__init__(parent) - self.api = api - self.setup_ui() - - def setup_ui(self): - layout = QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - - header = QHBoxLayout() - - title = QLabel("🌿 Ultrasonic Plant Predictions Dashboard") - title.setStyleSheet(""" - font-size: 18px; - font-weight: bold; - padding: 10px; - color: #2C3E50; - """) - header.addWidget(title) - - header.addStretch() - - refresh_btn = QPushButton("🔄 Refresh") - refresh_btn.setStyleSheet(""" - QPushButton { - background-color: #3498DB; - color: white; - border: none; - padding: 8px 16px; - border-radius: 4px; - font-weight: bold; - } - QPushButton:hover { - background-color: #2980B9; - } - """) - refresh_btn.clicked.connect(self.refresh_dashboard) - header.addWidget(refresh_btn) - - layout.addLayout(header) - - self.web_view = QWebEngineView() - - grafana_url = ( - "http://grafana:3000/d/ultrasonic-predictions/" - "ultrasonic-plant-predictions" - "?orgId=1&refresh=5s&kiosk=tv&theme=light" - ) - - self.web_view.setUrl(QUrl(grafana_url)) - layout.addWidget(self.web_view) - - self.status_label = QLabel("📊 Loading dashboard...") - self.status_label.setStyleSheet(""" - height: 24px; - padding: 5px; - color: #7F8C8D; - font-size: 12px; - """) - layout.addWidget(self.status_label) - - self.web_view.loadFinished.connect(self.on_load_finished) - - def refresh_dashboard(self): - self.status_label.setText("🔄 Refreshing dashboard...") - self.web_view.reload() - - def on_load_finished(self, success: bool): - if success: - self.status_label.setText("✓ Dashboard loaded successfully | Refreshes every 5s") - else: - self.status_label.setText( - "⚠ Failed to load dashboard. Please check Grafana server." - ) - - -# ========================================================== -# Main Sound View with Tabs -# ========================================================== class SoundView(QWidget): def __init__(self, api=None, parent=None): super().__init__(parent) self.api = api layout = QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - + layout.setContentsMargins(5, 5, 5, 5) + self.tabs = QTabWidget() + self.tabs.setSizePolicy( + QSizePolicy.Policy.Expanding, + QSizePolicy.Policy.Expanding + ) + self.tabs.setStyleSheet(""" QTabWidget::pane { - border: 2px solid #d1d5da; - border-radius: 10px; + border: 2px solid #e1e4e8; + border-radius: 12px; background: white; + margin-top: -1px; } QTabBar::tab { - background: #f6f8fa; - padding: 12px 24px; - border-radius: 8px 8px 0 0; - margin-right: 4px; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #f8f9fa, stop:1 #e9ecef); + padding: 14px 28px; + margin-right: 2px; + margin-top: 4px; + border: 1px solid #dee2e6; + border-bottom: none; + border-top-left-radius: 10px; + border-top-right-radius: 10px; font-size: 14px; - font-weight: 500; - color: #586069; + font-weight: 600; + color: #495057; + min-width: 140px; } QTabBar::tab:selected { - background-color: #4A90E2; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #4A90E2, stop:1 #357ABD); color: white; + border: 1px solid #357ABD; + border-bottom: 2px solid white; + margin-top: 2px; + padding-bottom: 16px; + } + QTabBar::tab:hover:!selected { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #e3f2fd, stop:1 #bbdefb); + color: #1976d2; + } + QTabBar::tab:first { + margin-left: 8px; } - QTabBar::tab:hover { background: #e1e4e8; } """) - self.map_tab = ImageMapView(api=self.api) + # Create tabs self.env_tab = RecordingsTab(recording_type="audio", api=self.api) self.plant_tab = RecordingsTab(recording_type="ultrasound", api=self.api) - self.dashboard_tab = Sound2View(api=self.api) + self.dashboard_tab = SoundGraphic(api=self.api) self.analytics_tab = SoundAnalyticsView(api=self.api) - - self.tabs.addTab(self.map_tab, "🗺️ Interactive Map") + + # Add tabs (removed map_tab that doesn't exist) self.tabs.addTab(self.env_tab, "🎵 Environment Sounds") self.tabs.addTab(self.plant_tab, "🌿 Plant Ultrasounds") self.tabs.addTab(self.dashboard_tab, "📊 Ultrasonic Dashboard") diff --git a/GUI/src/vast/views/sound/utils.py b/GUI/src/vast/views/sound/utils.py new file mode 100644 index 000000000..aeb1117cc --- /dev/null +++ b/GUI/src/vast/views/sound/utils.py @@ -0,0 +1,16 @@ +import os +import math + +MINIO_BASE = os.getenv("MINIO_PUBLIC_BASE", "http://minio-hot:9000") + +def normalize_minio_url(url: str) -> str: + if not url: + return "" + if url.startswith("http://") or url.startswith("https://"): + return url + + url = url.lstrip("/") + if url.startswith("sounds/"): + url = "sound/" + url + + return f"{MINIO_BASE.rstrip('/')}/{url}" From 8eb1fa51448b60e1a216dd89aef694b9d981bb77 Mon Sep 17 00:00:00 2001 From: Tehila-Git Date: Mon, 24 Nov 2025 17:08:27 +0200 Subject: [PATCH 2/2] fix the file --- GUI/src/vast/views/sound/sound_graphic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GUI/src/vast/views/sound/sound_graphic.py b/GUI/src/vast/views/sound/sound_graphic.py index 59027cbe1..91f19bf94 100644 --- a/GUI/src/vast/views/sound/sound_graphic.py +++ b/GUI/src/vast/views/sound/sound_graphic.py @@ -23,7 +23,7 @@ def setup_ui(self): self.web_view = QWebEngineView() self.web_view.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) - self.web_view.setUrl(QUrl("http://grafana:3000/d/ultrasonic-predictions")) + self.web_view.setUrl(QUrl("http://grafana:3000/d/ultrasonic-plant-dashboard-bw-01/plant-health-monitoring---professional-dashboard"")) layout.addWidget(self.web_view) self.status = QLabel("Loading...")