aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--sb_builder/hints_tab.py8
-rw-r--r--sb_builder/packages.py32
-rw-r--r--sb_builder/ui.py2
-rw-r--r--tests/test_gui.py7
5 files changed, 35 insertions, 16 deletions
diff --git a/README.md b/README.md
index ccf5edd..ca3613f 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,7 @@ reopens it.
- **Packages**: every package of the repository, with built version and
status (`ok`, `failed`, `never`, `outdated`). Search by name or summary, or
with `cat:`, `maint:`, `req:`, `status:`; toggle the overlay and status
- chips. The detail pane shows the package's versions, its dependencies after
+ chips, and **orphaned** for the packages SBo lists without a maintainer. The detail pane shows the package's versions, its dependencies after
hints (dropped ones struck through), what its hint file does, and a link
to it in Hints. **Build**, **Update**, **Rebuild**, **Remove** and **Update
all** run a slackrepo `--preview` first; **New hint** creates hints, pinned
diff --git a/sb_builder/hints_tab.py b/sb_builder/hints_tab.py
index 071e304..fbf8a70 100644
--- a/sb_builder/hints_tab.py
+++ b/sb_builder/hints_tab.py
@@ -61,8 +61,6 @@ class HintsTab(QWidget):
self.chips[name].toggled.connect(self.refilter)
chips.addWidget(self.chips[name])
chips.addStretch()
- self.status = QLabel()
- chips.addWidget(self.status)
self.table = QTableWidget(0, len(COLUMNS))
self.table.setHorizontalHeaderLabels(COLUMNS)
@@ -97,8 +95,12 @@ class HintsTab(QWidget):
for key, on in (("Ctrl+A", True), ("Ctrl+Shift+A", False))]
self.loaded = False
+ self.status = QLabel()
+ search = QHBoxLayout()
+ search.addWidget(self.query)
+ search.addWidget(self.status)
layout = QVBoxLayout(self)
- layout.addWidget(self.query)
+ layout.addLayout(search)
layout.addLayout(chips)
layout.addWidget(self.table, 1)
self.refilter()
diff --git a/sb_builder/packages.py b/sb_builder/packages.py
index a4d1f1b..ab6f3ce 100644
--- a/sb_builder/packages.py
+++ b/sb_builder/packages.py
@@ -64,12 +64,19 @@ def parse_query(text):
return terms
-def matches(row, terms, overlays=(), statuses=()):
+def orphaned(row):
+ # SBo marks them MAINTAINER="orphaned - no maintainer", EMAIL="NOEMAIL"
+ return (row["maintainer"] or "").startswith("orphaned")
+
+
+def matches(row, terms, overlays=(), statuses=(), orphans=False):
"""All terms match, and the row is in a toggled chip of each non-empty group."""
if overlays and overlay(row) not in overlays:
return False
if statuses and row["status"] not in statuses:
return False
+ if orphans and not orphaned(row):
+ return False
for field, term in terms:
if field is None:
hay = f"{row['name']} {row['summary'] or ''}"
@@ -148,15 +155,16 @@ class Model(QAbstractTableModel):
class Filter(QSortFilterProxyModel):
def __init__(self):
super().__init__()
- self.terms, self.overlays, self.statuses = [], set(), set()
+ self.terms, self.overlays, self.statuses, self.orphans = [], set(), set(), False
- def update(self, text, overlays, statuses):
+ def update(self, text, overlays, statuses, orphans=False):
self.terms, self.overlays, self.statuses = parse_query(text), overlays, statuses
+ self.orphans = orphans
self.invalidateFilter()
def filterAcceptsRow(self, source_row, _parent):
row = self.sourceModel().rows[source_row]
- return matches(row, self.terms, self.overlays, self.statuses)
+ return matches(row, self.terms, self.overlays, self.statuses, self.orphans)
def detail_html(row):
@@ -214,16 +222,15 @@ class PackagesTab(QWidget):
self.query.textChanged.connect(self.refilter)
self.chips = {}
chips = QHBoxLayout()
- for label, group in (("overlay", ("personal", "pentesting", "upstream")), ("status", STATUSES)):
+ for label, group in (("overlay", ("personal", "pentesting", "upstream")), ("status", STATUSES),
+ ("maint", ("orphaned",))): # the query prefix's name
chips.addWidget(group_label(label))
for name in group:
self.chips[name] = chip(name, STATUS_COLOR.get(name) if group is STATUSES else None)
self.chips[name].toggled.connect(self.refilter)
chips.addWidget(self.chips[name])
- chips.addSpacing(16)
+ chips.addSpacing(12)
chips.addStretch()
- self.count = QLabel()
- chips.addWidget(self.count)
self.table = QTableView(sortingEnabled=True)
self.table.setModel(self.proxy)
@@ -279,8 +286,12 @@ class PackagesTab(QWidget):
for key, slot in (("Ctrl+A", self.select_visible), ("Ctrl+Shift+A", self.clear))]
self.loaded = False
+ self.count = QLabel()
+ search = QHBoxLayout()
+ search.addWidget(self.query)
+ search.addWidget(self.count)
layout = QVBoxLayout(self)
- layout.addWidget(self.query)
+ layout.addLayout(search)
layout.addLayout(chips)
layout.addWidget(split, 1)
self.model.dataChanged.connect(lambda *_: self.update_count())
@@ -300,7 +311,8 @@ class PackagesTab(QWidget):
def refilter(self):
on = {n for n, b in self.chips.items() if b.isChecked()}
- self.proxy.update(self.query.text(), on - set(STATUSES), on & set(STATUSES))
+ self.proxy.update(self.query.text(), on - set(STATUSES) - {"orphaned"}, on & set(STATUSES),
+ "orphaned" in on)
self.update_count()
def update_count(self):
diff --git a/sb_builder/ui.py b/sb_builder/ui.py
index 82ddc6d..3f02515 100644
--- a/sb_builder/ui.py
+++ b/sb_builder/ui.py
@@ -50,7 +50,7 @@ SP = QStyle.StandardPixmap
# ── filter bars: search field, group labels, chips ──────────────────────────
CHIP_STYLE = ("QPushButton { background: palette(button); border: 1px solid palette(mid);"
- " border-radius: 11px; padding: 3px 12px; }"
+ " border-radius: 11px; padding: 3px 10px; }"
"QPushButton:hover { border-color: palette(highlight); }"
"QPushButton:checked { background: palette(highlight);"
" color: palette(highlighted-text); border-color: palette(highlight); }")
diff --git a/tests/test_gui.py b/tests/test_gui.py
index 9d3e565..d5a1128 100644
--- a/tests/test_gui.py
+++ b/tests/test_gui.py
@@ -151,7 +151,7 @@ INVENTORY = {"columns": COLS, "rows": [
requires=["go"]),
inv_row("personal/foo", built="1.0", result="ok", hints={"DELREQUIRES": "x"}, requires=["x", "y"]),
inv_row("pentesting/nmap", built="7.9", result="bad"),
- inv_row("network/curl"),
+ inv_row("network/curl", maintainer="orphaned - no maintainer"),
]}
RESULTS = {"rows": [["games/pcsx2", "failed", 1789992366, None, True], # newest first, as the helper
["development/whisper.cpp", "aborted", 1789753147, None, False],
@@ -260,6 +260,8 @@ class FilterTest(unittest.TestCase):
self.assertEqual(self.names(overlays={"personal", "pentesting"}), ["foo", "nmap"])
self.assertEqual(self.names(overlays={"upstream"}, statuses={"never", "failed"}), ["curl"])
self.assertEqual(self.names("cat:misc", statuses={"ok"}), [])
+ terms = pk.parse_query("")
+ self.assertEqual([n for n, r in self.rows.items() if pk.matches(r, terms, orphans=True)], ["curl"])
class MeterTest(unittest.TestCase):
@@ -376,6 +378,9 @@ class SmokeTest(unittest.TestCase):
tab.chips["failed"].setChecked(True)
self.assertEqual(tab.proxy.rowCount(), 1)
tab.chips["failed"].setChecked(False)
+ tab.chips["orphaned"].setChecked(True)
+ self.assertEqual(tab.proxy.rowCount(), 1) # curl
+ tab.chips["orphaned"].setChecked(False)
started = []
tab.start_requested.connect(started.append)
tab.act("update")