From: Danilo M. Date: Sun, 3 May 2026 08:44:13 +0000 (+0200) Subject: feat: taxonomy view with IT/EN pair table and orphan detection X-Git-Tag: v1.0~9 X-Git-Url: https://git.danix.xyz/?a=commitdiff_plain;h=4549e942ef0b6259b068a15ccdbeb9bba0cfe919;p=publisher.git feat: taxonomy view with IT/EN pair table and orphan detection --- diff --git a/ui/main_window.py b/ui/main_window.py index 463c3dc..61d74e4 100644 --- a/ui/main_window.py +++ b/ui/main_window.py @@ -104,8 +104,14 @@ class MainWindow(QMainWindow): self._stack.addWidget(self._hugo_panel) self._page_hugo = self._stack.count() - 1 + from ui.taxonomy_view import TaxonomyView + + self._taxonomy_view = TaxonomyView(Path(self.config.blog_repo), parent=self) + self._stack.addWidget(self._taxonomy_view) + self._page_taxonomy = self._stack.count() - 1 + # Remaining placeholders - for name in ["taxonomy", "media"]: + for name in ["media"]: w = QLabel(f"[{name}]") w.setAlignment(Qt.AlignmentFlag.AlignCenter) self._stack.addWidget(w) diff --git a/ui/taxonomy_view.py b/ui/taxonomy_view.py new file mode 100644 index 0000000..f7b450b --- /dev/null +++ b/ui/taxonomy_view.py @@ -0,0 +1,131 @@ +from __future__ import annotations +from pathlib import Path +from PyQt6.QtWidgets import ( + QWidget, QVBoxLayout, QHBoxLayout, QTabWidget, + QTableWidget, QTableWidgetItem, QPushButton, + QLabel, QDialog, QFormLayout, QLineEdit, QMessageBox, +) +from PyQt6.QtCore import Qt +from PyQt6.QtGui import QColor +from core.taxonomy import TaxonomyModel, load_taxonomy, save_taxonomy + +class AddTermDialog(QDialog): + def __init__(self, parent=None): + super().__init__(parent) + self.setWindowTitle("Aggiungi termine") + layout = QVBoxLayout(self) + form = QFormLayout() + self._it = QLineEdit() + self._en = QLineEdit() + form.addRow("IT:", self._it) + form.addRow("EN:", self._en) + layout.addLayout(form) + btns = QHBoxLayout() + ok = QPushButton("Aggiungi") + ok.clicked.connect(self.accept) + cancel = QPushButton("Annulla") + cancel.clicked.connect(self.reject) + btns.addStretch() + btns.addWidget(cancel) + btns.addWidget(ok) + layout.addLayout(btns) + + @property + def it_term(self) -> str: + return self._it.text().strip() + + @property + def en_term(self) -> str: + return self._en.text().strip() + +class TaxonomyView(QWidget): + def __init__(self, blog_root: Path, parent=None): + super().__init__(parent) + self._blog_root = blog_root + self._tags_it = blog_root / "docs" / "tags-it.txt" + self._tags_en = blog_root / "docs" / "tags-en.txt" + self._model: TaxonomyModel | None = None + self._build_ui() + self.load() + + def _build_ui(self): + layout = QVBoxLayout(self) + + tabs = QTabWidget() + + tags_tab = QWidget() + tl = QVBoxLayout(tags_tab) + self._tags_table = QTableWidget(0, 2) + self._tags_table.setHorizontalHeaderLabels(["🇮🇹 Italiano", "🇬🇧 English"]) + self._tags_table.horizontalHeader().setStretchLastSection(True) + tl.addWidget(self._tags_table) + tag_btns = QHBoxLayout() + add_tag_btn = QPushButton("➕ Aggiungi termine") + add_tag_btn.clicked.connect(self._add_term) + save_tags_btn = QPushButton("💾 Salva") + save_tags_btn.clicked.connect(self._save) + tag_btns.addWidget(add_tag_btn) + tag_btns.addStretch() + tag_btns.addWidget(save_tags_btn) + tl.addLayout(tag_btns) + tabs.addTab(tags_tab, "🏷 Tags") + + layout.addWidget(tabs) + + self._status = QLabel("") + self._status.setStyleSheet("color:#888;font-size:10px;padding:4px;") + layout.addWidget(self._status) + + def load(self): + if not self._tags_it.exists(): + self._status.setText("tags-it.txt non trovato.") + return + self._model = load_taxonomy(self._tags_it, self._tags_en) + self._populate_table() + + def _populate_table(self): + if not self._model: + return + self._tags_table.setRowCount(0) + for it_term, en_term in sorted(self._model.it_to_en.items()): + row = self._tags_table.rowCount() + self._tags_table.insertRow(row) + self._tags_table.setItem(row, 0, QTableWidgetItem(it_term)) + self._tags_table.setItem(row, 1, QTableWidgetItem(en_term)) + + for orphan in self._model.orphans_it: + row = self._tags_table.rowCount() + self._tags_table.insertRow(row) + it_item = QTableWidgetItem(orphan) + it_item.setForeground(QColor("#ff6b6b")) + en_item = QTableWidgetItem("⚠ mancante") + en_item.setForeground(QColor("#ff6b6b")) + self._tags_table.setItem(row, 0, it_item) + self._tags_table.setItem(row, 1, en_item) + + self._status.setText( + f"{len(self._model.it_to_en)} coppie · {len(self._model.orphans_it)} IT orfani · {len(self._model.orphans_en)} EN orfani" + ) + + def _add_term(self): + dlg = AddTermDialog(self) + if dlg.exec() and dlg.it_term and dlg.en_term: + if self._model: + self._model.it_to_en[dlg.it_term] = dlg.en_term + self._populate_table() + + def _save(self): + if not self._model: + return + updated: dict[str, str] = {} + for row in range(self._tags_table.rowCount()): + it_item = self._tags_table.item(row, 0) + en_item = self._tags_table.item(row, 1) + if it_item and en_item: + it_val = it_item.text().strip() + en_val = en_item.text().strip() + if it_val and en_val and en_val != "⚠ mancante": + updated[it_val] = en_val + self._model.it_to_en = updated + save_taxonomy(self._model, self._tags_it, self._tags_en) + self._status.setText("Salvato.")