diff --git a/PKG-INFO b/PKG-INFO
index f8faa0a..00fcae1 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,10 +1,10 @@
-Metadata-Version: 1.1
+Metadata-Version: 2.1
 Name: etmtk
-Version: 3.2.30
+Version: 3.2.39
 Summary: event and task manager
-Home-page: http://people.duke.edu/~dgraham/etmtk
+Home-page: https://github.com/dagraham/etm-tk
 Author: Daniel A Graham
-Author-email: daniel.graham@duke.edu
+Author-email: dnlgrhm@gmail.com
 License: License :: OSI Approved :: GNU General Public License (GPL)
 Description: manage events and tasks using simple text files
 Platform: Any
@@ -26,6 +26,8 @@ Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.4
 Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
 Classifier: Topic :: Office/Business
 Classifier: Topic :: Office/Business :: News/Diary
 Classifier: Topic :: Office/Business :: Scheduling
+Provides-Extra: icalendar
diff --git a/debian/changelog b/debian/changelog
index 1a3aa0e..709151b 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,4 +1,4 @@
-etm (3.2.30-2) UNRELEASED; urgency=medium
+etm (3.2.39-1) UNRELEASED; urgency=medium
 
   [ Ondřej Nový ]
   * d/copyright: Use https protocol in Format field
@@ -14,8 +14,9 @@ etm (3.2.30-2) UNRELEASED; urgency=medium
   [ Debian Janitor ]
   * Use secure URI in Homepage field.
   * Bump debhelper from old 9 to 12.
+  * New upstream release.
 
- -- Ondřej Nový <onovy@debian.org>  Tue, 27 Feb 2018 21:13:47 +0100
+ -- Ondřej Nový <onovy@debian.org>  Sat, 29 May 2021 06:25:09 -0000
 
 etm (3.2.30-1) unstable; urgency=medium
 
diff --git a/etm b/etm
index 1d609f5..e668697 100755
--- a/etm
+++ b/etm
@@ -8,7 +8,7 @@ sys.path.append(lib_path)
 # from etmTk import view
 from etmTk.data import setup_logging
 import codecs
-import yaml
+import ruamel.yaml as yaml
 import locale
 import gettext
 import platform
diff --git a/etmTk/CHANGES b/etmTk/CHANGES
index 2afb073..ba14180 100644
--- a/etmTk/CHANGES
+++ b/etmTk/CHANGES
@@ -1,134 +1,381 @@
-# Recent changes as of Fri Mar 31 09:35:30 EDT 2017:
-- 8 minutes ago (HEAD -> master, tag: 3.2.30) Dan Graham
+# Recent changes as of Thu Oct  3 17:41:10 EDT 2019:
+- 5 minutes ago (HEAD -> master, tag: 3.2.39) Dan Graham
+    7b37fa3 2019-10-03 17:36:24 -0400
+    Merge branch 'master' of https://github.com/dagraham/etm-tk Tagged
+    version 3.2.39 [2019-10-03 17:36:24 -0400].
+
+- 7 minutes ago Dan Graham
+    91a1676 2019-10-03 17:34:34 -0400
+    cleanup
+
+- 23 minutes ago (origin/master) dagraham
+    eb03f6f 2019-10-03 17:18:33 -0400
+    Merge pull request #70 from cipharius/patch/fix-font-colors
+
+    Fix broken Tk Treeview item fg/bg colors
+- 27 minutes ago dagraham
+    6232ac5 2019-10-03 17:14:28 -0400
+    Merge pull request #71 from
+    cipharius/feature/handle-item-linebreaks
+
+    Wrap item summary newlines in treeview
+- 3 days ago cipharius
+    bdc452e 2019-10-01 10:28:43 +0300
+    Wrap item summary newlines in treeview
+
+- 3 days ago cipharius
+    1199f9e 2019-10-01 10:02:23 +0300
+    Fix broken tkinter Treeview item fg/bg colors
+
+    Since Python3.7.3, there has appeared an issue with tkinter
+    Treeview item colors. A workaround exists which can be found at:
+    https://bugs.python.org/issue36468
+
+- 9 months ago Dan Graham
+    753616c 2019-01-06 10:03:27 -0500
+    Merge branch 'master' of https://github.com/dagraham/etm-tk
+
+- 9 months ago Dan Graham
+    54cecda 2019-01-06 10:03:02 -0500
+    tagged 3.2.38
+
+- 9 months ago (tag: 3.2.38) dagraham
+    b8d991f 2019-01-06 09:09:33 -0500
+    Merge pull request #67 from kbulygin/master Tagged version 3.2.38
+    [2019-01-06 09:09:33 -0500].
+
+- 9 months ago (export-json) dagraham
+    81bbde2 2019-01-06 09:09:33 -0500
+    Merge pull request #67 from kbulygin/master
+
+     Fixing an UnboundLocalError, requiring ruamel.yaml
+- 9 months ago Kirill Bulygin
+    92377a3 2019-01-04 16:42:59 +0500
+    Changing PyYaml to (the actually used) ruamel.yaml
+
+    The current master revision in the parent repo is
+    https://github.com/dagraham/etm-tk/commit/47a8f34607d40ed75aa4072394d7be123cdef4b1
+    (dated 2018-12-03), so the version of ruamel.yaml (0.15.80) is
+    chosen as the current version for that date (according to
+    https://pypi.org/project/ruamel.yaml/#history).
+- 9 months ago Kirill Bulygin
+    09ae310 2019-01-04 15:58:22 +0500
+    Fixing an UnboundLocalError for "where"
+- 10 months ago (export) Dan Graham
+    47a8f34 2018-12-03 11:00:22 -0500
+    Merge branch 'master' of https://github.com/dagraham/etm-tk
+
+- 10 months ago Dan Graham
+    7a42a79 2018-12-03 10:59:15 -0500
+    Cleaned up export_json
+
+- 10 months ago Dan Graham
+    604f757 2018-12-03 10:37:11 -0500
+    export_json tweaks
+
+- 1 year ago (tag: v3.2.37) dagraham
+    4be27f4 2018-09-23 12:54:31 -0400
+    Add files via upload
+- 1 year ago Dan Graham
+    e2ccac0 2018-09-23 11:54:39 -0400
+    CHANGES
+
+- 1 year ago (tag: 3.2.37) Dan Graham
+    d00f1b7 2018-09-23 11:06:33 -0400
+    Replaced yaml.load with yaml.safe_load Tagged version 3.2.37
+    [2018-09-23 11:06:33 -0400].
+
+- 1 year, 6 months ago Dan Graham
+    e1a2798 2018-03-24 13:58:28 -0400
+    Merge branch 'master' of https://github.com/dagraham/etm-tk
+
+- 1 year, 6 months ago Dan Graham
+    7c2a271 2018-03-24 13:58:11 -0400
+    remove freq 'l' and append '+' to 's'
+
+- 1 year, 6 months ago Dan Graham
+    89e4e59 2018-03-23 18:02:06 -0400
+    Merge branch 'master' of https://github.com/dagraham/etm-tk
+
+- 1 year, 6 months ago Dan Graham
+    2f1c41a 2018-03-23 18:01:46 -0400
+    CHANGES, ...
+
+- 1 year, 6 months ago (tag: 3.2.36) Dan Graham
+    a219190 2018-03-23 17:36:34 -0400
+    removed extraneous print from export_json. Tagged version 3.2.36
+    [2018-03-23 17:36:34 -0400].
+
+- 1 year, 6 months ago Dan Graham
+    433cd0b 2018-03-23 17:36:34 -0400
+    removed extraneous print from export_json.
+
+- 1 year, 6 months ago Dan Graham
+    df29f61 2018-03-23 17:32:23 -0400
+    patched CHANGES
+
+- 1 year, 6 months ago Dan Graham
+    9a763aa 2018-03-23 17:23:31 -0400
+    Fixed bug in binding for countdown timer. Fixed bug in
+    export_json.
+
+- 1 year, 7 months ago (tag: 3.2.35) Dan Graham
+    6e8b778 2018-03-16 11:36:19 -0400
+    Cleaned up duplicate alerts in export_json Tagged version 3.2.35
+    [2018-03-16 11:36:19 -0400].
+
+- 1 year, 7 months ago Dan Graham
+    ea61619 2018-03-16 11:36:19 -0400
+    Cleaned up duplicate alerts in export_json
+
+- 1 year, 7 months ago (tag: 3.2.34) Dan Graham
+    f3abcfe 2018-03-13 16:23:15 -0400
+    CHANGES, ... Tagged version 3.2.34 [2018-03-13 16:23:15 -0400].
+
+- 1 year, 7 months ago (tag: 3.2.33p1) Dan Graham
+    5451786 2018-03-13 16:20:38 -0400
+    merged Tagged version 3.2.33p1 [2018-03-13 16:20:38 -0400].
+
+- 1 year, 7 months ago Dan Graham
+    b1ef507 2018-03-13 15:53:07 -0400
+    Merge branch 'master' of https://github.com/dagraham/etm-tk
+
+- 1 year, 7 months ago Dan Graham
+    f7794d0 2018-03-13 15:52:43 -0400
+    data.py
+
+- 1 year, 7 months ago Dan Graham
+    4fdde26 2018-03-13 15:50:42 -0400
+    CHANGES and docs/conf.py
+
+- 1 year, 7 months ago Dan Graham
+    977a78c 2018-03-13 15:48:47 -0400
+    Merge branch 'master' of https://github.com/dagraham/etm-tk
+
+- 1 year, 7 months ago (origin/export-json) Dan Graham
+    9b00ca7 2018-03-13 15:25:48 -0400
+    fixed bug in exporting alerts to json.
+
+- 1 year, 7 months ago (tag: 3.2.33) Dan Graham
+    fe9a866 2018-03-08 15:16:24 -0500
+    document tweaks Tagged version 3.2.33 [2018-03-08 15:16:24 -0500].
+
+- 1 year, 7 months ago (tag: 3.2.32p1) Dan Graham
+    8cd923d 2018-03-08 14:55:43 -0500
+    fixed bug in hsh2ical Tagged version 3.2.32p1 [2018-03-08 14:55:43
+    -0500].
+
+- 1 year, 7 months ago (tag: 3.2.32) Dan Graham
+    88ed8da 2018-03-08 14:39:39 -0500
+    export_json seems to give the desired result. Tagged version
+    3.2.32 [2018-03-08 14:39:39 -0500].
+
+- 1 year, 7 months ago Dan Graham
+    342219e 2018-03-08 14:39:39 -0500
+    export_json seems to give the desired result.
+
+- 1 year, 7 months ago Dan Graham
+    06e4b08 2018-03-05 10:59:06 -0500
+    More export_json tweaks.
+
+- 1 year, 7 months ago Dan Graham
+    3223760 2018-02-24 11:47:12 -0500
+    More tweaks on export_json.
+
+- 1 year, 7 months ago Dan Graham
+    17ad2b3 2018-02-22 12:52:52 -0500
+    export_json now switches 't' to 'c' in rrules.
+
+- 1 year, 7 months ago Dan Graham
+    7130d43 2018-02-22 11:48:46 -0500
+    Added a png file and cleaned up extraneous files.
+
+- 1 year, 7 months ago Dan Graham
+    a469ac1 2018-02-22 11:43:19 -0500
+    export_json now uses the new etm-mvc item types
+
+- 1 year, 7 months ago Dan Graham
+    067b2b3 2018-02-22 10:34:46 -0500
+    Ready to work on export-json
+
+- 1 year, 8 months ago Dan Graham
+    c93a7cb 2018-02-16 21:29:27 -0500
+    Auto-commit: saved ATKEYS.rst
+
+- 1 year, 8 months ago Dan Graham
+    8a46a91 2018-02-16 16:16:18 -0500
+    Auto-commit: saved ATKEYS.rst
+
+- 2 years, 2 months ago Dan Graham
+    d971c97 2017-08-10 17:13:17 -0400
+    Packaging changes requested for Debian/Solus
+
+    Updated path for appdata.xml to /usr/share/metainfo and removed
+    .xpm icon from package.
+
+- 2 years, 2 months ago (tag: 3.2.31) Dan Graham
+    050ec13 2017-08-10 16:27:53 -0400
+    Auto-commit: saved setup.py Tagged version 3.2.31 [2017-08-10
+    16:27:53 -0400].
+
+- 2 years, 6 months ago Dan Graham
+    3027d50 2017-03-31 11:04:15 -0400
+    Auto-commit: saved
+    fugitive:///Users/dag/etm-tk/.git//0/etmTk/data.py
+
+- 2 years, 6 months ago Dan Graham
+    0997bc9 2017-03-31 10:55:04 -0400
+    Auto-commit: saved
+    fugitive:///Users/dag/etm-tk/.git//0/etmTk/data.py
+
+- 2 years, 6 months ago Dan Graham
+    aad1112 2017-03-31 10:50:22 -0400
+    Auto-commit: saved
+    fugitive:///Users/dag/etm-tk/.git//0/etmTk/data.py
+
+- 2 years, 6 months ago Dan Graham
+    aac9e52 2017-03-31 10:48:22 -0400
+    Auto-commit: saved
+    fugitive:///Users/dag/etm-tk/.git//0/etmTk/data.py
+
+- 2 years, 6 months ago Dan Graham
+    ce692a2 2017-03-31 10:13:26 -0400
+    Auto-commit: saved /Users/dag/etm-tk/setup.py
+
+- 2 years, 6 months ago Dan Graham
+    cffbcdc 2017-03-31 09:39:54 -0400
+    Updated conf.py, CHANGES and etm.1
+
+- 2 years, 6 months ago (tag: 3.2.30) Dan Graham
     4d8cdae 2017-03-31 09:28:19 -0400
     Incorporated unicode fixes from Sebastian. Merged export-json.
     Tagged version 3.2.30 [2017-03-31 09:28:19 -0400].
 
-- 25 minutes ago Dan Graham
+- 2 years, 6 months ago Dan Graham
     400626d 2017-03-31 09:10:29 -0400
     Auto-commit: saved data.py
 
-- 29 minutes ago Dan Graham
+- 2 years, 6 months ago Dan Graham
     3706c2d 2017-03-31 09:07:09 -0400
     Auto-commit: saved data.py
 
-- 54 minutes ago Dan Graham
+- 2 years, 6 months ago Dan Graham
     8a66e16 2017-03-31 08:41:30 -0400
     Auto-commit: saved
     fugitive:///Users/dag/etm-tk/.git//0/etmTk/data.py
 
-- 67 minutes ago Dan Graham
+- 2 years, 6 months ago Dan Graham
     6d07cf0 2017-03-31 08:29:11 -0400
     Auto-commit: saved data.py
 
-- 68 minutes ago Dan Graham
+- 2 years, 6 months ago Dan Graham
     d2ae945 2017-03-31 08:28:13 -0400
     Auto-commit: saved data.py
 
-- 68 minutes ago Dan Graham
+- 2 years, 6 months ago Dan Graham
     fd01f91 2017-03-31 08:27:33 -0400
     Auto-commit: saved data.py
 
-- 77 minutes ago Dan Graham
+- 2 years, 6 months ago Dan Graham
     9f5cc21 2017-03-31 08:18:44 -0400
     Auto-commit: saved data.py
 
-- 15 hours ago (tag: 3.2.29) Dan Graham
+- 2 years, 6 months ago (tag: 3.2.29) Dan Graham
     44a7600 2017-03-30 19:03:50 -0400
     Auto-commit: saved data.py Tagged version 3.2.29 [2017-03-30
     19:03:50 -0400].
 
-- 15 hours ago Dan Graham
+- 2 years, 6 months ago Dan Graham
     bdd200f 2017-03-30 18:19:52 -0400
     Auto-commit: saved data.py
 
-- 16 hours ago (export-json) Dan Graham
+- 2 years, 6 months ago Dan Graham
     24c693d 2017-03-30 18:03:51 -0400
     Back to master
 
-- 16 hours ago Dan Graham
+- 2 years, 6 months ago Dan Graham
     41f3f98 2017-03-30 17:58:00 -0400
     Auto-commit: saved .gitignore
 
-- 16 hours ago Dan Graham
+- 2 years, 6 months ago Dan Graham
     a9a3945 2017-03-30 17:19:56 -0400
     Auto-commit: saved data.py
 
-- 16 hours ago Dan Graham
+- 2 years, 6 months ago Dan Graham
     134bd7f 2017-03-30 17:15:53 -0400
     Auto-commit: saved data.py
 
-- 6 months ago (tag: 3.2.28p3) Dan Graham
+- 3 years ago (tag: 3.2.28p3) Dan Graham
     759a8d8 2016-10-05 09:05:13 -0400
     Unicode fix provided for lst2str by Sebastian. Tagged version
     3.2.28p1 [2016-10-05 09:05:13 -0400]. Tagged version 3.2.28p2
     [2016-10-05 09:05:13 -0400]. Tagged version 3.2.28p3 [2016-10-05
     09:05:13 -0400].
 
-- 6 months ago (origin/export-json) Dan Graham
+- 3 years ago Dan Graham
     9bc0b9f 2016-10-04 21:44:28 -0400
     Added rst docs using sphinx.
 
-- 6 months ago (tag: 3.2.28) Dan Graham
+- 3 years ago (tag: 3.2.28) Dan Graham
     6fb8f22 2016-10-04 17:24:51 -0400
     import version into conf.py Tagged version 3.2.28 [2016-10-04
     17:24:51 -0400].
 
-- 6 months ago Dan Graham
+- 3 years ago Dan Graham
     d5898ed 2016-10-04 17:21:53 -0400
     removed keybindings.rst
 
-- 6 months ago Dan Graham
+- 3 years ago Dan Graham
     b5e7617 2016-10-04 16:45:58 -0400
     Added rst docs using sphinx.
 
-- 6 months ago Dan Graham
+- 3 years ago Dan Graham
     f533975 2016-10-04 16:22:28 -0400
     Added export_json.
 
-- 8 months ago (no_etmdir) Dan Graham
+- 3 years, 2 months ago (no_etmdir) Dan Graham
     a53f82c 2016-08-03 06:46:19 -0400
     Only write colors.cfg if etmdir exists.
 
-- 8 months ago Dan Graham
+- 3 years, 2 months ago Dan Graham
     0757f9b 2016-08-03 06:44:58 -0400
     Only write colors.cfg if etmdir exists.
 
-- 9 months ago Dan Graham
+- 3 years, 3 months ago Dan Graham
     db97a2e 2016-07-13 17:31:14 -0400
     Generate id's in export_json using random choice of seconds from
     modification time to last access time. Fixed bug in creating
     histories for jobs.
 
-- 9 months ago Dan Graham
+- 3 years, 3 months ago Dan Graham
     b2b4e7f 2016-07-12 20:35:57 -0400
     Added hsh2entry to update "entry" in export_json.
 
-- 9 months ago Dan Graham
+- 3 years, 3 months ago Dan Graham
     1cc880d 2016-07-12 18:38:34 -0400
     Added alerts.
 
-- 9 months ago Dan Graham
+- 3 years, 3 months ago Dan Graham
     0a38559 2016-07-12 18:37:36 -0400
     Implemented export_json with a binding in view.py. Handles jobs
     with ids and prereqs, rrulestr, datetime and timedelta formatting,
     finish and history. Missing currently is an update for entry.
 
-- 10 months ago (origin/master) Dan Graham
+- 3 years, 4 months ago Dan Graham
     a847007 2016-06-06 10:56:28 -0400
     Fixed itemtype missing key error when entering "=" default
     entries.
 
-- 10 months ago (tag: 3.2.27) Dan Graham
+- 3 years, 4 months ago (tag: 3.2.27) Dan Graham
     2fb7c10 2016-05-23 15:04:24 -0400
     Fixed timezone bug in processing dated reports with rrules. Tagged
     version 3.2.27 [2016-05-23 15:04:24 -0400].
 
-- 11 months ago Dan Graham
+- 3 years, 5 months ago Dan Graham
     b5b1d4d 2016-04-26 11:16:34 -0400
     Removed version info conflicts.
 
-- 11 months ago Dan Graham
+- 3 years, 5 months ago Dan Graham
     d1b86f8 2016-04-26 11:07:42 -0400
     Merge remote-tracking branch 'origin/master'
 
@@ -138,32 +385,32 @@
     #	etmTk/v.py
     #	etmTk/version.py
 
-- 11 months ago Dan Graham
+- 3 years, 5 months ago Dan Graham
     ab4b5e9 2016-04-26 11:06:36 -0400
     Coordinated versions with github
 
-- 1 year ago (tag: 3.2.26) Dan Graham
+- 3 years, 6 months ago (tag: 3.2.26) Dan Graham
     74262ed 2016-03-28 10:39:48 -0400
     Fixed @f entry when editing this instance of a repeating task.
     Tagged version 3.2.25 [2016-03-28 10:39:48 -0400]. Tagged version
     3.2.25p1 [2016-03-28 10:39:48 -0400]. Tagged version 3.2.26
     [2016-03-28 10:39:48 -0400].
 
-- 1 year ago Dan Graham
+- 3 years, 6 months ago Dan Graham
     5d5f261 2016-03-28 10:39:48 -0400
     Fixed @f entry when editing this instance of a repeating task.
     Tagged version 3.2.25 [2016-03-28 10:39:48 -0400]. Tagged version
     3.2.25p1 [2016-03-28 10:39:48 -0400].
 
-- 1 year ago Dan Graham
+- 3 years, 6 months ago Dan Graham
     a4cabe7 2016-03-28 10:39:48 -0400
     Fixed @f entry when editing this instance of a repeating task.
 
-- 1 year ago Dan Graham
+- 3 years, 6 months ago Dan Graham
     ef32f89 2016-03-27 15:59:44 -0400
     Fix for dateutil 2.5.1 in getInstance.
 
-- 1 year, 2 months ago Dan Graham
+- 3 years, 8 months ago Dan Graham
     50d2a52 2016-02-04 15:57:41 -0500
     Merge remote-tracking branch 'origin/master'
 
@@ -173,335 +420,50 @@
     #	etmTk/v.py
     #	etmTk/version.py
 
-- 1 year, 2 months ago Dan Graham
+- 3 years, 8 months ago Dan Graham
     6850202 2016-02-04 15:55:19 -0500
     Update to 3.2.24
 
     Tagged new release
 
-- 1 year, 2 months ago (tag: 3.2.24) Dan Graham
+- 3 years, 8 months ago (tag: 3.2.24) Dan Graham
     151995a 2016-02-04 09:29:19 -0500
     Include both the passed command and the error output in
     check_output. Tagged version 3.2.23p3 [2016-02-04 09:29:19 -0500].
     Tagged version 3.2.24 [2016-02-04 09:29:19 -0500].
 
-- 1 year, 2 months ago (tag: 3.2.23p3) Dan Graham
+- 3 years, 8 months ago (tag: 3.2.23p3) Dan Graham
     a3541eb 2016-02-04 09:29:19 -0500
     Include both the passed command and the error output in
     check_output. Tagged version 3.2.23p3 [2016-02-04 09:29:19 -0500].
 
-- 1 year, 2 months ago (tag: 3.2.23p2) Dan Graham
+- 3 years, 8 months ago (tag: 3.2.23p2) Dan Graham
     fdd1cb1 2016-02-03 16:02:09 -0500
     In check_output, replaced STDOUT with subprocess.STDOUT. Tagged
     version 3.2.23p2 [2016-02-03 16:02:09 -0500].
 
-- 1 year, 2 months ago (tag: 3.2.23p1) Dan Graham
+- 3 years, 8 months ago (tag: 3.2.23p1) Dan Graham
     8fc070e 2016-02-03 11:00:15 -0500
     Replaced subprocess.call with check_output which calls
     subprocess.check_output and logs the error message if an exception
     occurs. Tagged version 3.2.23p1 [2016-02-03 11:00:15 -0500].
 
-- 1 year, 2 months ago (tag: 3.2.23) Dan Graham
+- 3 years, 9 months ago (tag: 3.2.23) Dan Graham
     caf8bb4 2016-01-15 09:13:35 -0500
     Apply 'b' and 'e' settings to all items in reports. Tagged version
     3.2.23 [2016-01-15 09:13:35 -0500].
 
-- 1 year, 3 months ago Dan Graham
+- 3 years, 9 months ago Dan Graham
     38918e1 2016-01-14 10:17:50 -0500
     Changed 'event' to 'start' in show alerts dialog.
 
-- 1 year, 4 months ago (tag: 3.2.22p1) Dan Graham
+- 3 years, 11 months ago (tag: 3.2.22p1) Dan Graham
     b937b61 2015-11-18 14:27:23 -0500
     Use, e.g., Wed 18 Dec (Today), in short date format. Allow @n
     entries in events (reminders). Tagged version 3.2.22p1 [2015-11-18
     14:27:23 -0500].
 
-- 1 year, 5 months ago Dan Graham
+- 3 years, 11 months ago Dan Graham
     2e862aa 2015-11-08 16:57:18 -0500
     Added __init__.py to satisfy setup.py.
 
-- 1 year, 5 months ago Dan Graham
-    15ede9e 2015-11-07 10:58:35 -0500
-    Tagged 3.2.22
-
-- 1 year, 5 months ago Dan Graham
-    546559b 2015-11-07 09:55:57 -0500
-    Added CHANGES and etm.1 to repository to satisfy setup.py. Tagged
-    version 3.2.22 [2015-11-07 09:55:57 -0500].
-
-- 1 year, 5 months ago Dan Graham
-    c6d88f7 2015-11-07 09:55:57 -0500
-    Added CHANGES and etm.1 to repository to satisfy setup.py.
-
-- 1 year, 5 months ago Dan Graham
-    3412ab3 2015-11-06 14:44:23 -0500
-    Snooze message modification. Tagged version 3.2.21 [2015-11-06
-    14:44:23 -0500].
-
-- 1 year, 5 months ago Dan Graham
-    39ba0f5 2015-11-06 14:44:23 -0500
-    Snooze message modification.
-
-- 1 year, 5 months ago Dan Graham
-    a6bfc81 2015-11-06 10:39:05 -0500
-    Change snooze behavior to wait from the time the snooze button is
-    pressed with seconds are rounded off to the nearest minute. Fixed
-    typo: tmp_copy to tmp_cpy in view.py.
-
-- 1 year, 5 months ago (tag: 3.2.20) Dan Graham
-    6bbd054 2015-10-23 11:39:44 -0400
-    Added EXTRAS_REQUIRES. Tagged version 3.2.19 [2015-10-23 11:39:44
-    -0400]. Tagged version  [2015-10-23 11:39:44 -0400]. Tagged
-    version  [2015-10-23 11:39:44 -0400]. Tagged version  [2015-10-23
-    11:39:44 -0400]. Tagged version 3.2.19p1 [2015-10-23 11:39:44
-    -0400]. Tagged version 3.2.19p2 [2015-10-23 11:39:44 -0400].
-    Tagged version 3.2.19p3 [2015-10-23 11:39:44 -0400]. Tagged
-    version 3.2.19p4 [2015-10-23 11:39:44 -0400]. Tagged version
-    3.2.19p5 [2015-10-23 11:39:44 -0400]. Tagged version 3.2.20
-    [2015-10-23 11:39:44 -0400].
-
-- 1 year, 5 months ago Dan Graham
-    0afa353 2015-10-22 07:00:45 -0400
-    Changed REQUIRES in setup.py to eliminate the python requirement.
-
-- 1 year, 5 months ago (tag: 3.2.18) Dan Graham
-    73d73ca 2015-10-18 16:39:53 -0400
-    Merge branch 'master' of https://github.com/dagraham/etm-tk Tagged
-    version 3.2.18 [2015-10-18 16:39:53 -0400].
-
-- 1 year, 5 months ago dagraham
-    d890da8 2015-10-18 16:33:21 -0400
-    Merge pull request #49 from jeanCarloMachado/master
-
-    Added solarized dark colorscheme
-- 1 year, 5 months ago Jean Carlo Machado
-    8768a13 2015-10-18 17:31:13 -0200
-    added solarized dark colorscheme
-
-- 1 year, 5 months ago Dan Graham
-    9e99caf 2015-10-18 10:37:03 -0400
-    Changed error message for integer required.
-
-- 1 year, 5 months ago Dan Graham
-    7e22189 2015-10-15 22:41:05 -0400
-    Make @q an option for all item types.
-
-- 1 year, 6 months ago Dan Graham
-    5c2035a 2015-10-09 11:06:15 -0400
-    If 'q' but not  'z' is in hsh, then add hsh['z'] using
-    local_timezone.
-
-- 1 year, 6 months ago Dan Graham
-    278a723 2015-10-09 06:30:15 -0400
-    Added Python 3.5 to the setup.py classifiers.
-
-- 1 year, 6 months ago Dan Graham
-    11fd77f 2015-10-03 12:47:31 -0400
-    Fixed bug when copying an item in monthly view. Tagged version 
-    [2015-10-03 12:47:31 -0400]. Tagged version ..p1 [2015-10-03
-    12:47:31 -0400]. Tagged version ..p2 [2015-10-03 12:47:31 -0400].
-    Tagged version ..p3 [2015-10-03 12:47:31 -0400]. Tagged version
-    3.2.17p4 [2015-10-03 12:47:31 -0400].
-
-- 1 year, 6 months ago Dan Graham
-    99477f9 2015-10-03 12:47:31 -0400
-    Fixed bug when copying an item in monthly view.
-
-- 1 year, 6 months ago Dan Graham
-    03da1c8 2015-10-02 11:00:58 -0400
-    Updated REQUIRES and EXTRAS in setup.py.
-
-- 1 year, 6 months ago (tag: 3.2.17) Dan Graham
-    16b846a 2015-10-01 07:44:08 -0400
-    Added def for _() to data.  Some refactoring and optimizations,
-    e.g., removed unused lines2Items.  Changed "start time" to
-    "starting time". Reduced min sizes in view. Tagged version 3.2.17
-    [2015-10-01 07:44:08 -0400].
-
-- 1 year, 6 months ago Dan Graham
-    3bf408a 2015-09-21 11:33:23 -0400
-    Add today to datetimes if there is a begin by for today.
-
-- 1 year, 6 months ago Dan Graham
-    f6253ff 2015-09-18 10:52:38 -0400
-    Changed platform information from platform.system() to
-    platform.platform(terse=1).
-
-- 1 year, 7 months ago (tag: 3.2.16) Dan Graham
-    d5eca4c 2015-09-11 13:57:23 -0400
-    Added 'agenda_omit' to options. Accepts a list of types from 'ac',
-    'by', 'fn', 'ns', 'oc' to hide from the agenda day view. Tagged
-    version 3.2.16 [2015-09-11 13:57:23 -0400].
-
-- 1 year, 7 months ago (tag: 3.2.15) Dan Graham
-    0fe0e4b 2015-08-29 07:57:43 -0400
-    Use naive datetime in setting 'bef' in updateClock. Tagged version
-    3.2.14p6 [2015-08-29 07:57:43 -0400]. Tagged version 3.2.15
-    [2015-08-29 07:57:43 -0400].
-
-- 1 year, 7 months ago (tag: 3.2.13p2) Dan Graham
-    a6c3304 2015-08-28 06:27:11 -0400
-    Fixed bug in updateClock. Tagged version 3.2.14 [2015-08-28
-    06:27:11 -0400].
-
-- 1 year, 7 months ago Dan Graham
-    3a6a388 2015-08-28 06:27:11 -0400
-    Fixed bug in updateClock.
-
-- 1 year, 7 months ago (tag: 3.2.13) Dan Graham
-    2dab897 2015-08-26 12:07:13 -0400
-    Set options['bef'] in get_options and update on new_day in
-    update_clock. Refactored oneminute, onehour, oneday, oneweek as
-    all uppercase to be consistent. Tagged version 3.2.13 [2015-08-26
-    12:07:13 -0400].
-
-- 1 year, 7 months ago (tag: 3.2.12) Dan Graham
-    5a76ce8 2015-08-24 17:44:04 -0400
-    Changed the options for when/if to close the alert message box to
-    message_next and message_last. Tagged version 3.2.12 [2015-08-24
-    17:44:04 -0400].
-
-- 1 year, 7 months ago Dan Graham
-    0588774 2015-08-24 12:57:07 -0400
-    Added 'next' and 'next_alert' to template expansions. Added auto
-    close to message box dialogs using options 'message_alert_seconds'
-    for next message boxes and 'message_snooze_seconds' for last
-    message boxes.
-
-- 1 year, 7 months ago (tag: 3.2.11) Dan Graham
-    4d01318 2015-08-21 17:01:13 -0400
-    Cancel snooze if item is rescheduled, edited or deleted. Tagged
-    version 3.2.11 [2015-08-21 17:01:13 -0400].
-
-- 1 year, 7 months ago (tag: 3.2.10) Dan Graham
-    83af7b5 2015-08-21 11:25:03 -0400
-    Check that @n is only used with item types "-" and "%" and only
-    contains values from "d", "k" and "t". Tagged version 3.2.10
-    [2015-08-21 11:25:03 -0400].
-
-- 1 year, 7 months ago Dan Graham
-    1c681ff 2015-08-21 10:42:34 -0400
-    Check that @n is only used with item types "-" and "%" and only
-    contains values from "d", "k" and "t".
-
-- 1 year, 7 months ago (tag: 3.2.9) Dan Graham
-    e119efe 2015-08-20 13:04:31 -0400
-    Changed sort order for @n. Tagged version 3.2.9 [2015-08-20
-    13:04:31 -0400].
-
-- 1 year, 7 months ago Dan Graham
-    49b11c8 2015-08-20 12:58:52 -0400
-    Added @n (no show) for itemtype "-" only. Takes a list of views
-    from a)genda, d)ay (week and month), t)ag, k)eyword. E.g. with "@n
-    a, d" the task would be omitted from both agenda and day (week and
-    month) views but would appear in tag and keyword views. Note that
-    it is not possible to exclude the item from path view.
-
-- 1 year, 7 months ago (tag: 3.2.8, hidden) Dan Graham
-    afcf5ab 2015-08-19 18:11:40 -0400
-    image updates Tagged version 3.2.8 [2015-08-19 18:11:40 -0400].
-
-- 1 year, 7 months ago Dan Graham
-    d8ba751 2015-08-19 17:58:28 -0400
-    In onFinish, skip alertId for items without starting times.
-
-- 1 year, 7 months ago Dan Graham
-    fac82ae 2015-08-19 17:53:46 -0400
-    Trap and report errors in parse_period.
-
-- 1 year, 7 months ago (tag: 3.2.7) Dan Graham
-    a546170 2015-08-19 12:39:48 -0400
-    added alert_list image Tagged version 3.2.7 [2015-08-19 12:39:48
-    -0400].
-
-- 1 year, 7 months ago Dan Graham
-    dc16e1e 2015-08-19 12:32:23 -0400
-    Removed unused variables. Show alert trigger time in snooze
-    dialog. Make snooze trigger at least 40 seconds after closing
-    dialog. Use '~' as show alerts label when none remain.
-
-- 1 year, 7 months ago Dan Graham
-    1b7b32c 2015-08-19 09:37:51 -0400
-    Final tweaks for the show alerts button label.
-
-- 1 year, 7 months ago Dan Graham
-    abb9bbe 2015-08-19 09:11:28 -0400
-    Second pass at snoozing multiple items. Since uuid changes with
-    file updates, e.g., finishing a task, use (summary, s) as alertId.
-    Snooze for the chosen minutes beyond the alert time, not the
-    current time. When finishing a task, use the (summary, s) id to
-    find the correct alert_id to cancel the snooze.
-
-- 1 year, 7 months ago Dan Graham
-    cc3ba53 2015-08-18 14:45:37 -0400
-    First pass at snoozing multiple items. Added option seconds
-    (False) to fmt_time to show seconds when True. Added
-    longreprtimefmt to reprtimefmt in options to show seconds.
-
-- 1 year, 7 months ago (tag: 3.2.6) Dan Graham
-    67607eb 2015-08-18 07:12:59 -0400
-    When a task is finished that has a snooze alert running, cancel
-    the alert. Tagged version 3.2.6 [2015-08-18 07:12:59 -0400].
-
-- 1 year, 7 months ago Dan Graham
-    59b90b6 2015-08-17 17:53:23 -0400
-    Changed binding for onFinish in simpleEditor from Ctrl-w to Ctrl-s
-    to avoid conflicts. Changed button label from Finish to Save and
-    Close.
-
-- 1 year, 7 months ago Dan Graham
-    ca71856 2015-08-17 15:13:55 -0400
-    More KP_Enter bindings. Changed logic for setting master in
-    simpleEditor.
-
-- 1 year, 7 months ago Dan Graham
-    67fe85a 2015-08-17 12:54:41 -0400
-    Only try export_ical if has_calendar is True. Add KP_Enter to
-    Return bindings.
-
-- 1 year, 7 months ago Dan Graham
-    8f82a3c 2015-08-16 14:04:46 -0400
-    updated alert images
-
-- 1 year, 7 months ago (tag: 3.2.5) Dan Graham
-    ce4fd88 2015-08-16 12:14:42 -0400
-    Fixed bug is displaying next starting time. Tagged version 3.2.5
-    [2015-08-16 12:14:42 -0400].
-
-- 1 year, 7 months ago (tag: 3.2.4) Dan Graham
-    a9286a5 2015-08-16 11:36:40 -0400
-    When next is "none", show "at the starting time" instead of "none
-    before starting time" in the message alert. Tagged version 3.2.4
-    [2015-08-16 11:36:40 -0400].
-
-- 1 year, 7 months ago Dan Graham
-    3fcc6cd 2015-08-16 10:54:57 -0400
-    updated readme screenshots
-
-- 1 year, 7 months ago (tag: 3.2.3) Dan Graham
-    9f2ec94 2015-08-16 10:42:42 -0400
-    renamed alert images Tagged version 3.2.3 [2015-08-16 10:42:42
-    -0400].
-
-- 1 year, 7 months ago Dan Graham
-    a0efc30 2015-08-16 10:42:15 -0400
-    Added GetRepeat as subclass of GetInteger with Repeat and Close
-    buttons instead of OK and Cancel.
-
-- 1 year, 7 months ago Dan Graham
-    bf192d6 2015-08-16 09:54:13 -0400
-    Message tweaks.
-
-- 1 year, 7 months ago Dan Graham
-    5138126 2015-08-16 09:48:54 -0400
-    Show next alert in non-snooze alert and current time in title.
-
-- 1 year, 8 months ago (tag: 3.2.2) Dan Graham
-    09ecb48 2015-08-15 18:27:55 -0400
-    Added snooze, no-snooze images Tagged version 3.2.2 [2015-08-15
-    18:27:55 -0400].
-
-- 1 year, 8 months ago Dan Graham
-    af056ec 2015-08-15 17:10:19 -0400
-    Message tweaks for last alert.
-
diff --git a/etmTk/data.py b/etmTk/data.py
index 00fa2f8..9051dee 100644
--- a/etmTk/data.py
+++ b/etmTk/data.py
@@ -19,9 +19,11 @@ LANGUAGES = os.path.normpath(os.path.join(this_dir, "locale"))
 
 BGCOLOR = HLCOLOR = FGCOLOR = CALENDAR_COLORS = None
 
+
 def _(x):
     return(x)
 
+
 def setup_logging(level, etmdir=None):
     """
     Setup logging configuration. Override root:level in
@@ -668,7 +670,8 @@ def gettz(z=None):
 
 import calendar
 
-import yaml
+# import yaml
+import ruamel.yaml as yaml
 from itertools import groupby
 # from dateutil.rrule import *
 from dateutil.rrule import (DAILY, rrule)
@@ -1017,10 +1020,7 @@ def mail_report(message, smtp_from=None, smtp_server=None,
     smtp.close()
 
 
-def send_mail(smtp_to, subject, message, files=None, smtp_from=None, smtp_server=None,
-              smtp_id=None, smtp_pw=None):
-    """
-    """
+def send_mail(smtp_to, subject, message, files=None, smtp_from=None, smtp_server=None, smtp_id=None, smtp_pw=None):
     if not files:
         files = []
     import smtplib
@@ -1191,7 +1191,7 @@ def setConfig(options):
         elif n == "users":
             user_files.append((np, fp, False))
             fo = codecs.open(fp, 'r', dfile_encoding)
-            tmp = yaml.load(fo)
+            tmp = yaml.safe_load(fo)
             fo.close()
             try:
                 # if a key already exists, use the tmp value
@@ -1224,7 +1224,7 @@ def setConfig(options):
             for fp in options['cfg_files']['users']:
                 user_files.append((relpath(fp, options['etmdir']), fp, False))
                 fo = codecs.open(fp, 'r', dfile_encoding)
-                tmp = yaml.load(fo)
+                tmp = yaml.safe_load(fo)
                 fo.close()
                 # if a key already exists, use this value
                 options['user_data'].update(tmp)
@@ -1301,7 +1301,7 @@ def get_options(d=''):
     if os.path.isfile(colors_cfg):
         logger.info('using colors file: {0}'.format(colors_cfg))
         fo = codecs.open(colors_cfg, 'r', dfile_encoding)
-        use_colors = yaml.load(fo)
+        use_colors = yaml.safe_load(fo)
         fo.close()
 
         if use_colors:
@@ -1323,7 +1323,7 @@ def get_options(d=''):
     if os.path.isfile(locale_cfg):
         logger.info('using locale file: {0}'.format(locale_cfg))
         fo = codecs.open(locale_cfg, 'r', dfile_encoding)
-        use_locale = yaml.load(fo)
+        use_locale = yaml.safe_load(fo)
         fo.close()
         if use_locale:
             dgui_encoding = use_locale[0][1]
@@ -1484,7 +1484,7 @@ def get_options(d=''):
         try:
             logger.info('user options: {0}'.format(newconfig))
             fo = codecs.open(newconfig, 'r', dfile_encoding)
-            user_options = yaml.load(fo)
+            user_options = yaml.safe_load(fo)
             fo.close()
         except yaml.parser.ParserError:
             logger.exception(
@@ -1495,7 +1495,7 @@ def get_options(d=''):
             using_oldcfg = True
             logger.info('user options: {0}'.format(oldconfig))
             fo = codecs.open(oldconfig, 'r', dfile_encoding)
-            user_options = yaml.load(fo)
+            user_options = yaml.safe_load(fo)
             fo.close()
         except yaml.parser.ParserError:
             logger.exception(
@@ -5970,7 +5970,7 @@ def hsh2ical(hsh):
         # repeating
         rlst = hsh[u'_r']
         for r in rlst:
-            if r['f'] == 'l':
+            if 'f' in r and r['f'] == 'l':
                 if '+' not in hsh:
                     logger.warn("An entry for '@+' is required but missing.")
                     continue
@@ -5980,7 +5980,7 @@ def hsh2ical(hsh):
                 dt = dz
                 dd = dz.date()
 
-                r['f'] = 'd'
+                r['r'] = 'd'
                 r['t'] = 1
 
             rhsh = {}
@@ -6164,7 +6164,7 @@ def export_ical_active(file2uuids, uuid2hash, vcal_file, calendars=None):
 
 def export_json(file2uuids, uuid2hash, options={}):
     """
-    Export items from each calendar to a json file with @k entries corresponding to the calendar name.
+    Export items from each calendar to a json file with @c entries corresponding to the calendar name.
     New ids will be generated each time this is run.
     """
     # TODO: export relevant config info as well
@@ -6173,7 +6173,6 @@ def export_json(file2uuids, uuid2hash, options={}):
     logger.debug("json_folder: {0}; calendars: {1}".format(json_folder, calendars))
 
     cal_tuples = []
-    calfiles = []
     if calendars:
         for cal in calendars:
             logger.debug('processing cal: {0}'.format(cal))
@@ -6191,27 +6190,35 @@ def export_json(file2uuids, uuid2hash, options={}):
     hsh  = {}
     hsh['items'] = {}
     logger.debug('using cal_tuples: {0}'.format(cal_tuples))
-    json_file = os.path.join(json_folder, "etm-db.json")
+    hsh_file = os.path.join(json_folder, "etm-hsh.json")
+    lines_file = os.path.join(json_folder, "etm-lines.json")
+    text_lines = []
 
     prefix, filelist = getFiles(options['datadir'])
     filetimes = {}
 
     for fp, rp in filelist:
-        atime = os.path.getatime(fp)
-        mtime = os.path.getmtime(fp)
-        filetimes[rp] = (mtime, max(atime - mtime, 86400))
+        atime = int(os.path.getatime(fp))
+        ctime = int(os.path.getctime(fp))
+        mtime = int(os.path.getmtime(fp))
+        # filetimes[rp] = (ctime, max(mtime - ctime, 86400))
+        mincm = min(ctime, mtime)
+        # filetimes[rp] = (mincm, max(atime-mincm, 3600))
+        filetimes[rp] = (mincm,  3600)
 
     # uuids for jobs will have etm:NN appended - we only want one copy
     # e.g., b63c362940f147a1ae8404d8265fa4bdetm:01
-    for rp in file2uuids:
+    rps = [x for x in file2uuids]
+    rps.sort()
+    for rp in rps:
         this_calendar = None
-        this_lst = []  # for error logging
+        intervals = len(file2uuids[rp])
+        stime, diff = filetimes[rp]
+        delta = int(diff / intervals)
+        # this_lst = []  # for error logging
         for name, regex in cal_tuples:
             if regex.match(rp):
                 this_calendar = name
-                intervals = len(file2uuids[rp])
-                stime, diff = filetimes[rp]
-                delta = diff / intervals
                 last_uid = ""
                 for uid in file2uuids[rp]:
                     if uid[:32] == last_uid[:32]:
@@ -6219,11 +6226,13 @@ def export_json(file2uuids, uuid2hash, options={}):
                         continue
                     last_uid = uid
                     secs = int(uniform(stime, stime + delta))
-                    id = int(datetime.fromtimestamp(secs).strftime("%Y%m%d%H%M%S%f"))
+                    id = datetime.fromtimestamp(secs).strftime("%Y%m%d%H%M%S%f")
                     stime += delta
                     old_hsh = uuid2hash[uid]
                     new_hsh = deepcopy(old_hsh)
                     itemtype = old_hsh['itemtype']
+                    if itemtype in ['=', '#']:
+                        continue
 
                     for key in new_hsh:
                         if type(new_hsh[key]) is datetime:
@@ -6236,82 +6245,82 @@ def export_json(file2uuids, uuid2hash, options={}):
                             del new_hsh[key]
                     if '_a' in new_hsh:
                         alerts = []
-                        for alert in new_hsh['_a']:
+                        for a_tup in new_hsh['_a']:
+                            if len(a_tup) <= 1:
+                                alert = a_tup[0]
+                            else:
+                                alert = a_tup
+
                             args = []
                             if len(alert) >= 3:
                                 for r in alert[2]:
                                     args.extend(r)
                             args = [x.strip() for x in args]
+                            tds = []
                             for td in alert[0]:
-                                td = fmt_period(td)
-                                for cmd in alert[1]:
-                                    alerts.append([td, cmd] + args)
+                                tds.append(fmt_period(td))
+                            for cmd in alert[1]:
+                                if args:
+                                    alerts.append((tds, cmd, args))
+                                else:
+                                    alerts.append((tds, cmd))
                         new_hsh['a'] = alerts
                         del new_hsh['_a']
                     if 'h' in new_hsh:
                         tmp = []
                         for pair in new_hsh['h']:
                             tmp.append(pair[0].strftime("%Y%m%dT%H%M"))
+                            # tmp.append([x.strftime("%Y%m%dT%H%M") for x in pair if x])
                         new_hsh['h'] = tmp
-
                     if 'f' in new_hsh:
                         d, n, f = getDoneAndTwo(old_hsh)
                         o = old_hsh.get('o', 'k')
+                        hd = []
                         if n:
-                            new_hsh['s'] = n.strftime("%Y%m%dT%H:%M")
+                            new_hsh['s'] = n.strftime("%Y%m%dT%H%M")
                             # if o == 'r':
                             #     # reset start to the finish time
                             #     new_hsh['s'] = old_hsh['f'][0][0].strftime("%Y%m%dT%H%M")
                             # else:
                             #     # reset start to the next due date
                             #     new_hsh['s'] = n.strftime("%Y%m%dT%H:%M")
-                            new_hsh.setdefault('h', []).append(new_hsh['f'][0][0].strftime("%Y%m%dT%H%M"))
+                            hd = [x[0].strftime("%Y%m%dT%H%M") for x in new_hsh['f'][-3:]]
                             del new_hsh['f']
                         else:
-                            new_hsh['f'] = new_hsh['f'][0][0].strftime("%Y%m%dT%H%M")
+                            hd = [x[0].strftime("%Y%m%dT%H%M") for x in new_hsh['f'][-3:-1]]
+                            new_hsh['f'] = new_hsh['f'][-1][0].strftime("%Y%m%dT%H%M")
+                        if hd:
+                            new_hsh.setdefault('h', []).extend(hd)
 
                     if '+' in new_hsh and 's' in new_hsh:
                         new_hsh['+'] = [x for x in new_hsh['+'] if x.strftime("%Y%m%dT%H%M") >= new_hsh['s']]
 
                     if 'rrule' in new_hsh:
                         del new_hsh['rrule']
-                    if 'r' in new_hsh and 's in new_hsh':
-                        # drop the old dtstart and insert the new
-                        new_hsh['rrulestr'] = "DTSTART:{}\n{}".format(new_hsh['s'], new_hsh['r'][22:])
+                    if 'r' in new_hsh and 's' in new_hsh:
+                        new_hsh['rrulestr'] = "{}".format(new_hsh['r'][22:])
                         del new_hsh['r']
-                    elif 's' in new_hsh:
-                        new_hsh['rrulestr'] = "RDATE:{}".format(new_hsh['s'])
 
                     if '_j' in new_hsh:
-                        # print('jobs', new_hsh['_group_summary'], uid, new_hsh.get('f', 'unfinished'))
                         count = 0
                         jobs = {}
                         # make sure jobs are in q order
-                        finished = True
                         for job in new_hsh['_j']:
                             q = job['q']
-                            del job['q']
                             jobs.setdefault(q, []).append(job)
+                            del job['q']
 
                             if 'h' in job:
                                 tmp = []
                                 for pair in job['h']:
                                     tmp.append(pair[0].strftime("%Y%m%dT%H%M"))
                                 job['h'] = tmp
-                            else:
-                                finished = False
 
-                            # if 'f' in job:
-                            #     job['f'] = job['f'][0][0].strftime("%Y%m%dT%H%M")
-                            # else:
-                            #     finished = False
-
-                        if 'f' in new_hsh and not finished:
-                            del new_hsh['f']
+                            if 'f' in job:
+                                job['f'] = job['f'][0][0].strftime("%Y%m%dT%H%M")
 
                         q_keys = [x for x in jobs]
                         q_keys.sort()
-                        q_count = 0
                         prereqs = []
                         for q_key in q_keys:
                             tmp = []
@@ -6344,33 +6353,71 @@ def export_json(file2uuids, uuid2hash, options={}):
                             del new_hsh[key]
                             nkey = key[1:]
                             new_hsh[nkey] = old_hsh[key]
+                    if 'r' in new_hsh:
+                        tmp_r = []
+                        for tmp_hsh in new_hsh['r']:
+                            if 't' in tmp_hsh:
+                                tmp_hsh['c'] = tmp_hsh['t']
+                                del tmp_hsh['t']
+                            if 'f' in tmp_hsh:
+                                tmp_hsh['r'] = tmp_hsh['f']
+                                del tmp_hsh['f']
+                            if 'u' in tmp_hsh:
+                                tmp_hsh['u'] = parse(tmp_hsh['u']).strftime("%Y%m%dT%H%M")
+                            tmp_r.append(tmp_hsh)
+                        new_hsh['r'] = tmp_r
                     this_c = new_hsh.get('c', None)
                     this_l = new_hsh.get('l', None)
-                    new_hsh['l'] = "; ".join([x for x in [this_l, this_c] if x is not None])
+                    temp_l = "; ".join([x for x in [this_l, this_c] if x is not None])
+                    if temp_l:
+                        new_hsh['l'] = temp_l
                     new_hsh['c'] = this_calendar
                     k = old_hsh.get('k', None)
                     if k is not None:
-                        new_hsh['n'] = k
+                        new_hsh['i'] = k
                         del new_hsh['k']
                     if itemtype in ['+', '-', '%']:
+                        itemtype = '-'
                         s = old_hsh.get('s', None)
                         if s is None:
                             # undated
-                            itemtype = '%'
+                            if 'z' in new_hsh:
+                                del new_hsh['z']
                         elif s.hour == s.minute == 0:
                             # date-only
-                            itemtype = '+'
-                        else:
-                            # date-time
-                            itemtype = '-'
-                    if itemtype in ['^', '+', '%'] and 'z' in new_hsh:
-                        del new_hsh['z']
+                            pass
+                            # if 'z' in new_hsh:
+                            #     del new_hsh['z']
                     if '_group_summary' in new_hsh:
                         new_hsh['summary'] = new_hsh['_group_summary']
                         del new_hsh['_group_summary']
 
+                    if itemtype == "^":
+                        itemtype = "*"
+                        if 'z' in new_hsh:
+                            del new_hsh['z']
+                    elif itemtype == "!":
+                        itemtype = "%"
+                    elif itemtype == "$":
+                        itemtype = "!"
+                    elif itemtype == "~":
+                        itemtype = "%"
+                        if 'e' in new_hsh:
+                            tmp_s = parse_str(new_hsh['s'], new_hsh.get('z', None))
+                            tmp_e = parse_period(new_hsh['e'])
+                            new_hsh['f'] = (tmp_s + tmp_e).strftime("%Y%m%dT%H%M")
+
+                    for x in ['entry', 'rrulestr']:
+                        if x in new_hsh:
+                            del new_hsh[x]
                     new_hsh['itemtype'] = itemtype
-                    new_hsh['entry'] = hsh2entry(new_hsh)
+                    text_line = hsh2entry(new_hsh)
+                    text_lines.append(text_line)
+                    # new_hsh['entry'] = text_line
+                    # if 'r' in new_hsh:
+                    #     del new_hsh['r']
+                    # if 'z' in new_hsh:
+                    #     del new_hsh['z']
                     try:
                         json.dumps(new_hsh)
                         hsh['items'][id] = new_hsh
@@ -6383,50 +6430,69 @@ def export_json(file2uuids, uuid2hash, options={}):
             logger.debug('skipping {0} - no match in calendars'.format(rp))
             print('skipping {0} - no match in calendars'.format(rp))
 
-    with open(json_file, 'w') as jo:
+    with open(hsh_file, 'w') as jo:
         json.dump(hsh, jo, indent=1, sort_keys=True)
+    with open(lines_file, 'w') as fo:
+        json.dump(text_lines, fo, indent=1, sort_keys=True)
 
     return True
 
 def etm2dsp(s):
     """
     >>> etm2dsp("20160710T1730")
-    (True, '2016-07-10 17:30')
+    (True, '2016-07-10 05:30PM')
     >>> etm2dsp("2016710T1730")
     (False, 'Invalid datetime: 2016710T1730')
+    >>> etm2dsp("20160710")
+    (True, '2016-07-10')
+    >>> etm2dsp("20160710T0000")
+    (True, '2016-07-10')
     """
+
     dt_regex = re.compile(r'\d{8}T\d{4}')
     d_regex = re.compile(r'\d{8}')
-    if dt_regex.match(s):
-        return True, "{}-{}-{} {}:{}".format(s[:4], s[4:6], s[6:8], s[9:11], s[11:])
-    elif d_regex.match(s):
-        return True, "{}-{}-{}".format(s[:4], s[4:6], s[6:8])
+    m_regex = re.compile(r'\d{8}T0000')
+    if m_regex.fullmatch(s):
+        dt = datetime.strptime(s, "%Y%m%dT%H%M")
+        return True, dt.strftime("%Y-%m-%d") 
+    elif d_regex.fullmatch(s):
+        dt = datetime.strptime(s, "%Y%m%d")
+        return True, dt.strftime("%Y-%m-%d") 
+    elif dt_regex.fullmatch(s):
+        dt = datetime.strptime(s, "%Y%m%dT%H%M")
+        return True, dt.strftime("%Y-%m-%d %I:%M%p") 
     else:
         return False, "Invalid datetime: {}".format(s)
 
 def hsh2entry(h):
     """
     """
-    all_keys = [x for x in "seabr+-cdfghijklmnopqtuvz"]
-    rrule_keys = [x for x in "iMmWwhmEtus"]
+    # all_keys = [x for x in "seabr+-cdfghijklmnopqtuvz"]
+    all_keys = [x for x in "seabr+-cdfghijlmnoptx"]
+    rrule_keys = [x for x in "iMmWwhmEcus"]
     job_keys = [x for x in "jsabcdefhlnipq"]
 
     res = []
     hsh = deepcopy(h)
+    zstr = ""
+    if 'z' in hsh and hsh['z']:
+        if hsh['z'] != local_timezone:
+            zstr = ", {}".format(hsh['z'])
+        del hsh['z']
     res.append("{} {}".format(hsh['itemtype'], hsh['summary']))
     for k in all_keys:
         if k not in hsh:
             continue
         v = hsh[k]
         if k == 's':
-            res.append("@s {}".format(etm2dsp(v)[1]))
+            res.append("@s {}{}".format(etm2dsp(v)[1], zstr))
         elif k in ['+', '-', 'h']:
             # res.append("@{} {}".format(k, ", ".join([etm2dsp(x)[1] for x in v])))
             res.append("@{} {}".format(k, ", ".join(v)))
         elif k == 'r':
             for r in v:
-                frq = r['f']
-                del r['f']
+                frq = r['r']
+                del r['r']
                 tmp = []
                 for amp_key in rrule_keys:
                     if amp_key not in r:
@@ -6462,7 +6528,7 @@ def hsh2entry(h):
                 res.append("@j {} {}".format(jnm, " ".join(tmp)))
         elif k == 'a':
             for a in v:
-                res.append("@a {}: {}".format(a[0], ", ".join(a[1:])))
+                res.append("@a {}: {}".format(", ".join(a[0]), ", ".join(a[1:])))
         # elif k == 'f':
         #     res.append(("@f {}".format("; ".join(v))))
         else:
@@ -7179,7 +7245,7 @@ Generate an agenda including dated items for the next {0} days (agenda_days from
                 tmp = []
                 for h in hsh_rev['_r']:
                     if 'f' in h and h['f'] != u'l':
-                        h['u'] = dtn - ONEMINUTE
+                        h['u'] = (dtn - ONEMINUTE).strftime(sfmt)
                     tmp.append(h)
                 hsh_rev['_r'] = tmp
 
@@ -7764,4 +7830,7 @@ if __name__ == "__main__":
     if len(sys.argv) > 1:
         if sys.argv[1] not in ['a', 'c']:
             etmdir = sys.argv.pop(1)
+    import doctest
+    doctest.testmod()
+
     main(etmdir, sys.argv)
diff --git a/etmTk/dialog.py b/etmTk/dialog.py
index 1d28b74..71985b1 100644
--- a/etmTk/dialog.py
+++ b/etmTk/dialog.py
@@ -11,7 +11,10 @@ import os.path
 
 logger = logging.getLogger()
 import codecs
-import yaml
+# import ruamel.yaml as yaml
+from ruamel.yaml import YAML
+yaml = YAML(typ='unsafe')
+
 
 import platform
 
@@ -656,12 +659,12 @@ class SimpleEditor(Toplevel):
         logger.debug('target: {0}'.format(target))
         if target:
             where = self.text.search(target, INSERT, nocase=1)
-        if where:
-            pastit = where + ('+%dc' % len(target))
-            self.text.tag_add(FOUND, where, pastit)
-            self.text.mark_set(INSERT, pastit)
-            self.text.see(INSERT)
-            self.text.focus()
+            if where:
+                pastit = where + ('+%dc' % len(target))
+                self.text.tag_add(FOUND, where, pastit)
+                self.text.mark_set(INSERT, pastit)
+                self.text.see(INSERT)
+                self.text.focus()
 
     def cancel(self, e=None):
         t = self.find_text.get()
@@ -1163,7 +1166,7 @@ class Timer():
             self.currentStatus,
             self.currentMinutes,
             self.idlestart,
-            self.idletime
+            self.idletime,
         )
         fo = codecs.open(self.etmtimers, 'w', self.dfile_encoding)
         yaml.dump(tmp, fo)
diff --git a/etmTk/etm.1 b/etmTk/etm.1
index dd93454..bae8687 100644
--- a/etmTk/etm.1
+++ b/etmTk/etm.1
@@ -1,5 +1,5 @@
 .\" Text automatically generated by txt2man
-.TH etm 1 "31 March 2017" "version 3.2.30" "Unix user's manual"
+.TH etm 1 "03 October 2019" "version 3.2.39" "Unix user's manual"
 .SH NAME
 \fBetm \fP- manage events and tasks using simple text files
 .SH SYNOPSIS
diff --git a/etmTk/v.py b/etmTk/v.py
index 1e8204c..c4a03bd 100644
--- a/etmTk/v.py
+++ b/etmTk/v.py
@@ -1 +1 @@
-version = "3.2.30"
+version = "3.2.39"
diff --git a/etmTk/version.py b/etmTk/version.py
index f88a686..15fca73 100644
--- a/etmTk/version.py
+++ b/etmTk/version.py
@@ -1 +1 @@
-version = "3.2.30 [2017-03-31 09:28:19 -0400]"
+version = "3.2.39 [2019-10-03 17:36:24 -0400]"
diff --git a/etmTk/view.py b/etmTk/view.py
index 5dfebba..58d381b 100755
--- a/etmTk/view.py
+++ b/etmTk/view.py
@@ -216,6 +216,13 @@ class App(Tk):
 
         style = self.options['style']
         s = ttk.Style()
+
+        # Fix broken tkinter Treeview colors for Python3.7+
+        # Fix source: https://bugs.python.org/issue36468
+        def fixed_map(option):
+            return [elm for elm in s.map('Treeview', query_opt=option) if elm[:2] != ('!disabled', '!selected')]
+        s.map('Treeview', foreground=fixed_map('foreground'), background=fixed_map('background'))
+
         styles = s.theme_names()
         if style in styles:
             logger.info("using style {0}".format(style))
@@ -450,7 +457,7 @@ class App(Tk):
         timermenu.add_command(label=label,
                               command=self.setcountdownTimer)
         timermenu.entryconfig(7, accelerator=l)
-        self.bind(c, self.setcountdownTimer)
+        self.bindTop(c, self.setcountdownTimer)
 
         self.add2menu(path, (label, l))
 
@@ -4241,7 +4248,7 @@ or 0 to expand all branches completely.""")
 
                 if item_type:
                     # This hack avoids encoding issues under python 2
-                    col1 = "{0} {1}".format(id2Type[item_type], col1)
+                    col1 = "{0} {1}".format(id2Type[item_type], col1.replace('\n', ' '))
 
                 if type(col3) == int:
                     col3 = '%s' % col3
diff --git a/etmtk.egg-info/PKG-INFO b/etmtk.egg-info/PKG-INFO
index f8faa0a..00fcae1 100644
--- a/etmtk.egg-info/PKG-INFO
+++ b/etmtk.egg-info/PKG-INFO
@@ -1,10 +1,10 @@
-Metadata-Version: 1.1
+Metadata-Version: 2.1
 Name: etmtk
-Version: 3.2.30
+Version: 3.2.39
 Summary: event and task manager
-Home-page: http://people.duke.edu/~dgraham/etmtk
+Home-page: https://github.com/dagraham/etm-tk
 Author: Daniel A Graham
-Author-email: daniel.graham@duke.edu
+Author-email: dnlgrhm@gmail.com
 License: License :: OSI Approved :: GNU General Public License (GPL)
 Description: manage events and tasks using simple text files
 Platform: Any
@@ -26,6 +26,8 @@ Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.4
 Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
 Classifier: Topic :: Office/Business
 Classifier: Topic :: Office/Business :: News/Diary
 Classifier: Topic :: Office/Business :: Scheduling
+Provides-Extra: icalendar
diff --git a/etmtk.egg-info/requires.txt b/etmtk.egg-info/requires.txt
index 98d5b47..9bdad28 100644
--- a/etmtk.egg-info/requires.txt
+++ b/etmtk.egg-info/requires.txt
@@ -1,5 +1,5 @@
 python-dateutil>=1.5
-PyYaml>=3.10
+ruamel.yaml>=0.15.80
 
 [icalendar]
 icalendar>=3.8.4
diff --git a/setup.cfg b/setup.cfg
index 861a9f5..8bfd5a1 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,4 @@
 [egg_info]
 tag_build = 
 tag_date = 0
-tag_svn_revision = 0
 
diff --git a/setup.py b/setup.py
index 8ba6654..a8fdd52 100644
--- a/setup.py
+++ b/setup.py
@@ -5,7 +5,7 @@ import glob
 
 import sys
 
-INSTALL_REQUIRES = ["python-dateutil>=1.5", "PyYaml>=3.10"]
+INSTALL_REQUIRES = ["python-dateutil>=1.5", "ruamel.yaml>=0.15.80"]
 EXTRAS_REQUIRE = {"icalendar": ["icalendar>=3.8.4", "pytz>=2015.1"]}
 
 APP = ['etm']
@@ -24,13 +24,13 @@ setup(
     version=version,
     include_package_data=True,
     zip_safe=False,
-    url='http://people.duke.edu/~dgraham/etmtk',
+    url='https://github.com/dagraham/etm-tk',
     description='event and task manager',
     long_description='manage events and tasks using simple text files',
     platforms='Any',
     license='License :: OSI Approved :: GNU General Public License (GPL)',
     author='Daniel A Graham',
-    author_email='daniel.graham@duke.edu',
+    author_email='dnlgrhm@gmail.com',
     classifiers=[
         'Development Status :: 5 - Production/Stable',
         'Environment :: Console',
@@ -50,6 +50,7 @@ setup(
         'Programming Language :: Python :: 3.3',
         'Programming Language :: Python :: 3.4',
         'Programming Language :: Python :: 3.5',
+        'Programming Language :: Python :: 3.6',
         'Topic :: Office/Business',
         'Topic :: Office/Business :: News/Diary',
         'Topic :: Office/Business :: Scheduling'],
@@ -59,15 +60,14 @@ setup(
     extras_require=EXTRAS_REQUIRE,
     # extras_require={"icalendar": ["icalendar>=3.8.4"]},
     package_data={
-        'etmTk': ['icons/*', 'etm.desktop', 'etm.appdata.xml', 'CHANGES', 'etm.1', 'etm.xpm'],
+        'etmTk': ['icons/*', 'etm.desktop', 'etm.appdata.xml', 'CHANGES', 'etm.1'],
         'etmTk/help' : ['help/UserManual.html'],
         'etmTk/icons': ['icons/*']},
     data_files=[
         ('share/man/man1', ['etmTk/etm.1']),
         ('share/doc/etm', ['etmTk/CHANGES']),
-        ('share/pixmaps', ['etmTk/etm.xpm']),
         ('share/icons', glob.glob('etmTk/icons/*.gif')),
         ('share/applications', ['etmTk/etm.desktop']),
-        ('share/appdata', ['etmTk/etm.appdata.xml']),
+        ('share/metainfo', ['etmTk/etm.appdata.xml'])
     ]
 )