Feedreader configurable update interval and max entries (#14487)

This commit is contained in:
Malte Franken 2018-05-18 15:25:08 +10:00 committed by Sebastian Muszynski
parent 1c3293ac85
commit a3777c4ea8
2 changed files with 75 additions and 27 deletions

View file

@ -4,7 +4,7 @@ Support for RSS/Atom feeds.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/feedreader/
"""
from datetime import datetime
from datetime import datetime, timedelta
from logging import getLogger
from os.path import exists
from threading import Lock
@ -12,8 +12,8 @@ import pickle
import voluptuous as vol
from homeassistant.const import EVENT_HOMEASSISTANT_START
from homeassistant.helpers.event import track_utc_time_change
from homeassistant.const import EVENT_HOMEASSISTANT_START, CONF_SCAN_INTERVAL
from homeassistant.helpers.event import track_time_interval
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['feedparser==5.2.1']
@ -21,16 +21,22 @@ REQUIREMENTS = ['feedparser==5.2.1']
_LOGGER = getLogger(__name__)
CONF_URLS = 'urls'
CONF_MAX_ENTRIES = 'max_entries'
DEFAULT_MAX_ENTRIES = 20
DEFAULT_SCAN_INTERVAL = timedelta(hours=1)
DOMAIN = 'feedreader'
EVENT_FEEDREADER = 'feedreader'
MAX_ENTRIES = 20
CONFIG_SCHEMA = vol.Schema({
DOMAIN: {
vol.Required(CONF_URLS): vol.All(cv.ensure_list, [cv.url]),
vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL):
cv.time_period,
vol.Optional(CONF_MAX_ENTRIES, default=DEFAULT_MAX_ENTRIES):
cv.positive_int
}
}, extra=vol.ALLOW_EXTRA)
@ -38,18 +44,23 @@ CONFIG_SCHEMA = vol.Schema({
def setup(hass, config):
"""Set up the Feedreader component."""
urls = config.get(DOMAIN)[CONF_URLS]
scan_interval = config.get(DOMAIN).get(CONF_SCAN_INTERVAL)
max_entries = config.get(DOMAIN).get(CONF_MAX_ENTRIES)
data_file = hass.config.path("{}.pickle".format(DOMAIN))
storage = StoredData(data_file)
feeds = [FeedManager(url, hass, storage) for url in urls]
feeds = [FeedManager(url, scan_interval, max_entries, hass, storage) for
url in urls]
return len(feeds) > 0
class FeedManager(object):
"""Abstraction over Feedparser module."""
def __init__(self, url, hass, storage):
"""Initialize the FeedManager object, poll every hour."""
def __init__(self, url, scan_interval, max_entries, hass, storage):
"""Initialize the FeedManager object, poll as per scan interval."""
self._url = url
self._scan_interval = scan_interval
self._max_entries = max_entries
self._feed = None
self._hass = hass
self._firstrun = True
@ -69,8 +80,8 @@ class FeedManager(object):
def _init_regular_updates(self, hass):
"""Schedule regular updates at the top of the clock."""
track_utc_time_change(
hass, lambda now: self._update(), minute=0, second=0)
track_time_interval(hass, lambda now: self._update(),
self._scan_interval)
@property
def last_update_successful(self):
@ -116,10 +127,10 @@ class FeedManager(object):
def _filter_entries(self):
"""Filter the entries provided and return the ones to keep."""
if len(self._feed.entries) > MAX_ENTRIES:
if len(self._feed.entries) > self._max_entries:
_LOGGER.debug("Processing only the first %s entries "
"in feed %s", MAX_ENTRIES, self._url)
self._feed.entries = self._feed.entries[0:MAX_ENTRIES]
"in feed %s", self._max_entries, self._url)
self._feed.entries = self._feed.entries[0:self._max_entries]
def _update_and_fire_entry(self, entry):
"""Update last_entry_timestamp and fire entry."""

View file

@ -1,6 +1,6 @@
"""The tests for the feedreader component."""
import time
from datetime import datetime
from datetime import datetime, timedelta
import unittest
from genericpath import exists
@ -11,12 +11,12 @@ from unittest.mock import patch
from homeassistant.components import feedreader
from homeassistant.components.feedreader import CONF_URLS, FeedManager, \
StoredData, EVENT_FEEDREADER
from homeassistant.const import EVENT_HOMEASSISTANT_START
StoredData, EVENT_FEEDREADER, DEFAULT_SCAN_INTERVAL, CONF_MAX_ENTRIES, \
DEFAULT_MAX_ENTRIES
from homeassistant.const import EVENT_HOMEASSISTANT_START, CONF_SCAN_INTERVAL
from homeassistant.core import callback
from homeassistant.setup import setup_component
from tests.common import get_test_home_assistant, assert_setup_component, \
load_fixture
from tests.common import get_test_home_assistant, load_fixture
_LOGGER = getLogger(__name__)
@ -26,6 +26,18 @@ VALID_CONFIG_1 = {
CONF_URLS: [URL]
}
}
VALID_CONFIG_2 = {
feedreader.DOMAIN: {
CONF_URLS: [URL],
CONF_SCAN_INTERVAL: 60
}
}
VALID_CONFIG_3 = {
feedreader.DOMAIN: {
CONF_URLS: [URL],
CONF_MAX_ENTRIES: 100
}
}
class TestFeedreaderComponent(unittest.TestCase):
@ -45,11 +57,28 @@ class TestFeedreaderComponent(unittest.TestCase):
def test_setup_one_feed(self):
"""Test the general setup of this component."""
with assert_setup_component(1, 'feedreader'):
with patch("homeassistant.components.feedreader."
"track_time_interval") as track_method:
self.assertTrue(setup_component(self.hass, feedreader.DOMAIN,
VALID_CONFIG_1))
track_method.assert_called_once_with(self.hass, mock.ANY,
DEFAULT_SCAN_INTERVAL)
def setup_manager(self, feed_data):
def test_setup_scan_interval(self):
"""Test the setup of this component with scan interval."""
with patch("homeassistant.components.feedreader."
"track_time_interval") as track_method:
self.assertTrue(setup_component(self.hass, feedreader.DOMAIN,
VALID_CONFIG_2))
track_method.assert_called_once_with(self.hass, mock.ANY,
timedelta(seconds=60))
def test_setup_max_entries(self):
"""Test the setup of this component with max entries."""
self.assertTrue(setup_component(self.hass, feedreader.DOMAIN,
VALID_CONFIG_3))
def setup_manager(self, feed_data, max_entries=DEFAULT_MAX_ENTRIES):
"""Generic test setup method."""
events = []
@ -67,12 +96,13 @@ class TestFeedreaderComponent(unittest.TestCase):
feedreader.DOMAIN))
storage = StoredData(data_file)
with patch("homeassistant.components.feedreader."
"track_utc_time_change") as track_method:
manager = FeedManager(feed_data, self.hass, storage)
"track_time_interval") as track_method:
manager = FeedManager(feed_data, DEFAULT_SCAN_INTERVAL,
max_entries, self.hass, storage)
# Can't use 'assert_called_once' here because it's not available
# in Python 3.5 yet.
track_method.assert_called_once_with(self.hass, mock.ANY, minute=0,
second=0)
track_method.assert_called_once_with(self.hass, mock.ANY,
DEFAULT_SCAN_INTERVAL)
# Artificially trigger update.
self.hass.bus.fire(EVENT_HOMEASSISTANT_START)
# Collect events.
@ -116,12 +146,18 @@ class TestFeedreaderComponent(unittest.TestCase):
manager3, events3 = self.setup_manager(feed_data3)
assert len(events3) == 0
def test_feed_max_length(self):
"""Test long feed beyond the 20 entry limit."""
def test_feed_default_max_length(self):
"""Test long feed beyond the default 20 entry limit."""
feed_data = load_fixture('feedreader2.xml')
manager, events = self.setup_manager(feed_data)
assert len(events) == 20
def test_feed_max_length(self):
"""Test long feed beyond a configured 5 entry limit."""
feed_data = load_fixture('feedreader2.xml')
manager, events = self.setup_manager(feed_data, max_entries=5)
assert len(events) == 5
def test_feed_without_publication_date(self):
"""Test simple feed with entry without publication date."""
feed_data = load_fixture('feedreader3.xml')
@ -141,7 +177,8 @@ class TestFeedreaderComponent(unittest.TestCase):
data_file = self.hass.config.path("{}.pickle".format(
feedreader.DOMAIN))
storage = StoredData(data_file)
manager = FeedManager("FEED DATA", self.hass, storage)
manager = FeedManager("FEED DATA", DEFAULT_SCAN_INTERVAL,
DEFAULT_MAX_ENTRIES, self.hass, storage)
# Artificially trigger update.
self.hass.bus.fire(EVENT_HOMEASSISTANT_START)
# Collect events.