2021-08-27 23:45:24 +02:00
|
|
|
import logging
|
2021-10-16 01:25:45 +02:00
|
|
|
from dataclasses import dataclass
|
|
|
|
from typing import List, Optional
|
2021-07-07 11:45:54 +02:00
|
|
|
|
2021-08-16 10:49:52 +02:00
|
|
|
from mobilizon_reshare.models.publication import PublicationStatus
|
2021-10-02 18:09:03 +02:00
|
|
|
from mobilizon_reshare.publishers import get_active_notifiers
|
2021-10-16 01:25:45 +02:00
|
|
|
from mobilizon_reshare.publishers.abstract import (
|
|
|
|
EventPublication,
|
|
|
|
AbstractPlatform,
|
|
|
|
RecapPublication,
|
|
|
|
)
|
2021-08-16 10:49:52 +02:00
|
|
|
from mobilizon_reshare.publishers.exceptions import PublisherError
|
2021-10-02 18:09:03 +02:00
|
|
|
from mobilizon_reshare.publishers.platforms.platform_mapping import get_notifier_class
|
2021-07-07 11:45:54 +02:00
|
|
|
|
2021-08-27 23:45:24 +02:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2021-07-07 11:45:54 +02:00
|
|
|
|
|
|
|
@dataclass
|
2021-10-16 01:25:45 +02:00
|
|
|
class BasePublicationReport:
|
2021-08-05 00:29:50 +02:00
|
|
|
status: PublicationStatus
|
2021-10-16 01:25:45 +02:00
|
|
|
reason: Optional[str]
|
|
|
|
|
|
|
|
def get_failure_message(self):
|
|
|
|
|
|
|
|
return (
|
|
|
|
f"Publication failed with status: {self.status}.\n" f"Reason: {self.reason}"
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class EventPublicationReport(BasePublicationReport):
|
2021-10-24 21:32:28 +02:00
|
|
|
publication: EventPublication
|
2021-07-07 11:45:54 +02:00
|
|
|
|
2021-10-16 01:25:45 +02:00
|
|
|
def get_failure_message(self):
|
|
|
|
|
2021-10-24 21:32:28 +02:00
|
|
|
if not self.reason:
|
|
|
|
logger.error("Report of failure without reason.", exc_info=True)
|
|
|
|
|
2021-10-16 01:25:45 +02:00
|
|
|
return (
|
2021-10-24 21:32:28 +02:00
|
|
|
f"Publication {self.publication.id} failed with status: {self.status}.\n"
|
|
|
|
f"Reason: {self.reason}\n"
|
|
|
|
f"Publisher: {self.publication.publisher.name}"
|
2021-10-16 01:25:45 +02:00
|
|
|
)
|
|
|
|
|
2021-07-07 11:45:54 +02:00
|
|
|
|
|
|
|
@dataclass
|
2021-10-16 01:25:45 +02:00
|
|
|
class BaseCoordinatorReport:
|
|
|
|
reports: List[BasePublicationReport]
|
2021-07-07 11:45:54 +02:00
|
|
|
|
|
|
|
@property
|
|
|
|
def successful(self):
|
2021-10-16 01:25:45 +02:00
|
|
|
return all(r.status == PublicationStatus.COMPLETED for r in self.reports)
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class PublisherCoordinatorReport(BaseCoordinatorReport):
|
|
|
|
|
|
|
|
reports: List[EventPublicationReport]
|
|
|
|
publications: List[EventPublication]
|
2021-07-07 11:45:54 +02:00
|
|
|
|
|
|
|
|
2021-10-02 18:09:03 +02:00
|
|
|
class PublisherCoordinator:
|
|
|
|
def __init__(self, publications: List[EventPublication]):
|
|
|
|
self.publications = publications
|
2021-07-07 11:45:54 +02:00
|
|
|
|
|
|
|
def run(self) -> PublisherCoordinatorReport:
|
2021-08-05 00:29:50 +02:00
|
|
|
errors = self._validate()
|
2021-07-12 22:17:49 +02:00
|
|
|
if errors:
|
2021-08-27 23:45:24 +02:00
|
|
|
return PublisherCoordinatorReport(
|
2021-10-02 18:09:03 +02:00
|
|
|
reports=errors, publications=self.publications
|
2021-08-27 23:45:24 +02:00
|
|
|
)
|
2021-07-07 11:45:54 +02:00
|
|
|
|
2021-07-12 22:17:49 +02:00
|
|
|
return self._post()
|
2021-07-07 11:45:54 +02:00
|
|
|
|
2021-08-28 13:17:39 +02:00
|
|
|
def _make_successful_report(self, failed_ids):
|
2021-10-16 01:25:45 +02:00
|
|
|
return [
|
|
|
|
EventPublicationReport(
|
2021-10-24 21:32:28 +02:00
|
|
|
status=PublicationStatus.COMPLETED, reason="", publication=publication,
|
2021-07-15 18:13:11 +02:00
|
|
|
)
|
2021-10-02 18:09:03 +02:00
|
|
|
for publication in self.publications
|
|
|
|
if publication.id not in failed_ids
|
2021-10-16 01:25:45 +02:00
|
|
|
]
|
2021-07-07 11:45:54 +02:00
|
|
|
|
|
|
|
def _post(self):
|
2021-10-16 01:25:45 +02:00
|
|
|
reports = []
|
2021-10-02 18:09:03 +02:00
|
|
|
|
|
|
|
for publication in self.publications:
|
|
|
|
|
2021-07-07 11:45:54 +02:00
|
|
|
try:
|
2021-10-02 18:09:03 +02:00
|
|
|
message = publication.formatter.get_message_from_event(
|
|
|
|
publication.event
|
|
|
|
)
|
|
|
|
publication.publisher.send(message)
|
2021-10-16 01:25:45 +02:00
|
|
|
reports.append(
|
|
|
|
EventPublicationReport(
|
|
|
|
status=PublicationStatus.COMPLETED,
|
2021-10-24 21:32:28 +02:00
|
|
|
publication=publication,
|
2021-10-16 01:25:45 +02:00
|
|
|
reason=None,
|
|
|
|
)
|
|
|
|
)
|
2021-07-07 11:45:54 +02:00
|
|
|
except PublisherError as e:
|
2021-10-16 01:25:45 +02:00
|
|
|
reports.append(
|
|
|
|
EventPublicationReport(
|
|
|
|
status=PublicationStatus.FAILED,
|
|
|
|
reason=str(e),
|
2021-10-24 21:32:28 +02:00
|
|
|
publication=publication,
|
2021-10-16 01:25:45 +02:00
|
|
|
)
|
2021-07-07 11:45:54 +02:00
|
|
|
)
|
2021-08-27 23:45:24 +02:00
|
|
|
|
|
|
|
return PublisherCoordinatorReport(
|
2021-10-02 18:09:03 +02:00
|
|
|
publications=self.publications, reports=reports
|
2021-08-27 23:45:24 +02:00
|
|
|
)
|
2021-07-07 11:45:54 +02:00
|
|
|
|
2021-10-24 21:32:28 +02:00
|
|
|
def _safe_run(self, reasons, f, *args, **kwargs):
|
|
|
|
try:
|
|
|
|
f(*args, **kwargs)
|
|
|
|
return reasons
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(str(e))
|
|
|
|
return reasons + [str(e)]
|
|
|
|
|
2021-07-07 11:45:54 +02:00
|
|
|
def _validate(self):
|
2021-10-16 01:25:45 +02:00
|
|
|
errors = []
|
2021-10-02 18:09:03 +02:00
|
|
|
|
|
|
|
for publication in self.publications:
|
2021-10-24 21:32:28 +02:00
|
|
|
reasons = []
|
|
|
|
reasons = self._safe_run(
|
|
|
|
reasons, publication.publisher.validate_credentials,
|
|
|
|
)
|
|
|
|
reasons = self._safe_run(
|
|
|
|
reasons, publication.formatter.validate_event, publication.event
|
|
|
|
)
|
|
|
|
reasons = self._safe_run(
|
|
|
|
reasons,
|
|
|
|
publication.formatter.validate_message,
|
|
|
|
publication.formatter.get_message_from_event(publication.event),
|
|
|
|
)
|
2021-10-02 18:09:03 +02:00
|
|
|
|
2021-10-24 21:32:28 +02:00
|
|
|
if len(reasons) > 0:
|
2021-10-16 01:25:45 +02:00
|
|
|
errors.append(
|
|
|
|
EventPublicationReport(
|
|
|
|
status=PublicationStatus.FAILED,
|
2021-10-24 21:32:28 +02:00
|
|
|
reason=", ".join(reasons),
|
|
|
|
publication=publication,
|
2021-10-16 01:25:45 +02:00
|
|
|
)
|
2021-07-07 11:45:54 +02:00
|
|
|
)
|
2021-08-05 00:29:50 +02:00
|
|
|
|
|
|
|
return errors
|
2021-08-27 23:45:24 +02:00
|
|
|
|
|
|
|
|
2021-10-16 01:25:45 +02:00
|
|
|
class AbstractCoordinator:
|
|
|
|
def __init__(self, message: str, platforms: List[AbstractPlatform] = None):
|
2021-10-02 18:09:03 +02:00
|
|
|
self.message = message
|
2021-10-16 01:25:45 +02:00
|
|
|
self.platforms = platforms
|
2021-08-27 23:45:24 +02:00
|
|
|
|
2021-10-02 18:09:03 +02:00
|
|
|
def send_to_all(self):
|
2021-10-16 01:25:45 +02:00
|
|
|
for platform in self.platforms:
|
2021-10-24 21:43:09 +02:00
|
|
|
try:
|
|
|
|
platform.send(self.message)
|
|
|
|
except Exception as e:
|
|
|
|
logger.critical(f"Notifier failed to send message:\n{self.message}")
|
|
|
|
logger.exception(e)
|
2021-10-16 01:25:45 +02:00
|
|
|
|
|
|
|
|
|
|
|
class AbstractNotifiersCoordinator(AbstractCoordinator):
|
|
|
|
def __init__(self, message: str, notifiers: List[AbstractPlatform] = None):
|
|
|
|
platforms = notifiers or [
|
|
|
|
get_notifier_class(notifier)() for notifier in get_active_notifiers()
|
|
|
|
]
|
|
|
|
super(AbstractNotifiersCoordinator, self).__init__(message, platforms)
|
2021-08-27 23:45:24 +02:00
|
|
|
|
|
|
|
|
|
|
|
class PublicationFailureNotifiersCoordinator(AbstractNotifiersCoordinator):
|
2021-10-16 01:25:45 +02:00
|
|
|
def __init__(self, report: EventPublicationReport, platforms=None):
|
2021-10-02 18:09:03 +02:00
|
|
|
self.report = report
|
|
|
|
super(PublicationFailureNotifiersCoordinator, self).__init__(
|
2021-10-16 01:25:45 +02:00
|
|
|
message=report.get_failure_message(), notifiers=platforms
|
2021-08-27 23:45:24 +02:00
|
|
|
)
|
|
|
|
|
2021-10-02 18:09:03 +02:00
|
|
|
def notify_failure(self):
|
2021-10-16 01:25:45 +02:00
|
|
|
logger.info("Sending failure notifications")
|
2021-10-02 18:09:03 +02:00
|
|
|
if self.report.status == PublicationStatus.FAILED:
|
|
|
|
self.send_to_all()
|
2021-10-16 01:25:45 +02:00
|
|
|
|
|
|
|
|
|
|
|
class RecapCoordinator:
|
|
|
|
def __init__(self, recap_publications: List[RecapPublication]):
|
|
|
|
self.recap_publications = recap_publications
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
reports = []
|
|
|
|
for recap_publication in self.recap_publications:
|
|
|
|
try:
|
|
|
|
|
2021-10-17 14:09:24 +02:00
|
|
|
fragments = [recap_publication.formatter.get_recap_header()]
|
2021-10-16 01:25:45 +02:00
|
|
|
for event in recap_publication.events:
|
|
|
|
fragments.append(
|
|
|
|
recap_publication.formatter.get_recap_fragment(event)
|
|
|
|
)
|
|
|
|
message = "\n\n".join(fragments)
|
|
|
|
recap_publication.publisher.send(message)
|
|
|
|
reports.append(
|
|
|
|
BasePublicationReport(
|
|
|
|
status=PublicationStatus.COMPLETED, reason=None,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
except PublisherError as e:
|
|
|
|
reports.append(
|
|
|
|
BasePublicationReport(
|
|
|
|
status=PublicationStatus.FAILED, reason=str(e),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
return BaseCoordinatorReport(reports=reports)
|