From 4122ae8f64416aebe3a1792463ce1d9869c4689c Mon Sep 17 00:00:00 2001 From: Kevin Fronczak Date: Wed, 8 Apr 2020 18:45:59 -0400 Subject: [PATCH 1/5] Added time check on latest video --- blinkpy/helpers/constants.py | 2 +- blinkpy/helpers/util.py | 13 +++++++++++++ blinkpy/sync_module.py | 7 ++++++- requirements_test.txt | 2 +- tests/test_sync_module.py | 8 +++++--- tests/test_util.py | 10 +++++++++- 6 files changed, 35 insertions(+), 7 deletions(-) diff --git a/blinkpy/helpers/constants.py b/blinkpy/helpers/constants.py index 574f099..09c1db4 100644 --- a/blinkpy/helpers/constants.py +++ b/blinkpy/helpers/constants.py @@ -60,7 +60,7 @@ ONLINE = {'online': True, 'offline': False} ''' OTHER ''' -TIMESTAMP_FORMAT = '%Y-%m-%dT%H:%M:%S%Z' +TIMESTAMP_FORMAT = '%Y-%m-%dT%H:%M:%S%z' DEFAULT_MOTION_INTERVAL = 1 DEFAULT_REFRESH = 30 diff --git a/blinkpy/helpers/util.py b/blinkpy/helpers/util.py index 66c9f6b..4780a85 100644 --- a/blinkpy/helpers/util.py +++ b/blinkpy/helpers/util.py @@ -2,6 +2,8 @@ import logging import time +from calendar import timegm +from datetime import datetime from functools import partial, wraps from requests import Request, Session, exceptions from blinkpy.helpers.constants import BLINK_URL, TIMESTAMP_FORMAT @@ -11,6 +13,17 @@ import blinkpy.helpers.errors as ERROR _LOGGER = logging.getLogger(__name__) +def time_to_seconds(timestamp): + """Convert TIMESTAMP_FORMAT time to seconds.""" + try: + dtime = datetime.strptime(timestamp, TIMESTAMP_FORMAT) + except ValueError: + _LOGGER.error("Incorrect timestamp format for conversion: %s.", + timestamp) + return False + return timegm(dtime.timetuple()) + + def get_time(time_to_convert=None): """Create blink-compatible timestamp.""" if time_to_convert is None: diff --git a/blinkpy/sync_module.py b/blinkpy/sync_module.py index 6c6a6ce..8984ae6 100644 --- a/blinkpy/sync_module.py +++ b/blinkpy/sync_module.py @@ -5,6 +5,7 @@ import logging from requests.structures import CaseInsensitiveDict from blinkpy import api from blinkpy.camera import BlinkCamera +from blinkpy.helpers.util import time_to_seconds from blinkpy.helpers.constants import ONLINE _LOGGER = logging.getLogger(__name__) @@ -187,9 +188,13 @@ class BlinkSyncModule(): name = entry['device_name'] clip = entry['media'] timestamp = entry['created_at'] - self.motion[name] = True + self.motion[name] = self.check_new_video_time(timestamp) self.last_record[name] = {'clip': clip, 'time': timestamp} except KeyError: _LOGGER.debug("No new videos since last refresh.") return True + + def check_new_video_time(self, timestamp): + """Check if video has timestamp since last refresh.""" + return time_to_seconds(timestamp) > self.blink.last_refresh diff --git a/requirements_test.txt b/requirements_test.txt index 3af0828..c7c4e0b 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -2,7 +2,7 @@ flake8==3.5.0 flake8-docstrings==1.3.0 pylint==2.3.0 pydocstyle==2.1.1 -pytest==3.7.1 +pytest==5.3.5 pytest-cov>=2.3.1 pytest-sugar>=0.9.0 pytest-timeout>=1.0.0 diff --git a/tests/test_sync_module.py b/tests/test_sync_module.py index 191e83c..348e351 100644 --- a/tests/test_sync_module.py +++ b/tests/test_sync_module.py @@ -73,23 +73,25 @@ class TestBlinkSyncModule(unittest.TestCase): 'media': [{ 'device_name': 'foo', 'media': '/foo/bar.mp4', - 'created_at': '1970-01-01T00:00:00+0:00' + 'created_at': '1990-01-01T00:00:00+0:00' }] } + sync_module = self.blink.sync['test'] sync_module.cameras = {'foo': None} + sync_module.blink.last_refresh = 0 self.assertEqual(sync_module.motion, {}) self.assertTrue(sync_module.check_new_videos()) self.assertEqual(sync_module.last_record['foo'], {'clip': '/foo/bar.mp4', - 'time': '1970-01-01T00:00:00+0:00'}) + 'time': '1990-01-01T00:00:00+0:00'}) self.assertEqual(sync_module.motion, {'foo': True}) mock_resp.return_value = {'media': []} self.assertTrue(sync_module.check_new_videos()) self.assertEqual(sync_module.motion, {'foo': False}) self.assertEqual(sync_module.last_record['foo'], {'clip': '/foo/bar.mp4', - 'time': '1970-01-01T00:00:00+0:00'}) + 'time': '1990-01-01T00:00:00+0:00'}) def test_check_new_videos_failed(self, mock_resp): """Test method when response is unexpected.""" diff --git a/tests/test_util.py b/tests/test_util.py index 21524bf..3f888b1 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -3,7 +3,7 @@ import unittest from unittest import mock import time -from blinkpy.helpers.util import Throttle, BlinkURLHandler +from blinkpy.helpers.util import Throttle, BlinkURLHandler, time_to_seconds class TestUtil(unittest.TestCase): @@ -105,3 +105,11 @@ class TestUtil(unittest.TestCase): self.assertEqual(urls.subdomain, 'rest-test') urls = BlinkURLHandler('test', legacy=True) self.assertEqual(urls.subdomain, 'rest.test') + + def test_time_to_seconds(self): + """Test time to seconds conversion.""" + correct_time = '1970-01-01T00:00:05+00:00' + wrong_time = '1/1/1970 00:00:03' + self.assertEqual(time_to_seconds(correct_time), 5) + self.assertFalse(time_to_seconds(wrong_time)) + From efe05525e4f09a6ef52130a2e0744d4a97c23bb0 Mon Sep 17 00:00:00 2001 From: Kevin Fronczak Date: Thu, 9 Apr 2020 14:11:19 -0400 Subject: [PATCH 2/5] Fix tests, pin coverage due to sqlite problems --- .coveragerc | 6 +++--- .travis.yml | 5 ++++- requirements_test.txt | 15 ++++++++------- tests/test_sync_module.py | 22 +++++++++++++++++++--- tests/test_util.py | 1 - tox.ini | 4 ++-- 6 files changed, 36 insertions(+), 17 deletions(-) diff --git a/.coveragerc b/.coveragerc index b050fc2..7e19dd4 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,5 +1,5 @@ [run] -omit = - helpers/* +parallel = true +omit = tests/* - setup.py \ No newline at end of file + setup.py diff --git a/.travis.yml b/.travis.yml index 62b4e38..46fbc25 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,9 @@ sudo: required matrix: fast_finish: true include: - - python: "3.5.3" + - python: "3.7" env: TOXENV=lint + dist: xenial - python: "3.5.3" env: TOXENV=py35 - python: "3.6" @@ -13,6 +14,8 @@ matrix: - python: "3.7" env: TOXENV=py37 dist: xenial + - python: "3.8" + env: TOXENV=py38 - python: "3.8-dev" env: TOXENV=py38 dist: xenial diff --git a/requirements_test.txt b/requirements_test.txt index c7c4e0b..35418ae 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,10 +1,11 @@ -flake8==3.5.0 -flake8-docstrings==1.3.0 -pylint==2.3.0 -pydocstyle==2.1.1 +coverage==4.5.4 +flake8==3.7.9 +flake8-docstrings==1.5.0 +pylint==2.4.4 +pydocstyle==5.0.2 pytest==5.3.5 -pytest-cov>=2.3.1 -pytest-sugar>=0.9.0 -pytest-timeout>=1.0.0 +pytest-cov==2.8.1 +pytest-sugar>=0.9.2 +pytest-timeout>=1.3.3 restructuredtext-lint>=1.0.1 pygments>=2.2.0 diff --git a/tests/test_sync_module.py b/tests/test_sync_module.py index 348e351..e422a09 100644 --- a/tests/test_sync_module.py +++ b/tests/test_sync_module.py @@ -73,7 +73,7 @@ class TestBlinkSyncModule(unittest.TestCase): 'media': [{ 'device_name': 'foo', 'media': '/foo/bar.mp4', - 'created_at': '1990-01-01T00:00:00+0:00' + 'created_at': '1990-01-01T00:00:00+00:00' }] } @@ -84,14 +84,30 @@ class TestBlinkSyncModule(unittest.TestCase): self.assertTrue(sync_module.check_new_videos()) self.assertEqual(sync_module.last_record['foo'], {'clip': '/foo/bar.mp4', - 'time': '1990-01-01T00:00:00+0:00'}) + 'time': '1990-01-01T00:00:00+00:00'}) self.assertEqual(sync_module.motion, {'foo': True}) mock_resp.return_value = {'media': []} self.assertTrue(sync_module.check_new_videos()) self.assertEqual(sync_module.motion, {'foo': False}) self.assertEqual(sync_module.last_record['foo'], {'clip': '/foo/bar.mp4', - 'time': '1990-01-01T00:00:00+0:00'}) + 'time': '1990-01-01T00:00:00+00:00'}) + + def test_check_new_videos_old_date(self, mock_resp): + """Test videos return response with old date.""" + mock_resp.return_value = { + 'media': [{ + 'device_name': 'foo', + 'media': '/foo/bar.mp4', + 'created_at': '1970-01-01T00:00:00+00:00' + }] + } + + sync_module = self.blink.sync['test'] + sync_module.cameras = {'foo': None} + sync_module.blink.last_refresh = 1000 + self.assertTrue(sync_module.check_new_videos()) + self.assertEqual(sync_module.motion, {'foo': False}) def test_check_new_videos_failed(self, mock_resp): """Test method when response is unexpected.""" diff --git a/tests/test_util.py b/tests/test_util.py index 3f888b1..d5c1a49 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -112,4 +112,3 @@ class TestUtil(unittest.TestCase): wrong_time = '1/1/1970 00:00:03' self.assertEqual(time_to_seconds(correct_time), 5) self.assertFalse(time_to_seconds(wrong_time)) - diff --git a/tox.ini b/tox.ini index 3e9ccae..6245069 100644 --- a/tox.ini +++ b/tox.ini @@ -8,11 +8,11 @@ setenv = LANG=en_US.UTF-8 PYTHONPATH = {toxinidir} commands = - py.test --timeout=30 --duration=10 --cov=blinkpy --cov-report term-missing {posargs} + pytest --timeout=9 --durations=10 --cov=blinkpy --cov-report term-missing {posargs} deps = -r{toxinidir}/requirements.txt -r{toxinidir}/requirements_test.txt - + [testenv:lint] deps = -r{toxinidir}/requirements.txt From 2f4e8fcade75f02e46f74ff6c9b729f52a89479a Mon Sep 17 00:00:00 2001 From: Kevin Fronczak Date: Thu, 9 Apr 2020 14:14:47 -0400 Subject: [PATCH 3/5] Ignore flake8-black errors --- .flake8 | 2 ++ .hound.yml | 4 ++++ 2 files changed, 6 insertions(+) create mode 100644 .flake8 diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..20d6db4 --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +ignore = BLK100 diff --git a/.hound.yml b/.hound.yml index d3439b3..d76df3d 100644 --- a/.hound.yml +++ b/.hound.yml @@ -4,4 +4,8 @@ python: ignored_directories: - docs +flake8: + enabled: true + config_file: .flake8 + fail_on_violations: true From 2ebbe46ada882a979411d356ae8038135bf3f252 Mon Sep 17 00:00:00 2001 From: Kevin Fronczak Date: Thu, 9 Apr 2020 14:51:06 -0400 Subject: [PATCH 4/5] Change time conversion to something that supports older python versions --- blinkpy/helpers/util.py | 4 ++-- blinkpy/sync_module.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/blinkpy/helpers/util.py b/blinkpy/helpers/util.py index 4780a85..84e2816 100644 --- a/blinkpy/helpers/util.py +++ b/blinkpy/helpers/util.py @@ -3,9 +3,9 @@ import logging import time from calendar import timegm -from datetime import datetime from functools import partial, wraps from requests import Request, Session, exceptions +import dateutil.parser from blinkpy.helpers.constants import BLINK_URL, TIMESTAMP_FORMAT import blinkpy.helpers.errors as ERROR @@ -16,7 +16,7 @@ _LOGGER = logging.getLogger(__name__) def time_to_seconds(timestamp): """Convert TIMESTAMP_FORMAT time to seconds.""" try: - dtime = datetime.strptime(timestamp, TIMESTAMP_FORMAT) + dtime = dateutil.parser.isoparse(timestamp) except ValueError: _LOGGER.error("Incorrect timestamp format for conversion: %s.", timestamp) diff --git a/blinkpy/sync_module.py b/blinkpy/sync_module.py index 8984ae6..d45c42a 100644 --- a/blinkpy/sync_module.py +++ b/blinkpy/sync_module.py @@ -164,7 +164,7 @@ class BlinkSyncModule(): def check_new_videos(self): """Check if new videos since last refresh.""" try: - interval = self.blink.last_refresh - self.motion_interval*60 + interval = self.blink.last_refresh - self.motion_interval * 60 except TypeError: # This is the first start, so refresh hasn't happened yet. # No need to check for motion. From eb498bfb22b3713087e33426567a2ef1f7f96d6b Mon Sep 17 00:00:00 2001 From: Kevin Fronczak Date: Thu, 9 Apr 2020 15:01:33 -0400 Subject: [PATCH 5/5] Pin coverage in traviswq --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 46fbc25..dcd17e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,9 @@ matrix: env: TOXENV=py38 dist: xenial -install: pip install -U tox coveralls +install: + - pip install -U tox coveralls + - pip install coverage==4.5.4 language: python script: tox after_success: coveralls