Repository: pimutils/khal
Pull request #664 (2017-06-07 16:16:07)

Do not assume that if only one VEVENT is present, it's a proto event
This does not, however, fix the case where that single VEVENT also contains an RRULE (or RDATE).


Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index bc1975d..0f6547f 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -16,6 +16,8 @@ unreleased
 * FIX checking for RRULEs we understand
 * FIX after editing an event in ikhal, make sure both the calendar and the
   eventcolumn are focused on the new date
+* FIX no more crashes if only one event which is an overwritten instance is
+  present in an .ics file
 
 * CHANGE the symbol used for indicating a recurring event now has a space in
   front of it, also the ascii version changed to `(R)`
diff --git a/khal/khalendar/backend.py b/khal/khalendar/backend.py
index 739c4a5..6cb6794 100644
--- a/khal/khalendar/backend.py
+++ b/khal/khalendar/backend.py
@@ -363,21 +363,15 @@ class SQLiteDb(object):
             if dtype == DATE:
                 dbstart = utils.to_unix_time(dtstart)
                 dbend = utils.to_unix_time(dtend)
-                if rec_id is not None:
-                    rec_inst = utils.to_unix_time(rec_id.dt)
-                    ref = rec_inst
-                else:
-                    rec_inst = dbstart
-                    ref = PROTO
             else:
                 dbstart = utils.to_unix_time(dtstart)
                 dbend = utils.to_unix_time(dtend)
 
-                if rec_id is not None:
-                    ref = rec_inst = str(utils.to_unix_time(rec_id.dt))
-                else:
-                    rec_inst = dbstart
-                    ref = PROTO
+            if rec_id is not None:
+                ref = rec_inst = str(utils.to_unix_time(rec_id.dt))
+            else:
+                rec_inst = dbstart
+                ref = PROTO
 
             if thisandfuture:
                 recs_sql_s = (
diff --git a/khal/khalendar/event.py b/khal/khalendar/event.py
index 0de3df3..4bdda95 100644
--- a/khal/khalendar/event.py
+++ b/khal/khalendar/event.py
@@ -120,20 +120,18 @@ class Event(object):
         assert isinstance(events_list, list)
 
         vevents = dict()
-        if len(events_list) == 1:
-            vevents['PROTO'] = events_list[0]  # TODO set mutable = False
-        else:
-            for event in events_list:
-                if 'RECURRENCE-ID' in event:
-                    if invalid_timezone(event['RECURRENCE-ID']):
-                        default_timezone = kwargs['locale']['default_timezone']
-                        recur_id = default_timezone.localize(event['RECURRENCE-ID'].dt)
-                        ident = str(to_unix_time(recur_id))
-                    else:
-                        ident = str(to_unix_time(event['RECURRENCE-ID'].dt))
-                    vevents[ident] = event
+        for event in events_list:
+            if 'RECURRENCE-ID' in event:
+                if invalid_timezone(event['RECURRENCE-ID']):
+                    default_timezone = kwargs['locale']['default_timezone']
+                    recur_id = default_timezone.localize(event['RECURRENCE-ID'].dt)
+                    ident = str(to_unix_time(recur_id))
                 else:
-                    vevents['PROTO'] = event
+                    ident = str(to_unix_time(event['RECURRENCE-ID'].dt))
+                vevents[ident] = event
+            else:
+                vevents['PROTO'] = event
+
         if ref is None:
             ref = 'PROTO'
 


Test patch:
diff --git a/tests/backend_test.py b/tests/backend_test.py
index bde20f4..f1e7249 100644
--- a/tests/backend_test.py
+++ b/tests/backend_test.py
@@ -178,6 +178,26 @@ def test_event_rrule_recurrence_id_update_with_exclude():
     assert events[4].start == BERLIN.localize(datetime(2014, 8, 4, 7, 0))
 
 
+def test_event_recuid_no_master():
+    """
+    test for events which have a RECUID component, but the master event is
+    not present in the same file
+    """
+    dbi = backend.SQLiteDb([calname], ':memory:', locale=LOCALE_BERLIN)
+    dbi.update(_get_text('event_dt_recuid_no_master'),
+               href='12345.ics', etag='abcd', calendar=calname)
+    events = dbi.get_floating(
+        datetime(2017, 3, 1, 0, 0), datetime(2017, 4, 1, 0, 0),
+    )
+    events = sorted(events, key=lambda x: x.start)
+    assert len(events) == 1
+    assert events[0].start == datetime(2017, 3, 29, 16)
+    assert events[0].end == datetime(2017, 3, 29, 16, 25)
+    assert events[0].format(
+        '{title}', relative_to=date(2017, 3, 29)
+    ) == 'Infrastructure Planning\x1b[0m'
+
+
 def test_no_valid_timezone():
     dbi = backend.SQLiteDb([calname], ':memory:', locale=LOCALE_BERLIN)
     dbi.update(_get_text('event_dt_local_missing_tz'),
diff --git a/tests/ics/event_dt_recuid_no_master.ics b/tests/ics/event_dt_recuid_no_master.ics
new file mode 100644
index 0000000..5e2b8e0
--- /dev/null
+++ b/tests/ics/event_dt_recuid_no_master.ics
@@ -0,0 +1,12 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+DTSTART:20170329T160000
+DTEND:20170329T162500
+DTSTAMP:20170322T171834Z
+RECURRENCE-ID:20170329T160000
+SUMMARY:Infrastructure Planning
+UID:406336
+END:VEVENT
+END:VCALENDAR
+