summaryrefslogtreecommitdiffstats
path: root/docs/superpowers/plans/2026-05-03-article-list-metadata.md
blob: a9b97aa9a7c3956319bbed8496a31bebefc4849e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# 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"
```