"""Column statistics dialog — shows row count, nulls, distinct, min, max, avg.""" from PyQt6.QtWidgets import ( QDialog, QVBoxLayout, QFormLayout, QLabel, QDialogButtonBox, QGroupBox, QProgressBar, QSizePolicy, ) from PyQt6.QtCore import Qt, QThread, pyqtSignal from PyQt6.QtGui import QFont class _StatsWorker(QThread): result = pyqtSignal(dict) error = pyqtSignal(str) def __init__(self, driver, database: str, table: str, column: str): super().__init__() self._driver = driver self._database = database self._table = table self._column = column def run(self): col = self._column tbl = self._table try: _, rows, _ = self._driver.execute_query( f'SELECT COUNT(*), COUNT("{col}"), COUNT(DISTINCT "{col}"), ' f'MIN("{col}"), MAX("{col}") FROM "{tbl}"' ) total, non_null, distinct, min_val, max_val = rows[0] null_count = (total or 0) - (non_null or 0) except Exception as e: self.error.emit(str(e)) return avg_val = None try: _, avg_rows, _ = self._driver.execute_query( f'SELECT AVG("{col}") FROM "{tbl}"' ) avg_val = avg_rows[0][0] except Exception: pass self.result.emit({ "total": total, "non_null": non_null, "nulls": null_count, "distinct": distinct, "min": min_val, "max": max_val, "avg": avg_val, }) class ColumnStatsDialog(QDialog): def __init__(self, driver, database: str, table: str, column: str, parent=None): super().__init__(parent) self.setWindowTitle(f"Column Statistics — {column}") self.setMinimumWidth(360) self._driver = driver self._database = database self._table = table self._column = column self._build_ui() self._load() def _build_ui(self): root = QVBoxLayout(self) root.setSpacing(10) root.setContentsMargins(16, 16, 16, 16) subtitle = QLabel(f"{self._table}.{self._column}") subtitle.setTextFormat(Qt.TextFormat.RichText) root.addWidget(subtitle) # ── Stats group ─────────────────────────────────────────────────────── box = QGroupBox("Statistics") self._form = QFormLayout(box) self._form.setSpacing(6) self._rows: dict[str, QLabel] = {} for key, label in [ ("total", "Total rows"), ("non_null", "Non-null"), ("nulls", "Null count"), ("distinct", "Distinct values"), ("min", "Min"), ("max", "Max"), ("avg", "Avg (numeric)"), ]: lbl = QLabel("…") lbl.setFont(QFont("Consolas", 10)) self._form.addRow(label + ":", lbl) self._rows[key] = lbl root.addWidget(box) # ── Loading bar ─────────────────────────────────────────────────────── self._progress = QProgressBar() self._progress.setMaximum(0) self._progress.setFixedHeight(6) self._progress.setTextVisible(False) root.addWidget(self._progress) bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Close) bb.rejected.connect(self.reject) root.addWidget(bb) def _load(self): self._worker = _StatsWorker( self._driver, self._database, self._table, self._column ) self._worker.result.connect(self._on_result) self._worker.error.connect(self._on_error) self._worker.finished.connect(self._progress.hide) self._worker.start() def _on_result(self, stats: dict): for key, lbl in self._rows.items(): val = stats.get(key) if val is None: text = "N/A" elif isinstance(val, float): text = f"{val:,.4f}" else: try: text = f"{int(val):,}" except (TypeError, ValueError): text = str(val) lbl.setText(text) def _on_error(self, msg: str): for lbl in self._rows.values(): lbl.setText("—") self._rows["total"].setText(f"Error: {msg[:60]}") self._rows["total"].setStyleSheet("color: #e78284;")