Repository: pimutils/todoman
Pull request #362 (2019-09-03 11:28:21)

Try out github actions


Patch:
diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml
new file mode 100644
index 0000000..393b170
--- /dev/null
+++ b/.github/workflows/checks.yml
@@ -0,0 +1,39 @@
+name: Checks
+
+on: [push]
+
+jobs:
+  styles:
+    runs-on: ubuntu-18.04
+    name: Check linting
+    steps:
+      - name: Checkout repo
+        uses: actions/checkout@master
+      - name: Setup python
+        uses: actions/setup-python@v1
+        with:
+          python-version: 3.7
+          architecture: x64
+      - name: Install test dependencies
+        run: pip install tox
+      - name: Run flake8
+        run: tox
+        env:
+          TOXENV: flake8
+  docs:
+    runs-on: ubuntu-18.04
+    name: Build documentation
+    steps:
+      - name: Checkout repo
+        uses: actions/checkout@master
+      - name: Setup python
+        uses: actions/setup-python@v1
+        with:
+          python-version: 3.7
+          architecture: x64
+      - name: Install test dependencies
+        run: pip install tox
+      - name: Build docs
+        run: tox
+        env:
+          TOXENV: docs
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 0000000..778ddda
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,48 @@
+name: Publish
+
+on:
+  push:
+    tags:
+      - v*
+
+jobs:
+  github-release:
+    runs-on: ubuntu-18.04
+    name: Publish GitHub Release
+    steps:
+      - name: Checkout repo
+        uses: actions/checkout@master
+      - name: Setup python
+        uses: actions/setup-python@v1
+        with:
+          python-version: 3.7
+          architecture: x64
+      - name: Install build dependencies
+        run: pip install wheel
+      - name: Build packages
+        run: python setup.py sdist bdist_wheel
+      - name: Release
+        uses: softprops/action-gh-release@v1
+        with:
+          files: dist/*
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+  pypi:
+    runs-on: ubuntu-18.04
+    name: Publish package on PyPI
+    steps:
+      - name: Checkout repo
+        uses: actions/checkout@master
+      - name: Setup python
+        uses: actions/setup-python@v1
+        with:
+          python-version: 3.7
+          architecture: x64
+      - name: Install build dependencies
+        run: pip install wheel
+      - name: Build packages
+        run: python setup.py sdist bdist_wheel
+      - name: Publish a Python distribution to PyPI
+        uses: pypa/gh-action-pypi-publish@master
+        with:
+          password: ${{ secrets.PYPI_TOKEN }}
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 5816f19..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,69 +0,0 @@
-language: python
-cache: pip
-
-python:
-  - 3.6
-  - 3.4
-  - 3.5
-  - pypy3.3-5.2-alpha1
-
-env:
-  - TOXENV=py
-
-addons:
-  apt:
-    packages:
-      - language-pack-de
-
-stages:
-  - test
-  - name: deploy
-    if: tag IS present
-
-install:
-  - pip install tox codecov
-
-script:
-  - tox
-
-after_script:
-  - codecov
-
-jobs:
-  include:
-    - python: 3.7
-      dist: xenial
-      sudo: true
-    - env: TOXENV=flake8
-      python: 3.6
-    - env: TOXENV=docs
-    - env: TOXENV=repl
-    - env: TOXENV=pyicu
-
-    - os: osx
-      language: generic
-      env: TOXENV=py
-      before_install:
-        - brew update
-        - brew link --overwrite gcc
-        - brew upgrade python
-        - pip3 install -U virtualenv  # Travis' images rename pip to pip3.
-        - virtualenv env -p python3
-        - source env/bin/activate
-
-    - stage: deploy
-      script:
-        - pip install wheel twine
-        - python setup.py sdist bdist_wheel
-        - twine upload -u $PYPI_USERNAME -p $PYPI_PASSWORD dist/*
-    - stage: deploy
-      script:
-        - pip install wheel
-        - python setup.py sdist bdist_wheel
-        - gem install dpl
-        - dpl --api-key=$RELEASES_TOKEN --provider=releases --file=dist/* --file_glob=true --skip_cleanup=true
-
-matrix:
-  allow_failures:
-    - python: pypy3.3-5.2-alpha1
-    - os: osx
diff --git a/AUTHORS.rst b/AUTHORS.rst
index 31ed5a2..7bdaef9 100644
--- a/AUTHORS.rst
+++ b/AUTHORS.rst
@@ -19,7 +19,6 @@ Authors are listed in alphabetical order.
 * José Ribeiro <hello@jlbribeiro.com>
 * Mansimar Kaur <mansimarkaur.mks@gmail.com>
 * Markus Unterwaditzer <markus@unterwaditzer.net>
-* Nicolas Évrard <nicoe@openhex.org>
 * Pascal Wichmann <wichmannpas@gmail.com>
 * Paweł Fertyk <pfertyk@openmailbox.org>
 * Rimsha Khan <rimshakhan.rk03@gmail.com>
diff --git a/todoman/formatters.py b/todoman/formatters.py
index 701af4b..7740d3e 100644
--- a/todoman/formatters.py
+++ b/todoman/formatters.py
@@ -66,10 +66,7 @@ class DefaultFormatter:
             priority = self.format_priority_compact(todo.priority)
 
             due = self.format_datetime(todo.due)
-            now = (self.now
-                   if isinstance(todo.due, datetime.datetime)
-                   else self.now.date())
-            if todo.due and todo.due <= now and not todo.is_completed:
+            if todo.due and todo.due <= self.now and not todo.is_completed:
                 due = click.style(due, fg='red')
 
             recurring = '⟳' if todo.is_recurring else ''
@@ -174,21 +171,16 @@ class DefaultFormatter:
             return None
 
         rv = self._parse_datetime_naive(dt)
-        return (
-            rv.replace(tzinfo=self.tz)
-            if isinstance(rv, datetime.datetime)
-            else rv
-        )
+        return rv.replace(tzinfo=self.tz)
 
     def _parse_datetime_naive(self, dt):
-        """Parse dt and returns a naive datetime or a date"""
         try:
             return datetime.datetime.strptime(dt, self.datetime_format)
         except ValueError:
             pass
 
         try:
-            return datetime.datetime.strptime(dt, self.date_format).date()
+            return datetime.datetime.strptime(dt, self.date_format)
         except ValueError:
             pass
 
diff --git a/todoman/model.py b/todoman/model.py
index f02c143..f4e4bda 100644
--- a/todoman/model.py
+++ b/todoman/model.py
@@ -296,7 +296,7 @@ class VtodoWritter:
         dealing with that.
         '''
         if isinstance(dt, date) and not isinstance(dt, datetime):
-            return dt
+            dt = datetime(dt.year, dt.month, dt.day)
 
         if not dt.tzinfo:
             dt = dt.replace(tzinfo=LOCAL_TIMEZONE)
@@ -395,7 +395,7 @@ class Cache:
     may be used for filtering/sorting.
     """
 
-    SCHEMA_VERSION = 7
+    SCHEMA_VERSION = 6
 
     def __init__(self, path):
         self.cache_path = str(path)
@@ -474,9 +474,7 @@ class Cache:
                 "uid" TEXT,
                 "summary" TEXT,
                 "due" INTEGER,
-                "due_dt" INTEGER,
                 "start" INTEGER,
-                "start_dt" INTEGER,
                 "priority" INTEGER,
                 "created_at" INTEGER,
                 "completed_at" INTEGER,
@@ -556,25 +554,16 @@ class Cache:
             raise exceptions.AlreadyExists('file', list_name) from e
 
     def _serialize_datetime(self, todo, field):
-        """
-        Serialize a todo field in two value, the first one is the corresponding
-        timestamp, the second one is a boolean indicating if the serialized
-        value is a date or a datetime.
-
-        :param icalendar.Todo todo: An icalendar component object
-        :param str field: The name of the field to serialize
-        """
         dt = todo.decoded(field, None)
         if not dt:
-            return None, None
+            return None
 
-        is_date = isinstance(dt, date) and not isinstance(dt, datetime)
-        if is_date:
+        if isinstance(dt, date) and not isinstance(dt, datetime):
             dt = datetime(dt.year, dt.month, dt.day)
 
         if not dt.tzinfo:
             dt = dt.replace(tzinfo=LOCAL_TIMEZONE)
-        return dt.timestamp(), is_date
+        return dt.timestamp()
 
     def _serialize_rrule(self, todo, field):
         rrule = todo.get(field)
@@ -604,9 +593,7 @@ class Cache:
                 uid,
                 summary,
                 due,
-                due_dt,
                 start,
-                start_dt,
                 priority,
                 created_at,
                 completed_at,
@@ -619,12 +606,11 @@ class Cache:
                 sequence,
                 last_modified,
                 rrule
-            ) VALUES ({}?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
-                ?)
+            ) VALUES ({}?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
             '''
 
-        due, due_dt = self._serialize_datetime(todo, 'due')
-        start, start_dt = self._serialize_datetime(todo, 'dtstart')
+        due = self._serialize_datetime(todo, 'due')
+        start = self._serialize_datetime(todo, 'dtstart')
 
         if start and due:
             start = None if start >= due else start
@@ -634,20 +620,18 @@ class Cache:
             todo.get('uid'),
             todo.get('summary'),
             due,
-            due_dt,
             start,
-            start_dt,
             todo.get('priority', 0) or None,
-            self._serialize_datetime(todo, 'created')[0],
-            self._serialize_datetime(todo, 'completed')[0],
+            self._serialize_datetime(todo, 'created'),
+            self._serialize_datetime(todo, 'completed'),
             todo.get('percent-complete', None),
-            self._serialize_datetime(todo, 'dtstamp')[0],
+            self._serialize_datetime(todo, 'dtstamp'),
             todo.get('status', 'NEEDS-ACTION'),
             todo.get('description', None),
             todo.get('location', None),
             self._serialize_categories(todo, 'categories'),
             todo.get('sequence', 1),
-            self._serialize_datetime(todo, 'last-modified')[0],
+            self._serialize_datetime(todo, 'last-modified'),
             self._serialize_rrule(todo, 'rrule'),
         )
 
@@ -809,12 +793,9 @@ class Cache:
             seen_paths.add(path)
             yield todo
 
-    def _dt_from_db(self, dt, is_date=False):
+    def _dt_from_db(self, dt):
         if dt:
-            val = datetime.fromtimestamp(dt, LOCAL_TIMEZONE)
-            if is_date:
-                val = val.date()
-            return val
+            return datetime.fromtimestamp(dt, LOCAL_TIMEZONE)
         return None
 
     def _todo_from_db(self, row):
@@ -822,8 +803,8 @@ class Cache:
         todo.id = row['id']
         todo.uid = row['uid']
         todo.summary = row['summary']
-        todo.due = self._dt_from_db(row['due'], row['due_dt'])
-        todo.start = self._dt_from_db(row['start'], row['start_dt'])
+        todo.due = self._dt_from_db(row['due'])
+        todo.start = self._dt_from_db(row['start'])
         todo.priority = row['priority']
         todo.created_at = self._dt_from_db(row['created_at'])
         todo.completed_at = self._dt_from_db(row['completed_at'])


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 0000000..72d4029
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,72 @@
+name: Tests
+
+on: [push]
+
+jobs:
+  tests:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ubuntu-18.04, macOS-10.14]
+        python: [ '3.5', '3.6', '3.7' ]
+    name: Python ${{ matrix.python }} on ${{ matrix.os }}
+    steps:
+      - name: Checkout repo
+        uses: actions/checkout@master
+      - name: Setup python
+        uses: actions/setup-python@v1
+        with:
+          python-version: ${{ matrix.python }}
+          architecture: x64
+      - name: Install test dependencies
+        run: pip install tox codecov
+      - name: Run tests
+        run: tox
+        env:
+          TOXENV: py
+      - name: Report coverage
+        run: codecov
+        env:
+          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
+  test-pyicu:
+    runs-on: ubuntu-18.04
+    name: Run tests with pyicu installed
+    steps:
+      - name: Checkout repo
+        uses: actions/checkout@master
+      - name: Setup python
+        uses: actions/setup-python@v1
+        with:
+          python-version: 3.7
+          architecture: x64
+      - name: Install test dependencies
+        run: pip install tox codecov
+      - name: Run tests
+        run: tox
+        env:
+          TOXENV: pyicu
+      - name: Report coverage
+        run: codecov
+        env:
+          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
+  test-repl:
+    runs-on: ubuntu-18.04
+    name: Run tests with click-repl installed
+    steps:
+      - name: Checkout repo
+        uses: actions/checkout@master
+      - name: Setup python
+        uses: actions/setup-python@v1
+        with:
+          python-version: 3.7
+          architecture: x64
+      - name: Install test dependencies
+        run: pip install tox codecov
+      - name: Run tests
+        run: tox
+        env:
+          TOXENV: repl
+      - name: Report coverage
+        run: codecov
+        env:
+          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
diff --git a/tests/test_backend.py b/tests/test_backend.py
index e4c294d..95bddfe 100644
--- a/tests/test_backend.py
+++ b/tests/test_backend.py
@@ -55,7 +55,6 @@ def test_vtodo_serialization(todo_factory):
         categories=['tea', 'drinking', 'hot'],
         description=description,
         due=datetime(3000, 3, 21),
-        start=date(3000, 3, 21),
         priority=7,
         status='IN-PROCESS',
         summary='Some tea',
@@ -71,7 +70,6 @@ def test_vtodo_serialization(todo_factory):
     assert str(vtodo.get('description')) == description
     assert vtodo.get('priority') == 7
     assert vtodo.decoded('due') == datetime(3000, 3, 21, tzinfo=tzlocal())
-    assert vtodo.decoded('dtstart') == date(3000, 3, 21)
     assert str(vtodo.get('status')) == 'IN-PROCESS'
     assert vtodo.get('rrule') == icalendar.vRecur.from_ical('FREQ=MONTHLY')
 
@@ -97,7 +95,9 @@ def test_sequence_increment(default_database, todo_factory, todos):
 def test_normalize_datetime():
     writter = VtodoWritter(None)
     assert (
-        writter.normalize_datetime(date(2017, 6, 17)) == date(2017, 6, 17)
+        writter.normalize_datetime(date(2017, 6, 17)) == datetime(
+            2017, 6, 17, tzinfo=tzlocal()
+        )
     )
     assert (
         writter.normalize_datetime(datetime(2017, 6, 17)) == datetime(
diff --git a/tests/test_formatter.py b/tests/test_formatter.py
index 5fb8c6f..a7de521 100644
--- a/tests/test_formatter.py
+++ b/tests/test_formatter.py
@@ -96,7 +96,7 @@ def test_parse_datetime(default_formatter):
     tz = pytz.timezone('CET')
 
     parsed = default_formatter.parse_datetime('2017-03-05')
-    assert parsed == date(2017, 3, 5)
+    assert parsed == datetime(2017, 3, 5).replace(tzinfo=tz)
 
     parsed = default_formatter.parse_datetime('2017-03-05 12:00')
     assert parsed == datetime(2017, 3, 5, 12).replace(tzinfo=tz)
diff --git a/tests/test_model.py b/tests/test_model.py
index 310d6e8..1b60f39 100644
--- a/tests/test_model.py
+++ b/tests/test_model.py
@@ -1,4 +1,4 @@
-from datetime import date, datetime, timedelta
+from datetime import datetime, timedelta
 from unittest.mock import patch
 
 import pytest
@@ -56,7 +56,7 @@ def test_due_date(tmpdir, create, todos):
     todos = list(todos())
 
     assert len(todos) == 1
-    assert todos[0].due == date(2017, 6, 17)
+    assert todos[0].due == datetime(2017, 6, 17, tzinfo=tzlocal())
 
 
 def test_change_paths(tmpdir, create):