Repository: pimutils/todoman
Pull request #257 (2017-06-17 18:53:46)

Move colour formatting code from models into formatters
Models shouldn't do terminal-specific encoding.


Patch:
diff --git a/todoman/formatters.py b/todoman/formatters.py
index 7e70228..980baa9 100644
--- a/todoman/formatters.py
+++ b/todoman/formatters.py
@@ -10,6 +10,23 @@ from dateutil.tz import tzlocal
 from tabulate import tabulate
 
 
+def rgb_to_ansi(colour):
+    """
+    Convert a string containing an RGB colour to ANSI escapes
+    """
+    if not colour or not colour.startswith('#'):
+        return
+
+    r, g, b = colour[1:3], colour[3:5], colour[5:8]
+
+    if not len(r) == len(g) == len(b) == 2:
+        return
+
+    return '\33[38;2;{!s};{!s};{!s}m'.format(
+        int(r, 16), int(g, 16), int(b, 16)
+    )
+
+
 class DefaultFormatter:
 
     def __init__(self, date_format='%Y-%m-%d', time_format='%H:%M',
@@ -166,8 +183,10 @@ class DefaultFormatter:
         return datetime.datetime.fromtimestamp(mktime(rv))
 
     def format_database(self, database):
-        return '{}@{}'.format(database.color_ansi or '',
-                              click.style(database.name))
+        return '{}@{}'.format(
+            rgb_to_ansi(database.colour) or '',
+            click.style(database.name)
+        )
 
 
 class HumanizedFormatter(DefaultFormatter):
diff --git a/todoman/model.py b/todoman/model.py
index 99128de..9c4777e 100644
--- a/todoman/model.py
+++ b/todoman/model.py
@@ -831,8 +831,8 @@ class List:
 
     def __init__(self, name, path, colour=None):
         self.path = path
-        self.name = name or List.name_for_path(path)
-        self.colour = colour or List.colour_for_path(self.path)
+        self.name = name
+        self.colour = colour
 
     @staticmethod
     def colour_for_path(path):
@@ -850,25 +850,6 @@ class List:
         except (OSError, IOError):
             return split(normpath(path))[1]
 
-    @cached_property
-    def color_rgb(self):
-        color = self.colour
-        if not color or not color.startswith('#'):
-            return
-
-        r = color[1:3]
-        g = color[3:5]
-        b = color[5:8]
-
-        if len(r) == len(g) == len(b) == 2:
-            return int(r, 16), int(g, 16), int(b, 16)
-
-    @cached_property
-    def color_ansi(self):
-        rv = self.color_rgb
-        if rv:
-            return '\33[38;2;{!s};{!s};{!s}m'.format(*rv)
-
     def __eq__(self, other):
         if isinstance(other, List):
             return self.name == other.name


Test patch:
diff --git a/tests/test_formatter.py b/tests/test_formatter.py
index 74693cc..7316f56 100644
--- a/tests/test_formatter.py
+++ b/tests/test_formatter.py
@@ -5,6 +5,7 @@ import pytz
 from freezegun import freeze_time
 
 from todoman.cli import cli
+from todoman.formatters import rgb_to_ansi
 
 
 @pytest.mark.parametrize('interval', [
@@ -132,3 +133,11 @@ def test_formatting_parsing_consitency(default_formatter):
 
     formatted = default_formatter.format_datetime(dt)
     assert default_formatter.parse_datetime(formatted) == dt
+
+
+def test_rgb_to_ansi():
+    assert rgb_to_ansi(None) is None
+    assert rgb_to_ansi('#8ab6d') is None
+    assert rgb_to_ansi('#8ab6d2f') is None
+    assert rgb_to_ansi('red') is None
+    assert rgb_to_ansi('#8ab6d2') == '\x1b[38;2;138;182;210m'
diff --git a/tests/test_model.py b/tests/test_model.py
index aff982b..2604349 100644
--- a/tests/test_model.py
+++ b/tests/test_model.py
@@ -92,8 +92,6 @@ def test_list_colour(tmpdir):
     list_ = next(db.lists())
 
     assert list_.colour == '#8ab6d2'
-    assert list_.color_rgb == (138, 182, 210)
-    assert list_.color_ansi == '\x1b[38;2;138;182;210m'
 
 
 def test_list_no_colour(tmpdir):
@@ -103,8 +101,6 @@ def test_list_no_colour(tmpdir):
     list_ = next(db.lists())
 
     assert list_.colour is None
-    assert list_.color_rgb is None
-    assert list_.color_ansi is None
 
 
 def test_database_priority_sorting(create, todos):