Rough mockup of subreddit page.
This commit is contained in:
@@ -147,7 +147,8 @@ class SubmissionDisplay(object):
|
||||
getattr(comment, 'author') else '[deleted]')
|
||||
date = utils.humanize_timestamp(comment.created_utc)
|
||||
score = submission.score
|
||||
color_attr = curses.color_pair(curses.COLOR_BLUE)
|
||||
color_attr = (curses.color_pair(curses.COLOR_GREEN) if comment.is_author
|
||||
else curses.color_pair(curses.COLOR_BLUE))
|
||||
win.addstr(0, 1, author, curses.A_UNDERLINE|color_attr)
|
||||
win.addstr(' {} points {}'.format(score, date), curses.A_BOLD)
|
||||
|
||||
@@ -174,7 +175,7 @@ class SubmissionDisplay(object):
|
||||
|
||||
return True
|
||||
|
||||
def draw_page(self, submission, index=-1):
|
||||
def draw_page(self, submission, comments, index=-1):
|
||||
"""
|
||||
Draw the comments page starting at the given index.
|
||||
"""
|
||||
@@ -190,13 +191,14 @@ class SubmissionDisplay(object):
|
||||
self._draw_post(submission)
|
||||
index += 1
|
||||
|
||||
comments = utils.flatten_tree(submission.comments)
|
||||
for comment in comments[index:]:
|
||||
try:
|
||||
if isinstance(comment, praw.objects.MoreComments):
|
||||
self._draw_more_comments(comment)
|
||||
else:
|
||||
comment.is_author = (comment.author == submission.author)
|
||||
self._draw_comment(comment)
|
||||
|
||||
except OOBError:
|
||||
break
|
||||
|
||||
@@ -211,9 +213,9 @@ class SubmissionController(object):
|
||||
self._index = -1
|
||||
self._cursor = 0
|
||||
|
||||
def loop(self, submission):
|
||||
def loop(self, submission, comments):
|
||||
|
||||
self.display.draw_page(submission, self._index)
|
||||
self.display.draw_page(submission, comments, self._index)
|
||||
|
||||
while True:
|
||||
|
||||
@@ -227,17 +229,20 @@ class SubmissionController(object):
|
||||
else:
|
||||
continue
|
||||
|
||||
self.display.draw_page(submission, self._index)
|
||||
self.display.draw_page(submission, comments, self._index)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
r = praw.Reddit(user_agent='reddit terminal viewer (rtv) v0.0')
|
||||
r.config.decode_html_entities = True
|
||||
submissions = r.get_subreddit('all').get_hot(limit=5)
|
||||
submission = submissions.next()
|
||||
comments = utils.flatten_tree(submission.comments)
|
||||
|
||||
|
||||
with utils.curses_session() as stdscr:
|
||||
|
||||
display = SubmissionDisplay(stdscr)
|
||||
controller = SubmissionController(display)
|
||||
controller.loop(submission)
|
||||
controller.loop(submission, comments)
|
||||
113
rtv/subreddit.py
Normal file
113
rtv/subreddit.py
Normal file
@@ -0,0 +1,113 @@
|
||||
import praw
|
||||
import textwrap
|
||||
import curses
|
||||
|
||||
from utils import humanize_timestamp, flatten_tree, clean
|
||||
|
||||
def strip_submission(sub):
|
||||
"Grab info from a PRAW submission and prep for display."
|
||||
|
||||
out = {}
|
||||
out['title'] = clean(sub.title)
|
||||
out['created'] = humanize_timestamp(sub.created_utc, long=False)
|
||||
out['comments'] = '{} comments'.format(sub.num_comments)
|
||||
out['score'] = '{} pts'.format(sub.score)
|
||||
out['author'] = clean(sub.author.name)
|
||||
out['subreddit'] = clean(sub.subreddit.url)
|
||||
out['url'] = ('(selfpost)' if sub.url.startswith('http://www.reddit.com/r/')
|
||||
else clean(sub.url))
|
||||
|
||||
return out
|
||||
|
||||
def draw_submission(win, data, top_down=True):
|
||||
"Draw a submission in the given window."
|
||||
|
||||
win.erase()
|
||||
n_rows, n_cols = win.getmaxyx()
|
||||
n_cols -= 1 # Leave space for the cursor on the first line
|
||||
|
||||
# Handle the case where the window is not large enough to fit the data.
|
||||
# Print as many rows as possible, either from the top down of the bottom up.
|
||||
valid_rows = range(0, n_rows)
|
||||
offset = 0 if top_down else -(data['n_rows'] - n_rows)
|
||||
|
||||
n_title = len(data['split_title'])
|
||||
for row, text in enumerate(data['split_title'], start=offset):
|
||||
if row in valid_rows:
|
||||
win.addstr(row, 1, text)
|
||||
|
||||
row = n_title
|
||||
if row in valid_rows:
|
||||
win.addnstr(row, 1, '{url}'.format(**data), n_cols)
|
||||
|
||||
row = n_title + 1
|
||||
if row in valid_rows:
|
||||
win.addnstr(row, 1, '{created} {comments} {score}'.format(**data), n_cols)
|
||||
|
||||
row = n_title + 2
|
||||
if row in valid_rows:
|
||||
win.addnstr(row, 1, '{author} {subreddit}'.format(**data), n_cols)
|
||||
|
||||
|
||||
class SubmissionGenerator(object):
|
||||
"""
|
||||
Grab submissions from PRAW lazily and store in an internal list for repeat
|
||||
access.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.r = praw.Reddit(user_agent='reddit terminal viewer (rtv) v0.0')
|
||||
self.r.config.decode_html_entities = True
|
||||
|
||||
self._submissions = self.r.get_front_page(limit=None)
|
||||
self._submission_data = []
|
||||
|
||||
def get(self, index, n_cols):
|
||||
|
||||
assert(index >= 0)
|
||||
|
||||
while index >= len(self._submission_data):
|
||||
data = strip_submission(self._submissions.next())
|
||||
self._submission_data.append(data)
|
||||
|
||||
# Modifies the original original dict, faster than copying
|
||||
out = self._submission_data[index]
|
||||
out['split_title'] = textwrap.wrap(out['title'], width=n_cols)
|
||||
out['n_rows'] = len(out['split_title']) + 3
|
||||
|
||||
return out
|
||||
|
||||
def iterate(self, index, n_cols):
|
||||
|
||||
while True:
|
||||
yield self.get(index, n_cols)
|
||||
index += 1
|
||||
|
||||
|
||||
|
||||
def draw_subreddit(stdscr):
|
||||
|
||||
generator = SubmissionGenerator()
|
||||
|
||||
main_window = stdscr.derwin(1, 0)
|
||||
main_window.erase()
|
||||
max_rows, max_cols = main_window.getmaxyx()
|
||||
|
||||
submission_i, current_row = 0, 0
|
||||
for data in generator.iterate(submission_i, max_cols-1):
|
||||
n_rows = min(max_rows-current_row, data['n_rows'])
|
||||
sub_window = main_window.derwin(n_rows, max_cols, current_row, 0)
|
||||
draw_submission(sub_window, data)
|
||||
sub_window.refresh() # Debugging
|
||||
current_row += n_rows + 1
|
||||
if current_row >= max_rows:
|
||||
break
|
||||
|
||||
main_window.refresh()
|
||||
main_window.getch()
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
#draw_submissions(None)
|
||||
curses.wrapper(draw_subreddit)
|
||||
19
rtv/utils.py
19
rtv/utils.py
@@ -52,28 +52,28 @@ def curses_session():
|
||||
curses.nocbreak()
|
||||
curses.endwin()
|
||||
|
||||
def humanize_timestamp(utc_timestamp):
|
||||
def humanize_timestamp(utc_timestamp, long=True):
|
||||
"""
|
||||
Convert a utc timestamp into a human readable time relative to now.
|
||||
"""
|
||||
timedelta = datetime.utcnow() - datetime.utcfromtimestamp(utc_timestamp)
|
||||
seconds = int(timedelta.total_seconds())
|
||||
if seconds < 60:
|
||||
return 'moments ago'
|
||||
return 'moments ago' if long else '0min'
|
||||
minutes = seconds / 60
|
||||
if minutes < 60:
|
||||
return '{} minutes ago'.format(minutes)
|
||||
return '%d' % minutes + (' minutes ago' if long else 'min')
|
||||
hours = minutes / 60
|
||||
if hours < 24:
|
||||
return '{} hours ago'.format(hours)
|
||||
return '%d' % hours + (' hours ago' if long else 'hr')
|
||||
days = hours / 24
|
||||
if days < 30:
|
||||
return '{} days ago'.format(days)
|
||||
return '%d' % days + (' days ago' if long else 'day')
|
||||
months = days / 30.4
|
||||
if months < 12:
|
||||
return '{} months ago'.format(months)
|
||||
return '%d' % months + (' months ago' if long else 'month')
|
||||
years = months / 12
|
||||
return '{} years ago'.format(years)
|
||||
return '%d' % years + (' years ago' if long else 'yr')
|
||||
|
||||
|
||||
def flatten_tree(tree):
|
||||
@@ -96,3 +96,8 @@ def flatten_tree(tree):
|
||||
stack[0:0] = nested
|
||||
retval.append(item)
|
||||
return retval
|
||||
|
||||
|
||||
def clean(unicode_string):
|
||||
"Convert unicode string into ascii-safe characters."
|
||||
return unicode_string.encode('ascii', 'replace').replace('\\', '')
|
||||
Reference in New Issue
Block a user