recap header (#79)

* added basic recap feature (no error handling)

* introduced abstractpublication

* extracted base reports

* added error report to recap

* added test

* added docs

* implemented publisher and formatter

* fixed API for recap

* removed redundant config validation

* added config sample

* added active publisher test

* added recap header template
This commit is contained in:
Simone Robutti 2021-10-17 14:09:24 +02:00 committed by GitHub
parent 71b65342b9
commit 489d41179e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 53 additions and 8 deletions

View File

@ -169,6 +169,13 @@ class AbstractEventFormatter(LoggerMixin, ConfLoaderMixin):
return False
return True
def get_recap_header(self):
template_path = (
self.conf.recap_header_template_path
or self.default_recap_header_template_path
)
return JINJA_ENV.get_template(template_path).render()
def get_recap_fragment_template(self) -> Template:
template_path = (
self.conf.recap_template_path or self.default_recap_template_path

View File

@ -176,7 +176,7 @@ class RecapCoordinator:
for recap_publication in self.recap_publications:
try:
fragments = []
fragments = [recap_publication.formatter.get_recap_header()]
for event in recap_publication.events:
fragments.append(
recap_publication.formatter.get_recap_fragment(event)

View File

@ -25,6 +25,10 @@ class TelegramFormatter(AbstractEventFormatter):
"mobilizon_reshare.publishers.templates", "telegram_recap.tmpl.j2"
)
default_recap_header_template_path = pkg_resources.resource_filename(
"mobilizon_reshare.publishers.templates", "telegram_recap_header.tmpl.j2"
)
_conf = ("publisher", "telegram")
@staticmethod

View File

@ -25,6 +25,10 @@ class TwitterFormatter(AbstractEventFormatter):
"mobilizon_reshare.publishers.templates", "twitter_recap.tmpl.j2"
)
default_recap_header_template_path = pkg_resources.resource_filename(
"mobilizon_reshare.publishers.templates", "twitter_recap_header.tmpl.j2"
)
def validate_event(self, event: MobilizonEvent) -> None:
text = event.description
if not (text and text.strip()):

View File

@ -29,6 +29,10 @@ class ZulipFormatter(AbstractEventFormatter):
"mobilizon_reshare.publishers.templates", "zulip_recap.tmpl.j2"
)
default_recap_header_template_path = pkg_resources.resource_filename(
"mobilizon_reshare.publishers.templates", "zulip_recap_header.tmpl.j2"
)
def validate_event(self, event: MobilizonEvent) -> None:
text = event.description
if not (text and text.strip()):

View File

@ -0,0 +1 @@
Upcoming events

View File

@ -0,0 +1 @@
Upcoming events

View File

@ -0,0 +1 @@
Upcoming events

View File

@ -1,3 +1,4 @@
from collections import UserList
from datetime import timedelta
from uuid import UUID
@ -45,9 +46,21 @@ def mock_formatter_valid():
def get_recap_fragment(self, event):
return event.name
def get_recap_header(self):
return "Upcoming"
return MockFormatter()
@pytest.fixture()
def message_collector():
class MessageCollector(UserList):
def collect_message(self, message):
self.append(message)
return MessageCollector()
@pytest.fixture
def mock_formatter_invalid():
class MockFormatter(AbstractEventFormatter):
@ -64,10 +77,10 @@ def mock_formatter_invalid():
@pytest.fixture
def mock_publisher_valid():
def mock_publisher_valid(message_collector):
class MockPublisher(AbstractPlatform):
def _send(self, message):
pass
message_collector.collect_message(message)
def _validate_response(self, response):
pass
@ -79,10 +92,11 @@ def mock_publisher_valid():
@pytest.fixture
def mock_publisher_invalid():
def mock_publisher_invalid(message_collector):
class MockPublisher(AbstractPlatform):
def _send(self, message):
pass
message_collector.collect_message(message)
def _validate_response(self, response):
return InvalidResponse("error")
@ -94,10 +108,10 @@ def mock_publisher_invalid():
@pytest.fixture
def mock_publisher_invalid_response():
def mock_publisher_invalid_response(message_collector):
class MockPublisher(AbstractPlatform):
def _send(self, message):
pass
message_collector.collect_message(message)
def _validate_response(self, response):
raise InvalidResponse("Invalid response")

View File

@ -155,8 +155,17 @@ async def test_notifier_coordinator_publication_failed(mock_publisher_valid):
@pytest.mark.parametrize("num_publications", [2])
@pytest.mark.asyncio
async def test_recap_coordinator_run_success(mock_recap_publications,):
async def test_recap_coordinator_run_success(
mock_recap_publications, message_collector
):
coordinator = RecapCoordinator(recap_publications=mock_recap_publications)
report = coordinator.run()
# one recap per publication
assert len(message_collector) == 2
# check that header is in all messages
assert all(("Upcoming" in message) for message in message_collector)
assert len(report.reports) == 2
assert report.successful, "\n".join(map(lambda rep: rep.reason, report.reports))