Added folding comment tree and loading more comments.
This commit is contained in:
@@ -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
|
||||
17
rtv/main.py
17
rtv/main.py
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user