mirror of
https://github.com/ihabunek/toot.git
synced 2025-02-02 15:07:51 -05:00
Nicer timeline output
This commit is contained in:
parent
996228d224
commit
e6d585ae5d
@ -128,7 +128,8 @@ def test_timeline(mock_get, monkeypatch, capsys):
|
||||
'created_at': '2017-04-12T15:53:18.174Z',
|
||||
'content': "<p>The computer can't tell you the emotional story. It can give you the exact mathematical design, but what's missing is the eyebrows.</p>",
|
||||
'reblog': None,
|
||||
'in_reply_to_id': None
|
||||
'in_reply_to_id': None,
|
||||
'media_attachments': [],
|
||||
}])
|
||||
|
||||
console.run_command(app, user, 'timeline', ['--once'])
|
||||
@ -136,16 +137,18 @@ def test_timeline(mock_get, monkeypatch, capsys):
|
||||
mock_get.assert_called_once_with(app, user, '/api/v1/timelines/home?limit=10', None)
|
||||
|
||||
out, err = capsys.readouterr()
|
||||
lines = out.split("\n")
|
||||
|
||||
assert "Frank Zappa 🎸" in lines[1]
|
||||
assert "@fz" in lines[1]
|
||||
assert "2017-04-12 15:53" in lines[1]
|
||||
|
||||
assert (
|
||||
"The computer can't tell you the emotional story. It can give you the "
|
||||
"exact mathematical design, but\nwhat's missing is the eyebrows." in out)
|
||||
|
||||
assert "111111111111111111" in lines[-3]
|
||||
|
||||
expected = (
|
||||
"───────────────────────────────┬────────────────────────────────────────────────────────────────────────────────────────\n"
|
||||
"Frank Zappa 🎸 │ The computer can't tell you the emotional story. It can give you the exact\n"
|
||||
"@fz │ mathematical design, but what's missing is the eyebrows.\n"
|
||||
"2017-04-12 15:53 │ \n"
|
||||
"id: 111111111111111111 │ \n"
|
||||
"───────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────\n"
|
||||
)
|
||||
assert out == expected
|
||||
assert err == ""
|
||||
|
||||
|
||||
@ -153,14 +156,21 @@ def test_timeline(mock_get, monkeypatch, capsys):
|
||||
def test_timeline_with_re(mock_get, monkeypatch, capsys):
|
||||
mock_get.return_value = MockResponse([{
|
||||
'id': '111111111111111111',
|
||||
'created_at': '2017-04-12T15:53:18.174Z',
|
||||
'account': {
|
||||
'display_name': 'Frank Zappa',
|
||||
'username': 'fz'
|
||||
},
|
||||
'created_at': '2017-04-12T15:53:18.174Z',
|
||||
'content': "<p>The computer can't tell you the emotional story. It can give you the exact mathematical design, but what's missing is the eyebrows.</p>",
|
||||
'reblog': None,
|
||||
'in_reply_to_id': '111111111111111110'
|
||||
'reblog': {
|
||||
'account': {
|
||||
'display_name': 'Johnny Cash',
|
||||
'username': 'jc'
|
||||
},
|
||||
'content': "<p>The computer can't tell you the emotional story. It can give you the exact mathematical design, but what's missing is the eyebrows.</p>",
|
||||
'media_attachments': [],
|
||||
},
|
||||
'in_reply_to_id': '111111111111111110',
|
||||
'media_attachments': [],
|
||||
}])
|
||||
|
||||
console.run_command(app, user, 'timeline', ['--once'])
|
||||
@ -168,12 +178,21 @@ def test_timeline_with_re(mock_get, monkeypatch, capsys):
|
||||
mock_get.assert_called_once_with(app, user, '/api/v1/timelines/home?limit=10', None)
|
||||
|
||||
out, err = capsys.readouterr()
|
||||
assert "The computer can't tell you the emotional story." in out
|
||||
assert "but what's missing is the eyebrows." in out
|
||||
assert "Frank Zappa" in out
|
||||
assert "@fz" in out
|
||||
assert "id: 111111111111111111" in out
|
||||
assert "[RE]" in out
|
||||
lines = out.split("\n")
|
||||
|
||||
assert "Frank Zappa" in lines[1]
|
||||
assert "@fz" in lines[1]
|
||||
assert "2017-04-12 15:53" in lines[1]
|
||||
|
||||
assert (
|
||||
"The computer can't tell you the emotional story. It can give you the "
|
||||
"exact mathematical design, but\nwhat's missing is the eyebrows." in out)
|
||||
|
||||
assert "111111111111111111" in lines[-3]
|
||||
assert "↻ Reblogged \x1b[34m@jc\x1b[0m" in lines[-3]
|
||||
|
||||
assert err == ""
|
||||
|
||||
|
||||
@mock.patch('toot.http.get')
|
||||
def test_thread(mock_get, monkeypatch, capsys):
|
||||
@ -187,7 +206,8 @@ def test_thread(mock_get, monkeypatch, capsys):
|
||||
'created_at': '2017-04-12T15:53:18.174Z',
|
||||
'content': "my response in the middle",
|
||||
'reblog': None,
|
||||
'in_reply_to_id': '111111111111111110'
|
||||
'in_reply_to_id': '111111111111111110',
|
||||
'media_attachments': [],
|
||||
}),
|
||||
MockResponse({
|
||||
'ancestors': [{
|
||||
@ -198,6 +218,7 @@ def test_thread(mock_get, monkeypatch, capsys):
|
||||
},
|
||||
'created_at': '2017-04-12T15:53:18.174Z',
|
||||
'content': "original content",
|
||||
'media_attachments': [],
|
||||
'reblog': None,
|
||||
'in_reply_to_id': None}],
|
||||
'descendants': [{
|
||||
@ -208,6 +229,7 @@ def test_thread(mock_get, monkeypatch, capsys):
|
||||
},
|
||||
'created_at': '2017-04-12T15:53:18.174Z',
|
||||
'content': "response message",
|
||||
'media_attachments': [],
|
||||
'reblog': None,
|
||||
'in_reply_to_id': '111111111111111111'}],
|
||||
}),
|
||||
@ -223,6 +245,8 @@ def test_thread(mock_get, monkeypatch, capsys):
|
||||
|
||||
out, err = capsys.readouterr()
|
||||
|
||||
assert not err
|
||||
|
||||
# Display order
|
||||
assert out.index('original content') < out.index('my response in the middle')
|
||||
assert out.index('my response in the middle') < out.index('response message')
|
||||
@ -232,8 +256,8 @@ def test_thread(mock_get, monkeypatch, capsys):
|
||||
assert "response message" in out
|
||||
assert "Frank Zappa" in out
|
||||
assert "@fz" in out
|
||||
assert "id: 111111111111111111" in out
|
||||
assert "[RE]" in out
|
||||
assert "111111111111111111" in out
|
||||
assert "In reply to" in out
|
||||
|
||||
@mock.patch('toot.http.post')
|
||||
def test_upload(mock_post, capsys):
|
||||
|
@ -3,14 +3,12 @@
|
||||
import sys
|
||||
import re
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
from datetime import datetime
|
||||
from itertools import chain
|
||||
from itertools import zip_longest
|
||||
from textwrap import wrap, TextWrapper
|
||||
from textwrap import wrap
|
||||
from wcwidth import wcswidth
|
||||
|
||||
from toot.utils import format_content, get_text
|
||||
from toot.wcstring import pad
|
||||
from toot.utils import format_content, get_text, parse_html
|
||||
from toot.wcstring import wc_wrap
|
||||
|
||||
|
||||
START_CODES = {
|
||||
@ -101,6 +99,13 @@ def print_account(account):
|
||||
print_out(account['url'])
|
||||
|
||||
|
||||
HASHTAG_PATTERN = re.compile(r'(?<!\w)(#\w+)\b')
|
||||
|
||||
|
||||
def highlight_hashtags(line):
|
||||
return re.sub(HASHTAG_PATTERN, '<cyan>\\1</cyan>', line)
|
||||
|
||||
|
||||
def print_search_results(results):
|
||||
accounts = results['accounts']
|
||||
hashtags = results['hashtags']
|
||||
@ -121,54 +126,52 @@ def print_search_results(results):
|
||||
print_out("<yellow>Nothing found</yellow>")
|
||||
|
||||
|
||||
def print_timeline(items):
|
||||
def _print_item(item):
|
||||
def wrap_text(text, width):
|
||||
wrapper = TextWrapper(width=width, break_long_words=False, break_on_hyphens=False)
|
||||
return chain(*[wrapper.wrap(l) for l in text.split("\n")])
|
||||
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']
|
||||
|
||||
def timeline_rows(item):
|
||||
display_name = item['account']['display_name']
|
||||
username = "@" + item['account']['username']
|
||||
time = item['time'].strftime('%Y-%m-%d %H:%M%Z')
|
||||
time = status['created_at']
|
||||
time = datetime.strptime(time, "%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
time = time.strftime('%Y-%m-%d %H:%M%z')
|
||||
|
||||
left_column = [display_name]
|
||||
if display_name != username:
|
||||
left_column.append(username)
|
||||
left_column.append(time)
|
||||
if item['reblogged']:
|
||||
left_column.append("Reblogged @{}".format(item['reblogged']))
|
||||
username = "@" + status['account']['username']
|
||||
spacing = width - wcswidth(username) - wcswidth(time)
|
||||
|
||||
if item['reply_to_toot'] is not None:
|
||||
left_column.append('[RE]')
|
||||
display_name = status['account']['display_name']
|
||||
if display_name:
|
||||
spacing -= wcswidth(display_name) + 1
|
||||
|
||||
left_column.append("id: {}".format(item['id']))
|
||||
print_out("{}{}{}{}".format(
|
||||
"<green>{}</green> ".format(display_name) if display_name else "",
|
||||
"<blue>{}</blue>".format(username),
|
||||
" " * spacing,
|
||||
"<yellow>{}</yellow>".format(time),
|
||||
))
|
||||
|
||||
right_column = wrap_text(item['text'], 80)
|
||||
for paragraph in parse_html(content):
|
||||
print_out("")
|
||||
for line in paragraph:
|
||||
for subline in wc_wrap(line, width):
|
||||
print_out(highlight_hashtags(subline))
|
||||
|
||||
return zip_longest(left_column, right_column, fillvalue="")
|
||||
if media_attachments:
|
||||
print_out("\nMedia:")
|
||||
for attachment in media_attachments:
|
||||
url = attachment['text_url'] or attachment['url']
|
||||
for line in wc_wrap(url, width):
|
||||
print_out(line)
|
||||
|
||||
for left, right in timeline_rows(item):
|
||||
print_out("{} │ {}".format(pad(left, 30), right))
|
||||
print_out("\n{}{}{}".format(
|
||||
"ID <yellow>{}</yellow> ".format(status['id']),
|
||||
"↲ In reply to <yellow>{}</yellow> ".format(in_reply_to) if in_reply_to else "",
|
||||
"↻ Reblogged <blue>@{}</blue> ".format(reblog['account']['username']) if reblog else "",
|
||||
))
|
||||
|
||||
def _parse_item(item):
|
||||
content = item['reblog']['content'] if item['reblog'] else item['content']
|
||||
reblogged = item['reblog']['account']['username'] if item['reblog'] else None
|
||||
|
||||
soup = BeautifulSoup(content.replace(''', "'"), "html.parser")
|
||||
text = soup.get_text()
|
||||
time = datetime.strptime(item['created_at'], "%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
|
||||
return {
|
||||
"id": item['id'],
|
||||
"account": item['account'],
|
||||
"text": text,
|
||||
"time": time,
|
||||
"reblogged": reblogged,
|
||||
"reply_to_toot": item['in_reply_to_id']
|
||||
}
|
||||
|
||||
print_out("─" * 31 + "┬" + "─" * 88)
|
||||
def print_timeline(items, width=100):
|
||||
print_out("─" * width)
|
||||
for item in items:
|
||||
_print_item(_parse_item(item))
|
||||
print_out("─" * 31 + "┼" + "─" * 88)
|
||||
print_status(item, width)
|
||||
print_out("─" * width)
|
||||
|
Loading…
x
Reference in New Issue
Block a user