Skip to content

Commit

Permalink
refactor so estimate release is configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
aidan- committed Jan 25, 2023
1 parent 848cde1 commit 118d249
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 45 deletions.
20 changes: 13 additions & 7 deletions flexget/components/estimate_release/estimate_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@
def init_estimators(manager) -> None:
"""Prepare the list of available estimator plugins."""

estimators = {
p.name.replace('est_', ''): p for p in plugin.get_plugins(interface=ESTIMATOR_INTERFACE)
}
for provider in plugin.get_plugins(interface=ESTIMATOR_INTERFACE):
estimators[provider.name.replace('est_', '')] = provider

logger.debug('setting default estimators to {}', list(estimators.keys()))

Expand All @@ -35,6 +34,9 @@ class EstimateRelease:
for various things (series, movies).
"""

def __init__(self):
self.task_estimate_config = {}

@property
def schema(self) -> Dict[str, Any]:
"""Create schema for that allows configuring estimator providers and
Expand Down Expand Up @@ -83,12 +85,16 @@ def get_estimators(self) -> List[PluginInfo]:
"""
if "providers" in self.task_estimate_config:
# Map task configured providers to plugin instance map
task_estimators = [
estimators[p].instance.estimate for p in self.task_estimate_config['providers']
]
try:
task_estimators = [
estimators[p].instance.estimate for p in self.task_estimate_config['providers']
]
except KeyError as error:
logger.error(f"invalid provider plugin given: {error}")
raise
else:
# Use all loaded estimator plugins
task_estimators = [e.instance.estimate for e in estimators]
task_estimators = [e.instance.estimate for e in estimators.values()]

return sorted(
task_estimators,
Expand Down
44 changes: 9 additions & 35 deletions flexget/plugins/input/discover.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,9 @@ class Discover:
},
'interval': {'type': 'string', 'format': 'interval', 'default': '5 hours'},
'release_estimations': {
'oneOf': [
{
'type': 'string',
'default': 'strict',
'enum': ['loose', 'strict', 'ignore', 'smart'],
},
{
'type': 'object',
'properties': {'optimistic': {'type': 'string', 'format': 'interval'}},
'required': ['optimistic'],
},
]
'type': 'string',
'default': 'strict',
'enum': ['loose', 'strict', 'ignore', 'smart'],
},
'limit': {'type': 'integer', 'minimum': 1},
},
Expand Down Expand Up @@ -190,21 +181,21 @@ def estimated(self, entries, estimation_mode):
data_exists = estimation['data_exists']

if est_date is None:
if estimation_mode['mode'] == 'strict':
if estimation_mode == 'strict':
logger.debug('No release date could be determined for {}', entry['title'])
entry.reject('has no release date')
entry.complete()
elif estimation_mode['mode'] == 'smart' and data_exists:
elif estimation_mode == 'smart' and data_exists:
logger.debug(
'No release date could be determined for {}, but exists data',
'No release date could be determined for {}, but data exists',
entry['title'],
)
entry.reject('exists but has no release date')
entry.complete()
elif estimation_mode['mode'] == 'smart' and not data_exists:
elif estimation_mode == 'smart' and not data_exists:
logger.debug(
'Discovering because mode is \'{}\' and no data is found for entry',
estimation_mode['mode'],
estimation_mode,
)
result.append(entry)
else:
Expand All @@ -216,16 +207,6 @@ def estimated(self, entries, estimation_mode):
if datetime.datetime.now() >= est_date:
logger.debug('{} has been released at {}', entry['title'], est_date)
result.append(entry)
elif datetime.datetime.now() >= est_date - parse_timedelta(
estimation_mode['optimistic']
):
logger.debug(
'{} will be released at {}. Ignoring release estimation because estimated release date is in less than {}',
entry['title'],
est_date,
estimation_mode['optimistic'],
)
result.append(entry)
else:
entry.reject('has not been released')
entry.complete()
Expand Down Expand Up @@ -292,13 +273,6 @@ def interval_expired(self, config, task, entries):
return result

def on_task_input(self, task, config):
config.setdefault('release_estimations', {})
if not isinstance(config['release_estimations'], dict):
config['release_estimations'] = {'mode': config['release_estimations']}

config['release_estimations'].setdefault('mode', 'strict')
config['release_estimations'].setdefault('optimistic', '0 days')

task.no_entries_ok = True
entries = aggregate_inputs(task, config['what'])
logger.verbose('Discovering {} titles ...', len(entries))
Expand All @@ -310,7 +284,7 @@ def on_task_input(self, task, config):
# TODO: the entries that are estimated should be given priority over expiration
entries = self.interval_expired(config, task, entries)
estimation_mode = config['release_estimations']
if estimation_mode['mode'] != 'ignore':
if estimation_mode != 'ignore':
entries = self.estimated(entries, estimation_mode)
return self.execute_searches(config, entries, task)

Expand Down
100 changes: 97 additions & 3 deletions flexget/tests/test_discover.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from datetime import datetime, timedelta
from unittest.mock import MagicMock, patch

import pytest

from flexget import plugin
from flexget.entry import Entry
Expand Down Expand Up @@ -30,14 +33,14 @@ def search(self, task, entry, config=None):
plugin.register(SearchPlugin, 'test_search', interfaces=['search'], api_ver=2)


class EstRelease:
class FakeEstimator:
"""Fake release estimate plugin. Just returns 'est_release' entry field."""

def estimate(self, entry):
return entry.get('est_release')


plugin.register(EstRelease, 'test_release', interfaces=['estimate_release'], api_ver=2)
plugin.register(FakeEstimator, 'fake_estimator', interfaces=['estimate_release'], api_ver=2)


class TestDiscover:
Expand Down Expand Up @@ -225,7 +228,7 @@ class TestEmitSeriesInDiscover:
begin: s02e01
identified_by: ep
season_packs: yes
max_reruns: 0
max_reruns: 0
"""

def test_next_series_episodes_rerun(self, execute_task):
Expand Down Expand Up @@ -282,3 +285,94 @@ def test_next_series_seasons_with_completed_seasons(self, execute_task):
)
task = execute_task('test_next_series_seasons')
assert task.find_entry(title='My Show 2 S03')


class TestEstimateReleaseViaDiscover:
"""Suite of tests focusing on the configuration of the estimate_release
plugin.
"""

config = """
tasks:
test_estimates:
discover:
interval: 0 seconds
what:
- mock:
- title: Foo
from:
- test_search: yes
"""

def test_default_release_date_modifier(self, execute_task, manager):
"""Test that the default release_date_modifier value of '0 days'
results in only matching entries released in the past.
"""
mock_config = manager.config['tasks']['test_estimates']['discover']['what'][0]['mock']
# It should not be searched before the release date
mock_config[0]['est_release'] = {
'data_exists': True,
'entity_date': (datetime.now() + timedelta(days=1)),
}
task = execute_task('test_estimates')
assert len(task.entries) == 0
# It should be searched after the release date
mock_config[0]['est_release'] = {'data_exists': True, 'entity_date': datetime.now()}
task = execute_task('test_estimates')
assert len(task.entries) == 1

def test_release_date_modifier_positive(self, execute_task, manager):
"""Test that providing a 'positive' offset value for the
estimate_release config results in matching entries that have been
released far enough in the past.
"""
manager.config['tasks']['test_estimates']['estimate_release'] = {"offset": '7 days'}
discover_config = manager.config['tasks']['test_estimates']['discover']
mock_config = discover_config['what'][0]['mock']
mock_config[0]['est_release'] = {
'data_exists': True,
'entity_date': datetime.now(),
}
task = execute_task('test_estimates')
assert len(task.entries) == 0
mock_config[0]['est_release'] = {
'data_exists': True,
'entity_date': (datetime.now() - timedelta(days=7)),
}
task = execute_task('test_estimates')
assert len(task.entries) == 1

def test_release_date_modifier_negative(self, execute_task, manager):
"""Test that providing a 'negative' offset value for the
estimate_release config results in matching entries that have a release
date in the future.
"""
manager.config['tasks']['test_estimates']['estimate_release'] = {"offset": '-7 days'}
discover_config = manager.config['tasks']['test_estimates']['discover']
mock_config = discover_config['what'][0]['mock']
mock_config[0]['est_release'] = {
'data_exists': True,
'entity_date': datetime.now() + timedelta(days=5),
}
task = execute_task('test_estimates')
assert len(task.entries) == 1
mock_config[0]['est_release'] = {
'data_exists': True,
'entity_date': (datetime.now() + timedelta(days=9)),
}
task = execute_task('test_estimates')
assert len(task.entries) == 0

def test_provider_override_invalid(self, execute_task, manager):
"""Test that an invalid provider results in an exception being raised."""
manager.config['tasks']['test_estimates']['estimate_release'] = {
"providers": ['does-not-exist']
}
discover_config = manager.config['tasks']['test_estimates']['discover']
mock_config = discover_config['what'][0]['mock']
mock_config[0]['est_release'] = {
'data_exists': True,
'entity_date': datetime.now() + timedelta(days=5),
}
with pytest.raises(Exception):
execute_task('test_estimates')

0 comments on commit 118d249

Please sign in to comment.