cli: inspect: Refactor command.

Now the inspect command can display informations about
different kind of objects including events and publications.

This patch also changes the cli to output columnar values
suitable for further processing with standard Unix
tool, such as awk.

$ mobilizon-reshare.sh inspect publication -s completed | awk '{ print  }' | sort | uniq -c
[2021-12-01 01:05:55,321] [20] [INFO] Tortoise-ORM shutdown
      2 mastodon
      2 telegram
      2 zulip
This commit is contained in:
Giacomo Leidi 2021-12-01 01:08:37 +01:00
parent 2d8855f6fe
commit 6f8f96d5b6
6 changed files with 99 additions and 31 deletions

View File

@ -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,
)

View File

@ -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()}"
)

View File

@ -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}")

View File

@ -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")

View File

@ -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(

View File

@ -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)