Added folding comment tree and loading more comments.

This commit is contained in:
Michael Lazar
2015-01-29 23:51:46 -08:00
parent 606f41fd67
commit 6ef1abadc3
4 changed files with 96 additions and 43 deletions

View File

@@ -3,8 +3,7 @@ import praw
from utils import clean, strip_subreddit_url, humanize_timestamp
# TODO: rename, ... container?
class BaseContent(object):
class ContainerBase(object):
@staticmethod
def strip_praw_comment(comment):
@@ -19,6 +18,7 @@ class BaseContent(object):
if isinstance(comment, praw.objects.MoreComments):
data['type'] = 'MoreComments'
data['count'] = comment.count
data['body'] = 'More comments [{}]'.format(comment.count)
else:
data['type'] = 'Comment'
@@ -55,30 +55,7 @@ class BaseContent(object):
return data
@staticmethod
def flatten_comments(comments, initial_level=0):
"""
Flatten a PRAW comment tree while preserving the nested level of each
comment via the `nested_level` attribute.
"""
stack = comments[:]
for item in stack:
item.nested_level = initial_level
retval = []
while stack:
item = stack.pop(0)
nested = getattr(item, 'replies', None)
if nested:
for n in nested:
n.nested_level = item.nested_level + 1
stack[0:0] = nested
retval.append(item)
return retval
class SubredditContent(BaseContent):
class SubredditContainer(ContainerBase):
"""
Grabs a subreddit from PRAW and lazily stores submissions to an internal
list for repeat access.
@@ -152,7 +129,7 @@ class SubredditContent(BaseContent):
self.display_name = '/r/' + self.subreddit
class SubmissionContent(BaseContent):
class SubmissionContainer(ContainerBase):
"""
Grabs a submission from PRAW and lazily store comments to an internal
list for repeat access and to allow expanding and hiding comments.
@@ -166,7 +143,6 @@ class SubmissionContent(BaseContent):
self.display_name = None
self._submission_data = None
self._comments = None
self._comment_data = None
self.reset()
@@ -201,10 +177,52 @@ class SubmissionContent(BaseContent):
return data
def iterate(self, index, step, n_cols):
def toggle(self, index):
"""
Toggle the state of the object at the given index.
If it is a comment, pack it into a hidden comment.
If it is a hidden comment, unpack it.
If it is more comments, load the comments.
"""
data = self.get(index)
if data['type'] == 'Comment':
cache = [data]
count = 1
for d in self.iterate(index+1, 1):
if d['level'] <= data['level']:
break
count += d.get('count', 1)
cache.append(d)
comment = {}
comment['type'] = 'HiddenComment'
comment['cache'] = cache
comment['count'] = count
comment['level'] = data['level']
comment['body'] = 'Hidden [{}]'.format(count)
self._comment_data[index:index+len(cache)] = [comment]
elif data['type'] == 'HiddenComment':
self._comment_data[index:index+1] = data['cache']
elif data['type'] == 'MoreComments':
comments = data['object'].comments()
comments = self.flatten_comments(comments, root_level=data['level'])
comment_data = [self.strip_praw_comment(c) for c in comments]
self._comment_data[index:index+1] = comment_data
else:
raise ValueError('% type not recognized' % data['type'])
def iterate(self, index, step, n_cols=70):
while True:
yield self.get(index, n_cols)
yield self.get(index, n_cols=n_cols)
index += step
def reset(self):
@@ -216,5 +234,27 @@ class SubmissionContent(BaseContent):
self._submission_data = self.strip_praw_submission(self.submission)
self.display_name = self._submission_data['permalink']
self._comments = self.flatten_comments(self.submission.comments)
self._comment_data = [self.strip_praw_comment(c) for c in self._comments]
comments = self.flatten_comments(self.submission.comments)
self._comment_data = [self.strip_praw_comment(c) for c in comments]
@staticmethod
def flatten_comments(comments, root_level=0):
"""
Flatten a PRAW comment tree while preserving the nested level of each
comment via the `nested_level` attribute.
"""
stack = comments[:]
for item in stack:
item.nested_level = root_level
retval = []
while stack:
item = stack.pop(0)
nested = getattr(item, 'replies', None)
if nested:
for n in nested:
n.nested_level = item.nested_level + 1
stack[0:0] = nested
retval.append(item)
return retval

View File

@@ -1,24 +1,25 @@
import argparse
import praw
from utils import curses_session
from content_generators import SubredditContent
from content import SubredditContainer
from subreddit_viewer import SubredditViewer
parser = argparse.ArgumentParser(description='Reddit Terminal Viewer (RTV)')
parser.add_argument('-u', '--username', help='reddit username')
parser.add_argument('-p', '--password', help='reddit password')
parser.add_argument('-s', '--subreddit', default='front', help='subreddit name')
parser.add_argument('-l', '--link', help='full link to a specific submission')
parser = argparse.ArgumentParser(description='Reddit Terminal Viewer')
parser.add_argument('-s', dest='subreddit', default='front', help='subreddit name')
parser.add_argument('-l', dest='link', help='full link to a specific submission')
group = parser.add_argument_group('authentication (optional)')
group.add_argument('-u', dest='username', help='reddit username')
group.add_argument('-p', dest='password', help='reddit password')
def main(args):
r = praw.Reddit(user_agent='reddit terminal viewer (rtv) v0.0')
r = praw.Reddit(user_agent='reddit terminal viewer v0.0')
if args.username and args.password:
r.login(args.username, args.password)
with curses_session() as stdscr:
content = SubredditContent(r, subreddit=args.subreddit)
content = SubredditContainer(r, subreddit=args.subreddit)
viewer = SubredditViewer(stdscr, content)
viewer.loop()

View File

@@ -2,7 +2,7 @@ import praw
import curses
import sys
from content_generators import SubmissionContent, SubredditContent
from content import SubmissionContainer, SubredditContainer
from utils import curses_session, LoadScreen
from viewer import BaseViewer
@@ -33,6 +33,10 @@ class SubmissionViewer(BaseViewer):
elif cmd in (curses.KEY_F5, ord('r')):
self.refresh_content()
# Show / hide a comment tree
elif cmd == ord(' '):
self.toggle_comment()
elif cmd == curses.KEY_RESIZE:
self.draw()
@@ -47,6 +51,11 @@ class SubmissionViewer(BaseViewer):
else:
curses.beep()
def toggle_comment(self):
self.content.toggle(self.nav.absolute_index)
self.draw()
def refresh_content(self):
self.add_loading()
@@ -70,6 +79,9 @@ class SubmissionViewer(BaseViewer):
if data['type'] == 'MoreComments':
self.draw_more_comments(win, data)
elif data['type'] == 'HiddenComment':
self.draw_more_comments(win, data)
elif data['type'] == 'Comment':
self.draw_comment(win, data, inverted=inverted)

View File

@@ -3,7 +3,7 @@ import textwrap
import curses
import sys
from content_generators import SubredditContent, SubmissionContent
from content import SubredditContainer, SubmissionContainer
from submission_viewer import SubmissionViewer
from viewer import BaseViewer
from utils import curses_session, text_input, LoadScreen
@@ -75,7 +75,7 @@ class SubredditViewer(BaseViewer):
with LoadScreen(self.stdscr):
submission = self.content.get(self.nav.absolute_index)['object']
content = SubmissionContent(submission)
content = SubmissionContainer(submission)
viewer = SubmissionViewer(self.stdscr, content)
viewer.loop()