]> danix's work - publisher.git/commitdiff
feat: propagate tag renames to articles after taxonomy save
authorDanilo M. <redacted>
Sun, 3 May 2026 14:09:38 +0000 (16:09 +0200)
committerDanilo M. <redacted>
Sun, 3 May 2026 14:09:38 +0000 (16:09 +0200)
After saving TaxonomyView, detect renamed IT/EN tags by comparing
old and new it_to_en dicts, then rewrite frontmatter of affected
articles. Status bar shows count of updated articles. Taxonomy
save failure aborts propagation. Adds pytest-qt and four unit
tests for _detect_renames.

Co-Authored-By: Claude Sonnet 4.6 <redacted>
requirements.txt
tests/test_taxonomy.py
ui/taxonomy_view.py

index 112f3ac4f9343b0bfc41b5d028c36b61618faab8..1e1e7705ed447c6e967ace3403d568d42d6d26fa 100644 (file)
@@ -1,3 +1,4 @@
 PyQt6>=6.10.0,<6.11.0
 tomlkit>=0.12.0
 mistune>=3.0.0
+pytest-qt>=4.0.0
index a10531baf6d871cdecc9f1679f5d6d7387a4d087..b5f48e99ffa9447de8356ebbf9afec3cbbcf7c20 100644 (file)
@@ -2,6 +2,7 @@
 import pytest
 from pathlib import Path
 from core.taxonomy import TaxonomyModel, load_taxonomy, save_taxonomy
+from ui.taxonomy_view import TaxonomyView
 
 def _write_pair(tmp_path, it_lines, en_lines):
     it_file = tmp_path / "tags-it.txt"
@@ -41,3 +42,35 @@ def test_save_taxonomy_sorted(tmp_path):
     save_taxonomy(model, it_f, en_f)
     lines = it_f.read_text().splitlines()
     assert lines == sorted(lines)
+
+
+def test_detect_renames_en_change(qtbot):
+    old = {"linux": "linux", "vita": "life"}
+    new = {"linux": "linux", "vita": "living"}
+    it_r, en_r = TaxonomyView._detect_renames(old, new)
+    assert it_r == {}
+    assert en_r == {"life": "living"}
+
+
+def test_detect_renames_it_change(qtbot):
+    old = {"linux": "linux", "vita": "life"}
+    new = {"linux": "linux", "living": "life"}
+    it_r, en_r = TaxonomyView._detect_renames(old, new)
+    assert it_r == {"vita": "living"}
+    assert en_r == {}
+
+
+def test_detect_renames_no_change(qtbot):
+    old = {"linux": "linux"}
+    new = {"linux": "linux"}
+    it_r, en_r = TaxonomyView._detect_renames(old, new)
+    assert it_r == {}
+    assert en_r == {}
+
+
+def test_detect_renames_addition_not_rename(qtbot):
+    old = {"linux": "linux"}
+    new = {"linux": "linux", "vita": "life"}
+    it_r, en_r = TaxonomyView._detect_renames(old, new)
+    assert it_r == {}
+    assert en_r == {}
index 6efbffd630cd943d39c4340d965779a4cd389665..4862833b437568c9bc324752e170a8d56a1229d3 100644 (file)
@@ -9,6 +9,7 @@ from PyQt6.QtCore import Qt
 from PyQt6.QtGui import QColor
 from core.taxonomy import TaxonomyModel, load_taxonomy, save_taxonomy, load_categories, save_categories
 from core.article_scanner import scan_articles
+from core.frontmatter import parse_frontmatter, write_frontmatter
 
 class AddTermDialog(QDialog):
     def __init__(self, parent=None):
@@ -210,9 +211,50 @@ class TaxonomyView(QWidget):
         save_categories(self._categories_path, self._categories)
         self._status.setText("Categorie salvate.")
 
+    @staticmethod
+    def _detect_renames(
+        old: dict[str, str], new: dict[str, str]
+    ) -> tuple[dict[str, str], dict[str, str]]:
+        it_renames: dict[str, str] = {}
+        en_renames: dict[str, str] = {}
+        old_en_to_it = {v: k for k, v in old.items()}
+        for new_it, new_en in new.items():
+            if new_it not in old:
+                if new_en in old_en_to_it:
+                    it_renames[old_en_to_it[new_en]] = new_it
+            else:
+                if old[new_it] != new_en:
+                    en_renames[old[new_it]] = new_en
+        return it_renames, en_renames
+
+    def _propagate_renames(self, it_renames: dict[str, str], en_renames: dict[str, str]) -> int:
+        if not it_renames and not en_renames:
+            return 0
+        articles = scan_articles(self._blog_root)
+        count = 0
+        for article in articles:
+            renames = it_renames if article.lang == "it" else en_renames
+            if not renames:
+                continue
+            current_tags = article.frontmatter.get("tags", [])
+            if not isinstance(current_tags, list):
+                continue
+            new_tags = [renames.get(str(t), str(t)) for t in current_tags]
+            if new_tags == [str(t) for t in current_tags]:
+                continue
+            try:
+                fm, body = parse_frontmatter(article.path)
+                fm["tags"] = new_tags
+                write_frontmatter(article.path, fm, body)
+                count += 1
+            except Exception:
+                pass
+        return count
+
     def _save(self):
         if not self._model:
             return
+        old_it_to_en = dict(self._model.it_to_en)
         updated: dict[str, str] = {}
         for row in range(self._tags_table.rowCount()):
             it_item = self._tags_table.item(row, 0)
@@ -223,5 +265,14 @@ class TaxonomyView(QWidget):
                 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.")
+        try:
+            save_taxonomy(self._model, self._tags_it, self._tags_en)
+        except OSError as e:
+            QMessageBox.critical(self, "Errore", f"Impossibile salvare: {e}")
+            return
+        it_renames, en_renames = self._detect_renames(old_it_to_en, updated)
+        count = self._propagate_renames(it_renames, en_renames)
+        if count:
+            self._status.setText(f"Salvato · {count} articol{'o' if count == 1 else 'i'} aggiornati.")
+        else:
+            self._status.setText("Salvato.")