Merge pull request #109 from Tech-Workers-Coalition-Italia/inspect

cli: inspect: Refactor command.
This commit is contained in:
Simone Robutti 2021-12-11 11:17:59 +01:00 committed by GitHub
commit 94904a8917
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 160 additions and 50 deletions

View File

@ -1,5 +1,4 @@
import functools import functools
from enum import Enum
import click import click
from arrow import Arrow from arrow import Arrow
@ -8,98 +7,156 @@ from click import pass_context
from mobilizon_reshare.cli import safe_execution from mobilizon_reshare.cli import safe_execution
from mobilizon_reshare.cli.commands.format.format import format_event 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_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.start.main import main as start_main
from mobilizon_reshare.cli.commands.recap.main import main as recap_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.config.publishers import publisher_names
from mobilizon_reshare.event.event import EventPublicationStatus from mobilizon_reshare.event.event import EventPublicationStatus
from mobilizon_reshare.models.publication import PublicationStatus
status_name_to_enum = { status_name_to_enum = {
"waiting": EventPublicationStatus.WAITING, "event": {
"completed": EventPublicationStatus.COMPLETED, "waiting": EventPublicationStatus.WAITING,
"failed": EventPublicationStatus.FAILED, "completed": EventPublicationStatus.COMPLETED,
"partial": EventPublicationStatus.PARTIAL, "failed": EventPublicationStatus.FAILED,
"all": None, "partial": EventPublicationStatus.PARTIAL,
"all": None,
},
"publication": {
"completed": PublicationStatus.COMPLETED,
"failed": PublicationStatus.FAILED,
"all": None,
},
} }
settings_file_option = click.option( settings_file_option = click.option(
"-f",
"--settings-file", "--settings-file",
type=click.Path(exists=True), type=click.Path(exists=True),
help="The path for the settings file. " help="The path for the settings file. "
"Overrides the one specified in the environment variables.", "Overrides the one specified in the environment variables.",
) )
from_date_option = click.option( from_date_option = click.option(
"-b",
"--begin", "--begin",
type=click.DateTime(), type=click.DateTime(),
expose_value=True, expose_value=True,
help="Include only events that begin after this datetime", help="Include only events that begin after this datetime.",
) )
to_date_option = click.option( to_date_option = click.option(
"-e",
"--end", "--end",
type=click.DateTime(), type=click.DateTime(),
expose_value=True, expose_value=True,
help="Include only events that begin before this datetime", help="Include only events that begin before this datetime.",
)
event_status_option = click.option(
"-s",
"--status",
type=click.Choice(list(status_name_to_enum["event"].keys())),
default="all",
expose_value=True,
help="Include only events with the given STATUS.",
)
publication_status_option = click.option(
"-s",
"--status",
type=click.Choice(list(status_name_to_enum["publication"].keys())),
default="all",
expose_value=True,
help="Include only publications with the given STATUS.",
) )
class InspectTarget(Enum): def print_version(ctx, param, value):
ALL = "all" if not value or ctx.resilient_parsing:
WAITING = "waiting" return
click.echo(current_version())
def __str__(self): ctx.exit()
return self.value
@click.group() @click.group()
@click.option(
"--version", is_flag=True, callback=print_version, expose_value=False, is_eager=True
)
@pass_context
def mobilizon_reshare(): def mobilizon_reshare():
pass pass
@mobilizon_reshare.command(help="Synchronize and publish events") @mobilizon_reshare.command(help="Synchronize and publish events.")
@settings_file_option @settings_file_option
def start(settings_file): def start(ctx, settings):
safe_execution(start_main, settings_file=settings_file) ctx.ensure_object(dict)
safe_execution(start_main, settings_file=settings)
@mobilizon_reshare.command(help="Publish a recap of already published events") @mobilizon_reshare.command(help="Publish a recap of already published events.")
@settings_file_option @settings_file_option
def recap(settings_file): def recap(settings):
safe_execution(recap_main, settings_file=settings_file) safe_execution(recap_main, settings_file=settings)
@mobilizon_reshare.command(help="Print events in the database that are in STATUS") @mobilizon_reshare.group(help="List objects in the database with different criteria.")
@from_date_option @from_date_option
@to_date_option @to_date_option
@click.argument( @pass_context
"status", type=click.Choice(list(status_name_to_enum.keys())), def inspect(ctx, begin, end):
) ctx.ensure_object(dict)
ctx.obj["begin"] = Arrow.fromdatetime(begin) if begin else None
ctx.obj["end"] = Arrow.fromdatetime(end) if end else None
@inspect.command(help="Query for events in the database.")
@event_status_option
@settings_file_option @settings_file_option
@pass_context @pass_context
def inspect(ctx, status, begin, end, settings_file): def event(ctx, status, settings):
ctx.ensure_object(dict) ctx.ensure_object(dict)
begin = Arrow.fromdatetime(begin) if begin else None
end = Arrow.fromdatetime(end) if end else None
safe_execution( safe_execution(
functools.partial( functools.partial(
inspect_events, status_name_to_enum[status], frm=begin, to=end, inspect_events,
status_name_to_enum["event"][status],
frm=ctx.obj["begin"],
to=ctx.obj["end"],
), ),
settings_file, settings,
)
@inspect.command(help="Query for publications in the database.")
@publication_status_option
@settings_file_option
@pass_context
def publication(ctx, status, settings):
ctx.ensure_object(dict)
safe_execution(
functools.partial(
inspect_publications,
status_name_to_enum["publication"][status],
frm=ctx.obj["begin"],
to=ctx.obj["end"],
),
settings,
) )
@mobilizon_reshare.command( @mobilizon_reshare.command(
help="Format and print event with mobilizon id EVENT-ID using the publisher's format named" help="Format and print event with EVENT-ID using the publisher's format named "
"PUBLISHER" "PUBLISHER."
) )
@settings_file_option
@click.argument("event-id", type=click.UUID) @click.argument("event-id", type=click.UUID)
@click.argument("publisher", type=click.Choice(publisher_names)) @click.argument("publisher", type=click.Choice(publisher_names))
def format(settings_file, event_id, publisher): @settings_file_option
def format(event_id, publisher, settings):
safe_execution( safe_execution(
functools.partial(format_event, event_id, publisher), settings_file, functools.partial(format_event, event_id, publisher), settings,
) )
if __name__ == "__main__": if __name__ == "__main__":
mobilizon_reshare() mobilizon_reshare(obj={})

View File

@ -27,8 +27,8 @@ def show_events(events: Iterable[MobilizonEvent]):
def pretty(event: MobilizonEvent): def pretty(event: MobilizonEvent):
return ( return (
f"{event.name}|{click.style(event.status.name, fg=status_to_color[event.status])}" f"{event.name : ^40}{click.style(event.status.name, fg=status_to_color[event.status]) : ^22}"
f"|{event.mobilizon_id}|{event.begin_datetime.isoformat()}->{event.end_datetime.isoformat()}" 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) id = fields.UUIDField(pk=True)
status = fields.IntEnumField(PublicationStatus) status = fields.IntEnumField(PublicationStatus)
# When a Publication's status is WAITING timestamp = fields.DatetimeField()
# we don't need a timestamp nor a reason
timestamp = fields.DatetimeField(null=True)
reason = fields.TextField(null=True) reason = fields.TextField(null=True)
event = fields.ForeignKeyField("models.Event", related_name="publications") 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( async def get_all_events(
from_date: Optional[Arrow] = None, to_date: Optional[Arrow] = None, from_date: Optional[Arrow] = None, to_date: Optional[Arrow] = None,
) -> Iterable[MobilizonEvent]: ) -> 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( def _add_date_window(
query, query,
field_name: str, field_name: str,
@ -93,7 +109,7 @@ async def publications_with_status(
event_mobilizon_id: Optional[UUID] = None, event_mobilizon_id: Optional[UUID] = None,
from_date: Optional[Arrow] = None, from_date: Optional[Arrow] = None,
to_date: Optional[Arrow] = None, to_date: Optional[Arrow] = None,
) -> dict[UUID, Publication]: ) -> Iterable[Publication]:
query = Publication.filter(status=status) query = Publication.filter(status=status)
if event_mobilizon_id: if event_mobilizon_id:
@ -101,12 +117,9 @@ async def publications_with_status(
event__mobilizon_id=event_mobilizon_id event__mobilizon_id=event_mobilizon_id
) )
query = _add_date_window(query, "timestamp", from_date, to_date) return await prefetch_publication_relations(
_add_date_window(query, "timestamp", from_date, to_date)
publications_list = (
await query.prefetch_related("publisher").order_by("timestamp").distinct()
) )
return {pub.id: pub for pub in publications_list}
async def events_without_publications( async def events_without_publications(

View File

@ -1,4 +1,5 @@
import logging import logging
from datetime import timedelta
from uuid import UUID from uuid import UUID
import pytest import pytest
@ -18,6 +19,7 @@ from mobilizon_reshare.publishers.coordinator import (
PublicationFailureNotifiersCoordinator, PublicationFailureNotifiersCoordinator,
RecapCoordinator, RecapCoordinator,
) )
from tests import today
@pytest.fixture() @pytest.fixture()
@ -91,7 +93,7 @@ async def mock_publications(
id=UUID(int=i + 1), id=UUID(int=i + 1),
event=event, event=event,
publisher=publisher, publisher=publisher,
timestamp=None, timestamp=today + timedelta(hours=i),
reason=None, reason=None,
) )
publication = EventPublication.from_orm(publication, test_event) publication = EventPublication.from_orm(publication, test_event)

View File

@ -5,7 +5,6 @@ import arrow
import pytest import pytest
from mobilizon_reshare.event.event import MobilizonEvent, EventPublicationStatus from mobilizon_reshare.event.event import MobilizonEvent, EventPublicationStatus
from mobilizon_reshare.models.event import Event
from mobilizon_reshare.models.publication import PublicationStatus from mobilizon_reshare.models.publication import PublicationStatus
from mobilizon_reshare.storage.query.read import ( from mobilizon_reshare.storage.query.read import (
get_published_events, get_published_events,
@ -83,7 +82,7 @@ async def test_publications_with_status(
to_date=to_date, to_date=to_date,
) )
assert publications == {pub.id: pub for pub in expected_result} assert publications == expected_result
@pytest.mark.asyncio @pytest.mark.asyncio