From: Danilo M. Date: Sun, 3 May 2026 13:44:00 +0000 (+0200) Subject: feat: add Categories tab to TaxonomyView X-Git-Tag: v1.2~3 X-Git-Url: https://git.danix.xyz/?a=commitdiff_plain;h=ab830bf7b489339d6d13ec673fd539a0023eb97d;p=publisher.git feat: add Categories tab to TaxonomyView Single-list editor for docs/categories.txt with article usage counts scanned from frontmatter. Supports multi-select delete. Co-Authored-By: Claude Sonnet 4.6 --- diff --git a/core/taxonomy.py b/core/taxonomy.py index 8aee0aa..e72d486 100644 --- a/core/taxonomy.py +++ b/core/taxonomy.py @@ -22,3 +22,11 @@ def save_taxonomy(model: TaxonomyModel, it_path: Path, en_path: Path) -> None: pairs = sorted(model.it_to_en.items(), key=lambda x: x[0]) it_path.write_text("\n".join(k for k, _ in pairs) + "\n") en_path.write_text("\n".join(v for _, v in pairs) + "\n") + +def load_categories(path: Path) -> list[str]: + if not path.exists(): + return [] + return sorted(l.strip() for l in path.read_text().splitlines() if l.strip()) + +def save_categories(path: Path, categories: list[str]) -> None: + path.write_text("\n".join(sorted(categories)) + "\n") diff --git a/ui/taxonomy_view.py b/ui/taxonomy_view.py index f7b450b..6efbffd 100644 --- a/ui/taxonomy_view.py +++ b/ui/taxonomy_view.py @@ -3,11 +3,12 @@ from pathlib import Path from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QTabWidget, QTableWidget, QTableWidgetItem, QPushButton, - QLabel, QDialog, QFormLayout, QLineEdit, QMessageBox, + QLabel, QDialog, QFormLayout, QLineEdit, QMessageBox, QHeaderView, ) from PyQt6.QtCore import Qt from PyQt6.QtGui import QColor -from core.taxonomy import TaxonomyModel, load_taxonomy, save_taxonomy +from core.taxonomy import TaxonomyModel, load_taxonomy, save_taxonomy, load_categories, save_categories +from core.article_scanner import scan_articles class AddTermDialog(QDialog): def __init__(self, parent=None): @@ -44,7 +45,9 @@ class TaxonomyView(QWidget): self._blog_root = blog_root self._tags_it = blog_root / "docs" / "tags-it.txt" self._tags_en = blog_root / "docs" / "tags-en.txt" + self._categories_path = blog_root / "docs" / "categories.txt" self._model: TaxonomyModel | None = None + self._categories: list[str] = [] self._build_ui() self.load() @@ -70,6 +73,29 @@ class TaxonomyView(QWidget): tl.addLayout(tag_btns) tabs.addTab(tags_tab, "🏷 Tags") + cats_tab = QWidget() + cl = QVBoxLayout(cats_tab) + self._cats_table = QTableWidget(0, 2) + self._cats_table.setHorizontalHeaderLabels(["Categoria", "Articoli"]) + self._cats_table.setSelectionMode(QTableWidget.SelectionMode.ExtendedSelection) + self._cats_table.horizontalHeader().setStretchLastSection(False) + self._cats_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch) + self._cats_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeMode.ResizeToContents) + cl.addWidget(self._cats_table) + cat_btns = QHBoxLayout() + add_cat_btn = QPushButton("➕ Aggiungi") + add_cat_btn.clicked.connect(self._add_category) + remove_cat_btn = QPushButton("🗑 Rimuovi") + remove_cat_btn.clicked.connect(self._remove_category) + save_cats_btn = QPushButton("💾 Salva") + save_cats_btn.clicked.connect(self._save_categories) + cat_btns.addWidget(add_cat_btn) + cat_btns.addWidget(remove_cat_btn) + cat_btns.addStretch() + cat_btns.addWidget(save_cats_btn) + cl.addLayout(cat_btns) + tabs.addTab(cats_tab, "📂 Categorie") + layout.addWidget(tabs) self._status = QLabel("") @@ -79,9 +105,11 @@ class TaxonomyView(QWidget): 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() + else: + self._model = load_taxonomy(self._tags_it, self._tags_en) + self._populate_table() + self._categories = load_categories(self._categories_path) + self._populate_cats_table() def _populate_table(self): if not self._model: @@ -114,6 +142,74 @@ class TaxonomyView(QWidget): self._model.it_to_en[dlg.it_term] = dlg.en_term self._populate_table() + def _populate_cats_table(self): + articles = scan_articles(self._blog_root) + counts: dict[str, int] = {} + for a in articles: + for c in a.frontmatter.get("categories", []): + key = str(c).strip() + counts[key] = counts.get(key, 0) + 1 + + self._cats_table.setRowCount(0) + for cat in sorted(self._categories): + row = self._cats_table.rowCount() + self._cats_table.insertRow(row) + self._cats_table.setItem(row, 0, QTableWidgetItem(cat)) + count_item = QTableWidgetItem(str(counts.get(cat, 0))) + count_item.setFlags(count_item.flags() & ~Qt.ItemFlag.ItemIsEditable) + count_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) + self._cats_table.setItem(row, 1, count_item) + + def _add_category(self): + dlg = QDialog(self) + dlg.setWindowTitle("Aggiungi categoria") + layout = QVBoxLayout(dlg) + form = QFormLayout() + name_edit = QLineEdit() + form.addRow("Nome:", name_edit) + layout.addLayout(form) + btns = QHBoxLayout() + ok = QPushButton("Aggiungi") + ok.clicked.connect(dlg.accept) + cancel = QPushButton("Annulla") + cancel.clicked.connect(dlg.reject) + btns.addStretch() + btns.addWidget(cancel) + btns.addWidget(ok) + layout.addLayout(btns) + if dlg.exec() and name_edit.text().strip(): + name = name_edit.text().strip() + if name not in self._categories: + self._categories.append(name) + self._populate_cats_table() + + def _remove_category(self): + rows = {idx.row() for idx in self._cats_table.selectedIndexes()} + if not rows: + return + names = [self._cats_table.item(r, 0).text() for r in rows if self._cats_table.item(r, 0)] + if not names: + return + reply = QMessageBox.question( + self, "Rimuovi categorie", + f"Rimuovere {len(names)} categorie?", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, + ) + if reply == QMessageBox.StandardButton.Yes: + to_remove = set(names) + self._categories = [c for c in self._categories if c not in to_remove] + self._populate_cats_table() + + def _save_categories(self): + cats: list[str] = [] + for row in range(self._cats_table.rowCount()): + item = self._cats_table.item(row, 0) + if item and item.text().strip(): + cats.append(item.text().strip()) + self._categories = sorted(set(cats)) + save_categories(self._categories_path, self._categories) + self._status.setText("Categorie salvate.") + def _save(self): if not self._model: return