# Article List Metadata Display Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Show type, tags, categories, and date for each article row in the articles list (both IT and EN tabs). **Architecture:** Extend `ArticleItem` to render a two-line item: line 1 = slug + translation badge (existing), line 2 = metadata fields extracted from `article.frontmatter`. Use `\n` in item text and `setSizeHint` for row height. No new files needed. **Tech Stack:** PyQt6 QListWidgetItem, existing Article.frontmatter dict. --- ### Task 1: Add metadata helpers to Article model **Files:** - Modify: `core/models.py` - Test: `tests/test_models.py` - [ ] **Step 1: Write failing tests** Add to `tests/test_models.py`: ```python def test_article_draft_property(): fm = {"draft": True, "type": "Tech", "tags": ["linux"], "categories": ["DIY"], "date": "2024-01-15T10:00:00+00:00"} a = Article(slug="test", lang="it", path=Path("/tmp/test"), frontmatter=fm, has_translation=False, translation_path=None) assert a.draft is True def test_article_meta_type(): fm = {"type": "Tech"} a = Article(slug="test", lang="it", path=Path("/tmp/test"), frontmatter=fm, has_translation=False, translation_path=None) assert a.meta_type == "Tech" def test_article_meta_type_missing(): a = Article(slug="test", lang="it", path=Path("/tmp/test"), frontmatter={}, has_translation=False, translation_path=None) assert a.meta_type == "" def test_article_meta_tags(): fm = {"tags": ["linux", "python"]} a = Article(slug="test", lang="it", path=Path("/tmp/test"), frontmatter=fm, has_translation=False, translation_path=None) assert a.meta_tags == "linux, python" def test_article_meta_tags_empty(): a = Article(slug="test", lang="it", path=Path("/tmp/test"), frontmatter={}, has_translation=False, translation_path=None) assert a.meta_tags == "" def test_article_meta_categories(): fm = {"categories": ["DIY", "Tech"]} a = Article(slug="test", lang="it", path=Path("/tmp/test"), frontmatter=fm, has_translation=False, translation_path=None) assert a.meta_categories == "DIY, Tech" def test_article_meta_date(): fm = {"date": "2024-01-15T10:00:00+00:00"} a = Article(slug="test", lang="it", path=Path("/tmp/test"), frontmatter=fm, has_translation=False, translation_path=None) assert a.meta_date == "2024-01-15" def test_article_meta_date_missing(): a = Article(slug="test", lang="it", path=Path("/tmp/test"), frontmatter={}, has_translation=False, translation_path=None) assert a.meta_date == "" ``` - [ ] **Step 2: Run tests to confirm they fail** ```bash pytest tests/test_models.py -v ``` Expected: FAIL — `Article` has no `meta_type`, `meta_tags`, `meta_categories`, `meta_date` attributes. - [ ] **Step 3: Implement properties in Article** In `core/models.py`, add four properties after the existing `draft` property: ```python @property def meta_type(self) -> str: return str(self.frontmatter.get("type", "")) @property def meta_tags(self) -> str: tags = self.frontmatter.get("tags", []) if isinstance(tags, list): return ", ".join(str(t) for t in tags) return str(tags) if tags else "" @property def meta_categories(self) -> str: cats = self.frontmatter.get("categories", []) if isinstance(cats, list): return ", ".join(str(c) for c in cats) return str(cats) if cats else "" @property def meta_date(self) -> str: raw = self.frontmatter.get("date", "") if not raw: return "" return str(raw)[:10] # ISO date → "YYYY-MM-DD" ``` - [ ] **Step 4: Run tests to confirm they pass** ```bash pytest tests/test_models.py -v ``` Expected: all pass. - [ ] **Step 5: Commit** ```bash git add core/models.py tests/test_models.py git commit -m "feat: add meta_type, meta_tags, meta_categories, meta_date properties to Article" ``` --- ### Task 2: Render metadata in ArticleItem **Files:** - Modify: `ui/articles_view.py` No automated tests for UI — verify manually. - [ ] **Step 1: Update ArticleItem to render two-line text** In `ui/articles_view.py`, replace the `ArticleItem.__init__` method. The first line keeps slug + translation badge (and `[DRAFT]` prefix). The second line shows metadata fields, omitting empty ones. Replace the existing `ArticleItem` class (lines 10-26) with: ```python class ArticleItem(QListWidgetItem): def __init__(self, article: Article): super().__init__() self.article = article if article.has_translation: badge = f"🇬🇧 ✓" if article.lang == "it" else "🇮🇹 ✓" else: badge = f"🇬🇧 ✗" if article.lang == "it" else "🇮🇹 ✗" line1 = f"{article.slug} [{badge}]" if article.draft: line1 = f"[DRAFT] {line1}" parts = [] if article.meta_type: parts.append(article.meta_type) if article.meta_date: parts.append(article.meta_date) if article.meta_tags: parts.append(f"#{article.meta_tags.replace(', ', ' #')}") if article.meta_categories: parts.append(f"[{article.meta_categories}]") line2 = " ".join(parts) if parts else "" text = f"{line1}\n{line2}" if line2 else line1 self.setText(text) self.setSizeHint(QSize(0, 42) if line2 else QSize(0, 26)) if article.draft: self.setForeground(QColor("#f59e0b")) elif not article.has_translation: self.setForeground(QColor("#ff6b6b")) ``` Also add `QSize` to the imports at the top of the file: ```python from PyQt6.QtCore import Qt, pyqtSignal, QSize ``` - [ ] **Step 2: Run the app and verify visually** ```bash python main.py ``` Check: - Each article row shows two lines: slug + badge on line 1, metadata on line 2 - Articles with no metadata (missing type/date/tags/categories) show single line - Draft articles: amber color on both lines - Missing-translation articles: red color - Tags rendered as `#tag1 #tag2` - Categories rendered as `[Cat1, Cat2]` - Date rendered as `YYYY-MM-DD` - Row height accommodates two lines without clipping - [ ] **Step 3: Run full test suite** ```bash pytest tests/ -v ``` Expected: all 22+ tests pass (UI change has no tests, but existing tests must not break). - [ ] **Step 4: Commit** ```bash git add ui/articles_view.py git commit -m "feat: show type, date, tags, categories in article list rows" ```