Repository: pimutils/khal
Pull request #812 (2018-09-18 18:14:45)

Add coloring priority system as per #526
Adds a priority property to each calendar, which controls how day coloring is done. The day is colored based on the color of the calendar with the highest priority, falling back to the current system in the case of multiple calendars having the same highest priority.

I've updated the tests, and as far as I can tell all the tests pertaining to the code are passing (which is somewhat tricky with the current ```dateutil``` problems). 

Currently there don't appear to be any tests related to coloring (as far as I can tell from codecov), so I thought I'd just check that there wasn't a reason for that before adding some.

As for the documentation, because it's a config change the autogenerated documentation for the [calendars] has been updated. I'm not sure if this should also be mentioned in other sections as well however. I'd also appreciate somebody looking over the wording of the documentation since I'm not entirely happy with how it describes the options.


Patch:
diff --git a/AUTHORS.txt b/AUTHORS.txt
index 04c93b8..42d95d3 100644
--- a/AUTHORS.txt
+++ b/AUTHORS.txt
@@ -34,3 +34,4 @@ August Lindberg
 Thomas Kluyver - thomas [at] kluyver [dot] me [dot] uk
 Tobias Brummer - hallo [at] t0bybr.de - https://t0bybr.de
 Amanda Hickman - amanda [at] velociraptor [dot] info
+Raef Coles - raefcoles [at] gmail [dot] com
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 4f2f3b8..0f0d3b7 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -52,6 +52,11 @@ not released yet
 
 * NEW python 3.7 is now officially supported.
 
+* NEW configuration option [[per_calendar]]priority = int (default 10). If
+  multiple calendars events are on the same day, the day will be colored with
+  the color of the calendar with highest priority. If multiple calendars have
+  the same highest priority, it falls back to the previous system.
+
 0.9.8
 =====
 released 2017-10-05
diff --git a/khal/calendar_display.py b/khal/calendar_display.py
index 9aa0a48..9c41741 100644
--- a/khal/calendar_display.py
+++ b/khal/calendar_display.py
@@ -59,15 +59,37 @@ def get_calendar_color(calendar, default_color, collection):
     return collection._calendars[calendar]['color']
 
 
+def get_color_list(calendars, default_color, collection):
+    """Get the list of possible colors for the day, taking into account priority
+    """
+    dcolors = list(
+        map(lambda x: (get_calendar_color(x, default_color, collection),
+                       collection._calendars[x]['priority']), calendars)
+    )
+
+    dcolors.sort(key=lambda x: x[1], reverse=True)
+
+    maxPriority = dcolors[0][1]
+    dcolors = list(
+        filter(lambda x: x[1] == maxPriority, dcolors)
+    )
+
+    dcolors = list(
+        map(lambda x: x[0], dcolors)
+    )
+
+    dcolors = list(set(dcolors))
+
+    return dcolors
+
+
 def str_highlight_day(
         day, calendars, hmethod, default_color, multiple, color, bold_for_light_color, collection):
     """returns a string with day highlighted according to configuration
     """
     dstr = str(day.day).rjust(2)
     if color == '':
-        dcolors = list(set(
-            map(lambda x: get_calendar_color(x, default_color, collection), calendars)
-        ))
+        dcolors = get_color_list(calendars, default_color, collection)
         if len(dcolors) > 1:
             if multiple == '':
                 if hmethod == "foreground" or hmethod == "fg":
diff --git a/khal/cli.py b/khal/cli.py
index faf1e3c..f8d3c4a 100644
--- a/khal/cli.py
+++ b/khal/cli.py
@@ -162,6 +162,7 @@ def build_collection(conf, selection):
                     'path': cal['path'],
                     'readonly': cal['readonly'],
                     'color': cal['color'],
+                    'priority': cal['priority'],
                     'ctype': cal['type'],
                 }
         collection = khalendar.CalendarCollection(
diff --git a/khal/khalendar/khalendar.py b/khal/khalendar/khalendar.py
index 5cfdd6e..dfb2d15 100644
--- a/khal/khalendar/khalendar.py
+++ b/khal/khalendar/khalendar.py
@@ -65,6 +65,7 @@ class CalendarCollection(object):
                  default_color: str='',
                  multiple: str='',
                  color: str='',
+                 priority: int=10,
                  highlight_event_days: bool=False,
                  locale: Dict[str, Any]=dict(),
                  dbpath: Optional[str]=None,
@@ -93,6 +94,7 @@ class CalendarCollection(object):
         self.default_color = default_color
         self.multiple = multiple
         self.color = color
+        self.priority = priority
         self.highlight_event_days = highlight_event_days
         self._locale = locale
         self._backend = backend.SQLiteDb(self.names, dbpath, self._locale)
diff --git a/khal/settings/khal.spec b/khal/settings/khal.spec
index 9b39e80..056fba7 100644
--- a/khal/settings/khal.spec
+++ b/khal/settings/khal.spec
@@ -38,6 +38,12 @@ path = expand_path(default=None)
 
 color = color(default='auto')
 
+# When coloring days, the color will be determined based on the calendar with
+# the highest priority. If the priorities are equal, then the "multiple" color
+# will be used.
+
+priority = integer(default=10)
+
 # setting this to *True*, will keep khal from making any changes to this
 # calendar
 readonly = boolean(default=False)


Test patch:
diff --git a/tests/cal_display_test.py b/tests/cal_display_test.py
index 9c76ddb..e3b247d 100644
--- a/tests/cal_display_test.py
+++ b/tests/cal_display_test.py
@@ -4,7 +4,8 @@ import platform
 import unicodedata
 
 import pytest
-from khal.calendar_display import getweeknumber, str_week, vertical_month
+from khal.calendar_display import (getweeknumber, str_week, vertical_month,
+                                   get_calendar_color, get_color_list)
 
 today = dt.date.today()
 yesterday = today - dt.timedelta(days=1)
@@ -33,6 +34,68 @@ def test_str_week():
     assert str_week(week, bday) == ' 6  7 \x1b[7m 8\x1b[0m  9 10 11 12 13 '
 
 
+class testCollection():
+    def __init__(self):
+        self._calendars = {}
+
+    def addCalendar(self, name, color, priority):
+        self._calendars[name] = {'color': color, 'priority': priority}
+
+
+def test_get_calendar_color():
+
+    exampleCollection = testCollection()
+    exampleCollection.addCalendar('testCalendar1', 'dark red',    20)
+    exampleCollection.addCalendar('testCalendar2', 'light green', 10)
+    exampleCollection.addCalendar('testCalendar3', '',            10)
+
+    assert get_calendar_color('testCalendar1', 'light blue', exampleCollection) == 'dark red'
+    assert get_calendar_color('testCalendar2', 'light blue', exampleCollection) == 'light green'
+
+    # test default color
+    assert get_calendar_color('testCalendar3', 'light blue', exampleCollection) == 'light blue'
+
+
+def test_get_color_list():
+
+    exampleCalendarList = ['testCalendar1', 'testCalendar2']
+
+    # test different priorities
+    exampleCollection1 = testCollection()
+    exampleCollection1.addCalendar('testCalendar1', 'dark red',    20)
+    exampleCollection1.addCalendar('testCalendar2', 'light green', 10)
+
+    testList1 = get_color_list(exampleCalendarList, 'light_blue', exampleCollection1)
+    assert 'dark red' in testList1
+    assert len(testList1) == 1
+
+    # test same priorities
+    exampleCollection2 = testCollection()
+    exampleCollection2.addCalendar('testCalendar1', 'dark red',    20)
+    exampleCollection2.addCalendar('testCalendar2', 'light green', 20)
+
+    testList2 = get_color_list(exampleCalendarList, 'light_blue', exampleCollection2)
+    assert 'dark red' in testList2
+    assert 'light green' in testList2
+    assert len(testList2) == 2
+
+    # test duplicated colors
+    exampleCollection3 = testCollection()
+    exampleCollection3.addCalendar('testCalendar1', 'dark red',    20)
+    exampleCollection3.addCalendar('testCalendar2', 'dark red',    20)
+
+    testList3 = get_color_list(exampleCalendarList, 'light_blue', exampleCollection3)
+    assert len(testList3) == 1
+
+    # test indexing operator (required by str_highlight_day())
+    exampleCollection4 = testCollection()
+    exampleCollection4.addCalendar('testCalendar1', 'dark red',    20)
+    exampleCollection4.addCalendar('testCalendar2', 'dark red',    20)
+
+    testList3 = get_color_list(exampleCalendarList, 'light_blue', exampleCollection4)
+    assert testList3[0] == 'dark red'
+
+
 example1 = [
     '\x1b[1m    Mo Tu We Th Fr Sa Su \x1b[0m',
     '\x1b[1mDec \x1b[0m28 29 30  1  2  3  4 ',
diff --git a/tests/configs/small.conf b/tests/configs/small.conf
index e9c59a1..9981df2 100644
--- a/tests/configs/small.conf
+++ b/tests/configs/small.conf
@@ -3,6 +3,7 @@
   [[home]]
     path = ~/.calendars/home/
     color = dark green
+    priority = 20
 
   [[work]]
     path = ~/.calendars/work/
diff --git a/tests/settings_test.py b/tests/settings_test.py
index abffdbb..037abac 100644
--- a/tests/settings_test.py
+++ b/tests/settings_test.py
@@ -26,9 +26,9 @@ class TestSettings(object):
         comp_config = {
             'calendars': {
                 'home': {'path': os.path.expanduser('~/.calendars/home/'),
-                         'readonly': False, 'color': None, 'type': 'calendar'},
+                         'readonly': False, 'color': None, 'priority': 10, 'type': 'calendar'},
                 'work': {'path': os.path.expanduser('~/.calendars/work/'),
-                         'readonly': False, 'color': None, 'type': 'calendar'},
+                         'readonly': False, 'color': None, 'priority': 10, 'type': 'calendar'},
             },
             'sqlite': {'path': os.path.expanduser('~/.local/share/khal/khal.db')},
             'locale': LOCALE_BERLIN,
@@ -60,10 +60,10 @@ class TestSettings(object):
         comp_config = {
             'calendars': {
                 'home': {'path': os.path.expanduser('~/.calendars/home/'),
-                         'color': 'dark green', 'readonly': False,
+                         'color': 'dark green', 'readonly': False, 'priority': 20,
                          'type': 'calendar'},
                 'work': {'path': os.path.expanduser('~/.calendars/work/'),
-                         'readonly': True, 'color': None,
+                         'readonly': True, 'color': None, 'priority': 10,
                          'type': 'calendar'}},
             'sqlite': {'path': os.path.expanduser('~/.local/share/khal/khal.db')},
             'locale': {