From f3439ad30dede767565cc46965d43eedaa2d15cc Mon Sep 17 00:00:00 2001 From: Ivan Habunek Date: Mon, 26 Jun 2023 16:02:21 +0200 Subject: [PATCH] Use entitites to simpliy print functions --- tests/test_console.py | 7 +-- toot/commands.py | 9 +++- toot/entities.py | 2 +- toot/output.py | 104 +++++++++++++++++++++--------------------- 4 files changed, 64 insertions(+), 58 deletions(-) diff --git a/tests/test_console.py b/tests/test_console.py index ffe1d12..9f3b835 100644 --- a/tests/test_console.py +++ b/tests/test_console.py @@ -161,6 +161,7 @@ def test_timeline_with_re(mock_get, monkeypatch, capsys): 'acct': 'fz' }, 'reblog': { + 'created_at': '2017-04-12T15:53:18.174Z', 'account': { 'display_name': 'Johnny Cash', 'acct': 'jc' @@ -179,8 +180,8 @@ def test_timeline_with_re(mock_get, monkeypatch, capsys): out, err = capsys.readouterr() lines = uncolorize(out).split("\n") - assert "Frank Zappa" in lines[1] - assert "@fz" in lines[1] + assert "Johnny Cash" in lines[1] + assert "@jc" in lines[1] assert "2017-04-12 15:53 UTC" in lines[1] assert ( @@ -188,7 +189,7 @@ def test_timeline_with_re(mock_get, monkeypatch, capsys): "exact mathematical design, but\nwhat's missing is the eyebrows." in out) assert "111111111111111111" in lines[-3] - assert "↻ Reblogged @jc" in lines[-3] + assert "↻ @fz boosted" in lines[-3] assert err == "" diff --git a/toot/commands.py b/toot/commands.py index dbb5eb6..5dad7e8 100644 --- a/toot/commands.py +++ b/toot/commands.py @@ -6,6 +6,7 @@ from datetime import datetime, timedelta, timezone from time import sleep, time from toot import api, config, __version__ from toot.auth import login_interactive, login_browser_interactive, create_app_interactive +from toot.entities import Instance, Notification, Status, from_dict from toot.exceptions import ApiError, ConsoleError from toot.output import (print_lists, print_out, print_instance, print_account, print_acct_list, print_search_results, print_timeline, print_notifications, print_tag_list, @@ -56,7 +57,8 @@ def timeline(app, user, args, generator=None): if args.reverse: items = reversed(items) - print_timeline(items) + statuses = [from_dict(Status, item) for item in items] + print_timeline(statuses) if args.once or not sys.stdout.isatty(): break @@ -78,7 +80,8 @@ def thread(app, user, args): for item in context['descendants']: thread.append(item) - print_timeline(thread) + statuses = [from_dict(Status, s) for s in thread] + print_timeline(statuses) def post(app, user, args): @@ -515,6 +518,7 @@ def instance(app, user, args): try: instance = api.get_instance(base_url) + instance = from_dict(Instance, instance) print_instance(instance) except ApiError: raise ConsoleError( @@ -542,6 +546,7 @@ def notifications(app, user, args): if args.reverse: notifications = reversed(notifications) + notifications = [from_dict(Notification, n) for n in notifications] print_notifications(notifications) diff --git a/toot/entities.py b/toot/entities.py index ae51652..d812953 100644 --- a/toot/entities.py +++ b/toot/entities.py @@ -352,7 +352,7 @@ class Instance: approval_required: bool invites_enabled: bool configuration: InstanceConfiguration - contact_account: Account + contact_account: Optional[Account] rules: List[Rule] diff --git a/toot/output.py b/toot/output.py index 3414cdb..b533244 100644 --- a/toot/output.py +++ b/toot/output.py @@ -3,12 +3,11 @@ import re import sys import textwrap -from typing import List -from wcwidth import wcswidth - -from toot.tui.utils import parse_datetime +from toot.entities import Instance, Notification, Poll, Status from toot.utils import get_text, parse_html from toot.wcstring import wc_wrap +from typing import List +from wcwidth import wcswidth STYLES = { @@ -136,25 +135,23 @@ def print_err(*args, **kwargs): print(*args, file=sys.stderr, **kwargs) -def print_instance(instance): - print_out(f"{instance['title']}") - print_out(f"{instance['uri']}") - print_out(f"running Mastodon {instance['version']}") +def print_instance(instance: Instance): + print_out(f"{instance.title}") + print_out(f"{instance.uri}") + print_out(f"running Mastodon {instance.version}") print_out() - description = instance.get("description") - if description: - for paragraph in re.split(r"[\r\n]+", description.strip()): + if instance.description: + for paragraph in re.split(r"[\r\n]+", instance.description.strip()): paragraph = get_text(paragraph) print_out(textwrap.fill(paragraph, width=80)) print_out() - rules = instance.get("rules") - if rules: + if instance.rules: print_out("Rules:") - for ordinal, rule in enumerate(rules): + for ordinal, rule in enumerate(instance.rules): ordinal = f"{ordinal + 1}." - lines = textwrap.wrap(rule["text"], 80 - len(ordinal)) + lines = textwrap.wrap(rule.text, 80 - len(ordinal)) first = True for line in lines: if first: @@ -162,6 +159,11 @@ def print_instance(instance): first = False else: print_out(f"{' ' * len(ordinal)} {line}") + print_out() + + contact = instance.contact_account + if contact: + print_out(f"Contact: {contact.display_name} @{contact.acct}") def print_account(account): @@ -269,20 +271,18 @@ def print_search_results(results): print_out("Nothing found") -def print_status(status, width): - reblog = status['reblog'] - content = reblog['content'] if reblog else status['content'] - media_attachments = reblog['media_attachments'] if reblog else status['media_attachments'] - in_reply_to = status['in_reply_to_id'] - poll = reblog.get('poll') if reblog else status.get('poll') +def print_status(status: Status, width: int): + status_id = status.id + in_reply_to_id = status.in_reply_to_id + reblogged_by = status.account if status.reblog else None - time = parse_datetime(status['created_at']) - time = time.strftime('%Y-%m-%d %H:%M %Z') + status = status.original - username = "@" + status['account']['acct'] + time = status.created_at.strftime('%Y-%m-%d %H:%M %Z') + username = "@" + status.account.acct spacing = width - wcswidth(username) - wcswidth(time) - 2 - display_name = status['account']['display_name'] + display_name = status.account.display_name if display_name: spacing -= wcswidth(display_name) + 1 @@ -294,23 +294,24 @@ def print_status(status, width): ) print_out("") - print_html(content, width) + print_html(status.content, width) - if media_attachments: + if status.media_attachments: print_out("\nMedia:") - for attachment in media_attachments: - url = attachment["url"] + for attachment in status.media_attachments: + url = attachment.url for line in wc_wrap(url, width): print_out(line) - if poll: - print_poll(poll) + if status.poll: + print_poll(status.poll) print_out() + print_out( - f"ID {status['id']} ", - f"↲ In reply to {in_reply_to} " if in_reply_to else "", - f"↻ Reblogged @{reblog['account']['acct']} " if reblog else "", + f"ID {status_id} ", + f"↲ In reply to {in_reply_to_id} " if in_reply_to_id else "", + f"↻ @{reblogged_by.acct} boosted " if reblogged_by else "", ) @@ -325,33 +326,33 @@ def print_html(text, width=80): first = False -def print_poll(poll): +def print_poll(poll: Poll): print_out() - for idx, option in enumerate(poll["options"]): - perc = (round(100 * option["votes_count"] / poll["votes_count"]) - if poll["votes_count"] else 0) + for idx, option in enumerate(poll.options): + perc = (round(100 * option.votes_count / poll.votes_count) + if poll.votes_count and option.votes_count is not None else 0) - if poll["voted"] and poll["own_votes"] and idx in poll["own_votes"]: + if poll.voted and poll.own_votes and idx in poll.own_votes: voted_for = " " else: voted_for = "" - print_out(f'{option["title"]} - {perc}% {voted_for}') + print_out(f'{option.title} - {perc}% {voted_for}') - poll_footer = f'Poll · {poll["votes_count"]} votes' + poll_footer = f'Poll · {poll.votes_count} votes' - if poll["expired"]: + if poll.expired: poll_footer += " · Closed" - if poll["expires_at"]: - expires_at = parse_datetime(poll["expires_at"]).strftime("%Y-%m-%d %H:%M") + if poll.expires_at: + expires_at = poll.expires_at.strftime("%Y-%m-%d %H:%M") poll_footer += f" · Closes on {expires_at}" print_out() print_out(poll_footer) -def print_timeline(items, width=100): +def print_timeline(items: list[Status], width=100): print_out("─" * width) for item in items: print_status(item, width) @@ -366,20 +367,19 @@ notification_msgs = { } -def print_notification(notification, width=100): - account = "{display_name} @{acct}".format(**notification["account"]) - msg = notification_msgs.get(notification["type"]) +def print_notification(notification: Notification, width=100): + account = f"{notification.account.display_name} @{notification.account.acct}" + msg = notification_msgs.get(notification.type) if msg is None: return print_out("─" * width) print_out(msg.format(account=account)) - status = notification.get("status") - if status is not None: - print_status(status, width) + if notification.status: + print_status(notification.status, width) -def print_notifications(notifications, width=100): +def print_notifications(notifications: list[Notification], width=100): for notification in notifications: print_notification(notification) print_out("─" * width)