diff --git a/mobilizon_reshare/cli/cli.py b/mobilizon_reshare/cli/cli.py index 2c4c8b2..5deac7e 100644 --- a/mobilizon_reshare/cli/cli.py +++ b/mobilizon_reshare/cli/cli.py @@ -1,26 +1,35 @@ import functools -from enum import Enum import click from arrow import Arrow -from click import pass_context from mobilizon_reshare.cli import safe_execution from mobilizon_reshare.cli.commands.format.format import format_event from mobilizon_reshare.cli.commands.inspect.inspect_event import inspect_events +from mobilizon_reshare.cli.commands.inspect.inspect_publication import ( + inspect_publications, +) from mobilizon_reshare.cli.commands.start.main import main as start_main from mobilizon_reshare.cli.commands.recap.main import main as recap_main from mobilizon_reshare.config.config import current_version from mobilizon_reshare.config.publishers import publisher_names from mobilizon_reshare.event.event import EventPublicationStatus +from mobilizon_reshare.models.publication import PublicationStatus status_name_to_enum = { - "waiting": EventPublicationStatus.WAITING, - "completed": EventPublicationStatus.COMPLETED, - "failed": EventPublicationStatus.FAILED, - "partial": EventPublicationStatus.PARTIAL, - "all": None, + "event": { + "waiting": EventPublicationStatus.WAITING, + "completed": EventPublicationStatus.COMPLETED, + "failed": EventPublicationStatus.FAILED, + "partial": EventPublicationStatus.PARTIAL, + "all": None, + }, + "publication": { + "completed": PublicationStatus.COMPLETED, + "failed": PublicationStatus.FAILED, + "all": None, + }, } settings_file_option = click.option( @@ -41,6 +50,13 @@ to_date_option = click.option( expose_value=True, help="Include only events that begin before this datetime", ) +status_option = click.option( + "-s", + "--status", + default="all", + expose_value=True, + help="Include only objects with the given status", +) def print_version(ctx, param, value): @@ -50,14 +66,6 @@ def print_version(ctx, param, value): ctx.exit() -class InspectTarget(Enum): - ALL = "all" - WAITING = "waiting" - - def __str__(self): - return self.value - - @click.group() @click.option( "--version", is_flag=True, callback=print_version, expose_value=False, is_eager=True @@ -78,22 +86,25 @@ def recap(settings_file): safe_execution(recap_main, settings_file=settings_file) -@mobilizon_reshare.command(help="Print events in the database that are in STATUS") +@mobilizon_reshare.command(help="List objects in the database with different criteria") @from_date_option @to_date_option @click.argument( - "status", type=click.Choice(list(status_name_to_enum.keys())), + "object", + type=click.Choice(list(status_name_to_enum.keys())), ) @settings_file_option -@pass_context -def inspect(ctx, status, begin, end, settings_file): - ctx.ensure_object(dict) +@status_option +def inspect(object, begin, end, settings_file, status): begin = Arrow.fromdatetime(begin) if begin else None end = Arrow.fromdatetime(end) if end else None safe_execution( functools.partial( - inspect_events, status_name_to_enum[status], frm=begin, to=end, + inspect_events if object == "event" else inspect_publications, + status_name_to_enum[object][status], + frm=begin, + to=end, ), settings_file, ) diff --git a/mobilizon_reshare/cli/commands/inspect/inspect_event.py b/mobilizon_reshare/cli/commands/inspect/inspect_event.py index 838b760..43b0643 100644 --- a/mobilizon_reshare/cli/commands/inspect/inspect_event.py +++ b/mobilizon_reshare/cli/commands/inspect/inspect_event.py @@ -27,8 +27,8 @@ def show_events(events: Iterable[MobilizonEvent]): def pretty(event: MobilizonEvent): return ( - f"{event.name}\t{click.style(event.status.name, fg=status_to_color[event.status])}" - f"\t{event.mobilizon_id}\t{event.begin_datetime.isoformat()}->{event.end_datetime.isoformat()}" + f"{event.name : ^40}{click.style(event.status.name, fg=status_to_color[event.status]) : ^22}" + f"{str(event.mobilizon_id) : <40}{event.begin_datetime.isoformat() : <29}{event.end_datetime.isoformat()}" ) diff --git a/mobilizon_reshare/cli/commands/inspect/inspect_publication.py b/mobilizon_reshare/cli/commands/inspect/inspect_publication.py new file mode 100644 index 0000000..4a77770 --- /dev/null +++ b/mobilizon_reshare/cli/commands/inspect/inspect_publication.py @@ -0,0 +1,41 @@ +from typing import Iterable + +import click +from arrow import Arrow + +from mobilizon_reshare.models.publication import Publication, PublicationStatus +from mobilizon_reshare.storage.query.read import ( + get_all_publications, + publications_with_status, +) + +status_to_color = { + PublicationStatus.COMPLETED: "green", + PublicationStatus.FAILED: "red", +} + + +def show_publications(publications: Iterable[Publication]): + click.echo_via_pager("\n".join(map(pretty, publications))) + + +def pretty(publication: Publication): + return ( + f"{str(publication.id) : <40}{publication.timestamp.isoformat() : <36}" + f"{click.style(publication.status.name, fg=status_to_color[publication.status]) : <22}" + f"{publication.publisher.name : <12}{str(publication.event.id)}" + ) + + +async def inspect_publications( + status: PublicationStatus = None, frm: Arrow = None, to: Arrow = None +): + if status is None: + publications = await get_all_publications(from_date=frm, to_date=to) + else: + publications = await publications_with_status(status, from_date=frm, to_date=to) + + if publications: + show_publications(list(publications)) + else: + click.echo(f"No publication found with status: {status}") diff --git a/mobilizon_reshare/models/publication.py b/mobilizon_reshare/models/publication.py index 927146e..bf954ec 100644 --- a/mobilizon_reshare/models/publication.py +++ b/mobilizon_reshare/models/publication.py @@ -13,9 +13,7 @@ class Publication(Model): id = fields.UUIDField(pk=True) status = fields.IntEnumField(PublicationStatus) - # When a Publication's status is WAITING - # we don't need a timestamp nor a reason - timestamp = fields.DatetimeField(null=True) + timestamp = fields.DatetimeField() reason = fields.TextField(null=True) event = fields.ForeignKeyField("models.Event", related_name="publications") diff --git a/mobilizon_reshare/storage/query/read.py b/mobilizon_reshare/storage/query/read.py index 87baa20..2991d60 100644 --- a/mobilizon_reshare/storage/query/read.py +++ b/mobilizon_reshare/storage/query/read.py @@ -55,6 +55,14 @@ async def events_with_status( ) +async def get_all_publications( + from_date: Optional[Arrow] = None, to_date: Optional[Arrow] = None, +) -> Iterable[Publication]: + return await prefetch_publication_relations( + _add_date_window(Publication.all(), "timestamp", from_date, to_date) + ) + + async def get_all_events( from_date: Optional[Arrow] = None, to_date: Optional[Arrow] = None, ) -> Iterable[MobilizonEvent]: @@ -74,6 +82,14 @@ async def prefetch_event_relations(queryset: QuerySet[Event]) -> list[Event]: ) +async def prefetch_publication_relations(queryset: QuerySet[Publication]) -> list[Publication]: + return ( + await queryset.prefetch_related("publisher", "event") + .order_by("timestamp") + .distinct() + ) + + def _add_date_window( query, field_name: str, @@ -93,7 +109,7 @@ async def publications_with_status( event_mobilizon_id: Optional[UUID] = None, from_date: Optional[Arrow] = None, to_date: Optional[Arrow] = None, -) -> Publication: +) -> Iterable[Publication]: query = Publication.filter(status=status) if event_mobilizon_id: @@ -101,9 +117,9 @@ async def publications_with_status( event__mobilizon_id=event_mobilizon_id ) - query = _add_date_window(query, "timestamp", from_date, to_date) - - return await query.prefetch_related("publisher").order_by("timestamp").distinct() + return await prefetch_publication_relations( + _add_date_window(query, "timestamp", from_date, to_date) + ) async def events_without_publications( diff --git a/tests/publishers/test_coordinator.py b/tests/publishers/test_coordinator.py index ceaf1ae..cb59e50 100644 --- a/tests/publishers/test_coordinator.py +++ b/tests/publishers/test_coordinator.py @@ -1,4 +1,5 @@ import logging +from datetime import timedelta from uuid import UUID import pytest @@ -18,6 +19,7 @@ from mobilizon_reshare.publishers.coordinator import ( PublicationFailureNotifiersCoordinator, RecapCoordinator, ) +from tests import today @pytest.fixture() @@ -91,7 +93,7 @@ async def mock_publications( id=UUID(int=i + 1), event=event, publisher=publisher, - timestamp=None, + timestamp=today + timedelta(hours=i), reason=None, ) publication = EventPublication.from_orm(publication, test_event)