feat(notify): value-first emoji rows
Switch the summary to the layout jblu picked: each counter renders as
"emoji value label", grouped two per line and joined by a middle dot.
Scanned 42 torrents in 12s
🗑️ 7 removed · 👻 4 orphaned
⏭️ 5 ignored · 🐣 3 premature
🛡️ 10 protected · 🌐 20 public
- the value leads, so the numbers are the first thing the eye lands on
- html style now bolds the values instead of the labels
- "Non-protected" reads as "public", matching the non_protected_tag
- use_emoji now defaults to true; false gives the plain variant
- dry runs read "7 would be removed"
This commit is contained in:
1 parent
5d9da0d22b
commit
dfce115fd8
5 files changed
+90
-51
No files matched your search
+49
-19
@@ -190,16 +190,28 @@ class TestQbitmaid(unittest.TestCase):
|
||||
self.assertEqual(stats["runtime"], "unknown")
|
||||
|
||||
def test_build_summary_plain(self):
|
||||
title, body = build_summary(FakeQbt(), style="plain")
|
||||
title, body = build_summary(FakeQbt(), style="plain", use_emoji=False)
|
||||
self.assertEqual(title, "qbit-maid: 7 torrents removed")
|
||||
self.assertEqual(
|
||||
body,
|
||||
"Scanned 42 torrents in 12s\n"
|
||||
"\n"
|
||||
"Removed 7 \u00b7 Orphaned 4\n"
|
||||
"Ignored 5 \u00b7 Premature 3\n"
|
||||
"7 removed \u00b7 4 orphaned\n"
|
||||
"5 ignored \u00b7 3 premature\n"
|
||||
"\n"
|
||||
"Protected 10 \u00b7 Non-protected 20",
|
||||
"10 protected \u00b7 20 public",
|
||||
)
|
||||
|
||||
def test_build_summary_plain_emoji(self):
|
||||
_, body = build_summary(FakeQbt(), style="plain", use_emoji=True)
|
||||
self.assertEqual(
|
||||
body,
|
||||
"Scanned 42 torrents in 12s\n"
|
||||
"\n"
|
||||
"\U0001F5D1\uFE0F 7 removed \u00b7 \U0001F47B 4 orphaned\n"
|
||||
"\u23ED\uFE0F 5 ignored \u00b7 \U0001F423 3 premature\n"
|
||||
"\n"
|
||||
"\U0001F6E1\uFE0F 10 protected \u00b7 \U0001F310 20 public",
|
||||
)
|
||||
|
||||
def test_build_summary_plain_has_no_leading_indent(self):
|
||||
@@ -224,33 +236,47 @@ class TestQbitmaid(unittest.TestCase):
|
||||
for row in rows:
|
||||
self.assertEqual(row.count("\u00b7"), 1, row)
|
||||
|
||||
def test_build_summary_value_leads_label(self):
|
||||
"""The number comes first so it is the first thing the eye lands on."""
|
||||
_, body = build_summary(FakeQbt(), style="plain", use_emoji=False)
|
||||
self.assertIn("7 removed", body)
|
||||
self.assertIn("20 public", body)
|
||||
self.assertNotIn("Removed 7", body)
|
||||
self.assertNotIn("Non-protected", body)
|
||||
|
||||
def test_build_summary_html(self):
|
||||
title, body = build_summary(FakeQbt(), style="html")
|
||||
self.assertEqual(title, "qbit-maid: 7 torrents removed")
|
||||
self.assertIn("Scanned <b>42</b> torrents in <b>12s</b>", body)
|
||||
self.assertIn("<b>Removed</b> 7", body)
|
||||
self.assertIn("<b>Non-protected</b> 20", body)
|
||||
self.assertIn("<b>7</b> removed", body)
|
||||
self.assertIn("<b>20</b> public", body)
|
||||
self.assertNotIn("---", title)
|
||||
|
||||
def test_build_summary_emoji(self):
|
||||
_, plain = build_summary(FakeQbt(), style="plain", use_emoji=True)
|
||||
self.assertIn("\U0001F5D1\uFE0F Removed 7", plain)
|
||||
self.assertIn("\U0001F47B Orphaned 4", plain)
|
||||
self.assertIn("\U0001F5D1\uFE0F 7 removed", plain)
|
||||
self.assertIn("\U0001F47B 4 orphaned", plain)
|
||||
_, html = build_summary(FakeQbt(), style="html", use_emoji=True)
|
||||
self.assertIn("\U0001F5D1\uFE0F <b>Removed</b> 7", html)
|
||||
self.assertIn("\U0001F5D1\uFE0F <b>7</b> removed", html)
|
||||
|
||||
def test_build_summary_emoji_off_has_no_emoji(self):
|
||||
_, body = build_summary(FakeQbt(), style="plain", use_emoji=False)
|
||||
for marker in ("\U0001F5D1", "\U0001F47B", "\u23ED", "\U0001F423",
|
||||
"\U0001F6E1", "\U0001F310"):
|
||||
self.assertNotIn(marker, body)
|
||||
|
||||
def test_build_summary_delete_disabled(self):
|
||||
title, body = build_summary(FakeQbt(delete_torrents=False))
|
||||
self.assertEqual(title, "qbit-maid: 7 torrents would be removed")
|
||||
self.assertIn("Would remove 7", body)
|
||||
self.assertNotIn("Removed", body)
|
||||
|
||||
def test_build_summary_does_not_repeat_title_in_body(self):
|
||||
"""The title already states the outcome; the body must not restate it."""
|
||||
title, body = build_summary(FakeQbt(), style="plain")
|
||||
self.assertIn("7 would be removed", body)
|
||||
self.assertNotIn("7 removed", body)
|
||||
|
||||
def test_build_summary_body_does_not_repeat_title_phrase(self):
|
||||
"""The title carries the outcome; the body must not restate it verbatim."""
|
||||
title, body = build_summary(FakeQbt(), style="plain", use_emoji=False)
|
||||
self.assertNotIn("torrents removed", body)
|
||||
self.assertIn("Removed 7", body)
|
||||
self.assertNotIn("torrents would be removed", body)
|
||||
self.assertIn("7 removed", body)
|
||||
self.assertTrue(title.endswith("7 torrents removed"))
|
||||
|
||||
def test_build_summary_nothing_to_do(self):
|
||||
@@ -276,16 +302,20 @@ class TestQbitmaid(unittest.TestCase):
|
||||
def test_get_notify_options_defaults(self):
|
||||
opts = get_notify_options(FakeQbt())
|
||||
self.assertEqual(opts["style"], "plain")
|
||||
self.assertFalse(opts["use_emoji"])
|
||||
self.assertTrue(opts["use_emoji"])
|
||||
self.assertFalse(opts["include_hostname"])
|
||||
self.assertTrue(opts["notify_on_empty"])
|
||||
self.assertIsNone(opts["priority"])
|
||||
self.assertIsNone(opts["sound"])
|
||||
|
||||
def test_get_notify_options_emoji_can_be_disabled(self):
|
||||
opts = get_notify_options(FakeQbt(config={"pushover": {"use_emoji": False}}))
|
||||
self.assertFalse(opts["use_emoji"])
|
||||
|
||||
def test_get_notify_options_overrides(self):
|
||||
fake = FakeQbt(config={"pushover": {
|
||||
"format": "HTML",
|
||||
"use_emoji": True,
|
||||
"use_emoji": False,
|
||||
"include_hostname": True,
|
||||
"notify_on_empty": False,
|
||||
"priority": 2,
|
||||
@@ -295,7 +325,7 @@ class TestQbitmaid(unittest.TestCase):
|
||||
}})
|
||||
opts = get_notify_options(fake)
|
||||
self.assertEqual(opts["style"], "html")
|
||||
self.assertTrue(opts["use_emoji"])
|
||||
self.assertFalse(opts["use_emoji"])
|
||||
self.assertTrue(opts["include_hostname"])
|
||||
self.assertFalse(opts["notify_on_empty"])
|
||||
self.assertEqual(opts["priority"], 2)
|
||||
|
||||
Reference in new issue
Block a user