added event selector (w/o tests)

This commit is contained in:
Simone Robutti 2021-05-04 22:58:00 +02:00
parent e938ba709b
commit 3fe3c8efc6
7 changed files with 171 additions and 27 deletions

View File

@ -1,10 +1,18 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass, asdict from dataclasses import dataclass, asdict
from datetime import datetime from datetime import datetime
from enum import Enum
from typing import Optional from typing import Optional
from jinja2 import Template from jinja2 import Template
class PublicationStatus(Enum):
WAITING = 1
FAILED = 2
PARTIAL = 3
COMPLETED = 4
@dataclass @dataclass
class MobilizonEvent: class MobilizonEvent:
"""Class representing an event retrieved from Mobilizon.""" """Class representing an event retrieved from Mobilizon."""
@ -18,9 +26,16 @@ class MobilizonEvent:
mobilizon_id: str mobilizon_id: str
thumbnail_link: Optional[str] = None thumbnail_link: Optional[str] = None
location: Optional[str] = None location: Optional[str] = None
publication_time: Optional[datetime] = None
publication_status: PublicationStatus = PublicationStatus.WAITING
def begins_before(self, other: MobilizonEvent) -> bool: def __post_init__(self):
return self.begin_datetime < other.begin_datetime assert self.begin_datetime < self.end_datetime
if self.publication_time:
assert self.publication_status in [
PublicationStatus.COMPLETED,
PublicationStatus.PARTIAL,
]
def _fill_template(self, pattern: Template) -> str: def _fill_template(self, pattern: Template) -> str:
return pattern.render(**asdict(self)) return pattern.render(**asdict(self))

46
poetry.lock generated
View File

@ -56,6 +56,34 @@ toml = ["toml"]
vault = ["hvac"] vault = ["hvac"]
yaml = ["ruamel.yaml"] yaml = ["ruamel.yaml"]
[[package]]
name = "hypothesis"
version = "6.10.1"
description = "A library for property-based testing"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
attrs = ">=19.2.0"
sortedcontainers = ">=2.1.0,<3.0.0"
[package.extras]
all = ["black (>=19.10b0)", "click (>=7.0)", "django (>=2.2)", "dpcontracts (>=0.4)", "lark-parser (>=0.6.5)", "libcst (>=0.3.16)", "numpy (>=1.9.0)", "pandas (>=0.25)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "importlib-resources (>=3.3.0)", "importlib-metadata (>=3.6)", "backports.zoneinfo (>=0.2.1)", "tzdata (>=2020.4)"]
cli = ["click (>=7.0)", "black (>=19.10b0)", "rich (>=9.0.0)"]
codemods = ["libcst (>=0.3.16)"]
dateutil = ["python-dateutil (>=1.4)"]
django = ["pytz (>=2014.1)", "django (>=2.2)"]
dpcontracts = ["dpcontracts (>=0.4)"]
ghostwriter = ["black (>=19.10b0)"]
lark = ["lark-parser (>=0.6.5)"]
numpy = ["numpy (>=1.9.0)"]
pandas = ["pandas (>=0.25)"]
pytest = ["pytest (>=4.6)"]
pytz = ["pytz (>=2014.1)"]
redis = ["redis (>=3.0.0)"]
zoneinfo = ["importlib-resources (>=3.3.0)", "backports.zoneinfo (>=0.2.1)", "tzdata (>=2020.4)"]
[[package]] [[package]]
name = "iso8601" name = "iso8601"
version = "0.1.14" version = "0.1.14"
@ -170,6 +198,14 @@ category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
[[package]]
name = "sortedcontainers"
version = "2.3.0"
description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set"
category = "dev"
optional = false
python-versions = "*"
[[package]] [[package]]
name = "tortoise-orm" name = "tortoise-orm"
version = "0.17.2" version = "0.17.2"
@ -210,7 +246,7 @@ python-versions = "*"
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.9" python-versions = "^3.9"
content-hash = "2a9d2f27ed4c4197d4dd022c2e1f8968ef7fff2ab73145a4bd74712f35d88855" content-hash = "f78f8e295d8c94dfe0e4e0f29ce5056f5cea948037cc62b9903b6bc8263a0db6"
[metadata.files] [metadata.files]
aiosqlite = [ aiosqlite = [
@ -233,6 +269,10 @@ dynaconf = [
{file = "dynaconf-3.1.4-py2.py3-none-any.whl", hash = "sha256:e6f383b84150b70fc439c8b2757581a38a58d07962aa14517292dcce1a77e160"}, {file = "dynaconf-3.1.4-py2.py3-none-any.whl", hash = "sha256:e6f383b84150b70fc439c8b2757581a38a58d07962aa14517292dcce1a77e160"},
{file = "dynaconf-3.1.4.tar.gz", hash = "sha256:b2f472d83052f809c5925565b8a2ba76a103d5dc1dbb9748b693ed67212781b9"}, {file = "dynaconf-3.1.4.tar.gz", hash = "sha256:b2f472d83052f809c5925565b8a2ba76a103d5dc1dbb9748b693ed67212781b9"},
] ]
hypothesis = [
{file = "hypothesis-6.10.1-py3-none-any.whl", hash = "sha256:1d65f58d82d1cbd35b6441bda3bb11cb1adc879d6b2af191aea388fa412171b1"},
{file = "hypothesis-6.10.1.tar.gz", hash = "sha256:586b6c46e90878c2546743afbed348bca51e1f30e3461fa701fad58c2c47c650"},
]
iso8601 = [ iso8601 = [
{file = "iso8601-0.1.14-py2.py3-none-any.whl", hash = "sha256:e7e1122f064d626e17d47cd5106bed2c620cb38fe464999e0ddae2b6d2de6004"}, {file = "iso8601-0.1.14-py2.py3-none-any.whl", hash = "sha256:e7e1122f064d626e17d47cd5106bed2c620cb38fe464999e0ddae2b6d2de6004"},
{file = "iso8601-0.1.14.tar.gz", hash = "sha256:8aafd56fa0290496c5edbb13c311f78fa3a241f0853540da09d9363eae3ebd79"}, {file = "iso8601-0.1.14.tar.gz", hash = "sha256:8aafd56fa0290496c5edbb13c311f78fa3a241f0853540da09d9363eae3ebd79"},
@ -303,6 +343,10 @@ pytz = [
{file = "pytz-2020.5-py2.py3-none-any.whl", hash = "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4"}, {file = "pytz-2020.5-py2.py3-none-any.whl", hash = "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4"},
{file = "pytz-2020.5.tar.gz", hash = "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5"}, {file = "pytz-2020.5.tar.gz", hash = "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5"},
] ]
sortedcontainers = [
{file = "sortedcontainers-2.3.0-py2.py3-none-any.whl", hash = "sha256:37257a32add0a3ee490bb170b599e93095eed89a55da91fa9f48753ea12fd73f"},
{file = "sortedcontainers-2.3.0.tar.gz", hash = "sha256:59cc937650cf60d677c16775597c89a960658a09cf7c1a668f86e1e4464b10a1"},
]
tortoise-orm = [ tortoise-orm = [
{file = "tortoise-orm-0.17.2.tar.gz", hash = "sha256:1a742b2f15a31d47a8dea7706b478cc9a7ce9af268b61d77d0fa22cfbaea271a"}, {file = "tortoise-orm-0.17.2.tar.gz", hash = "sha256:1a742b2f15a31d47a8dea7706b478cc9a7ce9af268b61d77d0fa22cfbaea271a"},
{file = "tortoise_orm-0.17.2-py3-none-any.whl", hash = "sha256:b0c02be3800398053058377ddca91fa051eb98eebb704d2db2a3ab1c6a58e347"}, {file = "tortoise_orm-0.17.2-py3-none-any.whl", hash = "sha256:b0c02be3800398053058377ddca91fa051eb98eebb704d2db2a3ab1c6a58e347"},

View File

@ -13,6 +13,7 @@ Jinja2 = "^2.11.3"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pytest = "^5.2" pytest = "^5.2"
hypothesis = "^6.10.1"
[build-system] [build-system]
requires = ["poetry-core>=1.0.0"] requires = ["poetry-core>=1.0.0"]

View File

@ -0,0 +1,33 @@
import hypothesis
from hypothesis import assume
from hypothesis.provisional import urls
from hypothesis.strategies import characters, datetimes, text, sampled_from
from mobilizon_bots.event.event import MobilizonEvent, PublicationStatus
@hypothesis.strategies.composite
def events(draw, published: bool = False):
begin_datetime = draw(datetimes())
end_datetime = draw(datetimes())
assume(begin_datetime < end_datetime)
return MobilizonEvent(
name=draw(characters()),
description=draw(text()),
begin_datetime=begin_datetime,
end_datetime=end_datetime,
last_accessed=draw(datetimes()),
mobilizon_link=draw(urls()),
mobilizon_id=draw(characters()),
thumbnail_link=draw(urls()),
location=draw(text()),
publication_time=draw(datetimes()) if published else None,
publication_status=draw(
sampled_from(
[PublicationStatus.COMPLETED, PublicationStatus.PARTIAL]
if published
else [PublicationStatus.WAITING, PublicationStatus.FAILED]
)
),
)

View File

@ -0,0 +1,58 @@
from datetime import timedelta, datetime
from typing import List, Optional
from mobilizon_bots.event.event import MobilizonEvent
from abc import ABC, abstractmethod
class EventSelectionStrategy(ABC):
@abstractmethod
def select(
self,
published_events: List[MobilizonEvent],
unpublished_events: List[MobilizonEvent],
) -> Optional[MobilizonEvent]:
pass
class SelectNextEventStrategy(EventSelectionStrategy):
def __init__(self, minimum_break_between_events: timedelta = timedelta(days=0)):
self.minimum_break_between_events = minimum_break_between_events
def select(
self,
published_events: List[MobilizonEvent],
unpublished_events: List[MobilizonEvent],
) -> Optional[MobilizonEvent]:
last_published_event = published_events[-1]
first_unpublished_event = unpublished_events[0]
assert (
last_published_event.publication_time < datetime.now()
), "Last published event has been published in the future"
if (
last_published_event.publication_time + self.minimum_break_between_events
> first_unpublished_event.begin_datetime
):
return None
return first_unpublished_event
class EventSelector:
def __init__(
self,
published_events: List[MobilizonEvent],
unpublished_events: List[MobilizonEvent],
):
self.published_events = published_events.sort(key=lambda x: x.begin_datetime)
self.unpublished_events = unpublished_events.sort(
key=lambda x: x.begin_datetime
)
def select_event_to_publish(
self, strategy: EventSelectionStrategy
) -> Optional[MobilizonEvent]:
return strategy.select(self.published_events, self.unpublished_events)

View File

@ -1,9 +1,11 @@
from datetime import datetime from datetime import datetime
import pytest import pytest
from hypothesis import given
from jinja2 import Template from jinja2 import Template
from mobilizon_bots.event.event import MobilizonEvent from mobilizon_bots.event.event import MobilizonEvent
from tests import events
@pytest.fixture() @pytest.fixture()
@ -42,27 +44,6 @@ def test_format(event, simple_template):
) )
def test_is_newer(): @given(events())
older_event = MobilizonEvent( def test_event_properties(event):
name="test event", assert event.end_datetime > event.begin_datetime
description="description of the event",
begin_datetime=datetime(year=2021, month=1, day=1, hour=11, minute=30),
end_datetime=datetime(year=2021, month=1, day=1, hour=12, minute=30),
last_accessed=datetime.now(),
mobilizon_link="http://some_link.com/123",
mobilizon_id="12345",
thumbnail_link="http://some_link.com/123.jpg",
location="location",
)
newer_event = MobilizonEvent(
name="test event",
description="description of the event",
begin_datetime=datetime(year=2021, month=1, day=2, hour=11, minute=30),
end_datetime=datetime(year=2021, month=1, day=1, hour=12, minute=30),
last_accessed=datetime.now(),
mobilizon_link="http://some_link.com/123",
mobilizon_id="12345",
thumbnail_link="http://some_link.com/123.jpg",
location="location",
)
assert older_event.begins_before(newer_event)

View File

@ -0,0 +1,12 @@
from hypothesis import given
from hypothesis.strategies import lists
from tests import events
@given(
unpublished_events=lists(events(published=True), max_size=5),
published_events=lists(events(published=False), max_size=3),
)
def test_select_next_event_properties(unpublished_events, published_events):
pass