Squashed commit of the following:
Updated the supported python versions list.
Fixed regression in displaying xposts. #173.
Fixing a few style things.
Added a more robust test for the tornado handler.
Trying without pytest-cov
Updated travis for coverage.
Remove python 3.2 support because no unicode literals, following what praw supports.
"Side effect is not iterable."
Added requirements for travis.
Renamed travis file correctly.
Adding test configurations, got tox working.
Adding vcr cassettes to the repo.
Renamed requirements files.
Split up tests and cleaned up test names.
Tests done, still one failure.
Treat cassettes as binary to prevent bad merging.
Fixed a few broken tests.
Added a timeout to notifications.
Prepping subreddit page.
Finished submission page tests.
Working on submission tests.
Fixed vcr matching on urls with params, started submission tests.
Log cleanup.
Still trying to fix a broken test.
-Fixed a few pytest bugs and tweaked logging.
Still working on subscription tests.
Finished page tests, on to subscription page.
Finished content tests and starting page tests.
Added the test refresh-token file to gitignore.
Moved functional test file out of the repository.
Continuing work on subreddit content tests.
Tests now match module names, cassettes are split into individual tests for faster loading.
Linter fixes.
Cleanup.
Added support for nested loaders.
Added pytest options, starting subreddit content tests.
Back on track with loader, continuing content tests.
Finishing submission content tests and discovered snag with loader exception handling.
VCR up and running, continuing to implement content tests.
Playing around with vcr.py
Moved helper functions into terminal and new objects.py
Fixed a few broken tests.
Working on navigator tests.
Reorganizing some things.
Mocked webbrowser._tryorder for terminal test.
Completed oauth tests.
Progress on the oauth tests.
Working on adding fake tornado request.
Starting on OAuth tool tests.
Finished curses helpers tests.
Still working on curses helpers tests.
Almost finished with tests on curses helpers.
Adding tests and working on mocking stdscr.
Starting to add tests for curses functions.
Merge branch 'future_work' of https://github.com/michael-lazar/rtv into future_work
Refactoring controller, still in progress.
Renamed auth handler.
Rename CursesHelper to CursesBase.
Added temporary file with a possible template for func testing.
Mixup between basename and dirname.
Merge branch 'future_work' of https://github.com/michael-lazar/rtv into future_work
py3 compatability for mock.
Beginning to refactor the curses session.
Started adding tests, improved unicode handling in the config.
Cleanup, fixed a few typos.
Major refactor, almost done!.
Started a config class.
Merge branch 'master' into future_work
The editor now handles unicode characters in all situations.
Fixed a few typos from previous commits.
__main__.py formatting.
Cleaned up history logic and moved to the config file.
This commit is contained in:
236
rtv/subreddit.py
236
rtv/subreddit.py
@@ -1,54 +1,40 @@
|
||||
import curses
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import time
|
||||
import logging
|
||||
import atexit
|
||||
import curses
|
||||
|
||||
import requests
|
||||
import six
|
||||
|
||||
from .exceptions import SubredditError, AccountError
|
||||
from .page import BasePage, Navigator, BaseController
|
||||
from . import docs
|
||||
from .content import SubredditContent
|
||||
from .page import Page, PageController, logged_in
|
||||
from .objects import Navigator, Color
|
||||
from .submission import SubmissionPage
|
||||
from .subscription import SubscriptionPage
|
||||
from .content import SubredditContent
|
||||
from .helpers import open_browser, open_editor
|
||||
from .docs import SUBMISSION_FILE
|
||||
from .history import load_history, save_history
|
||||
from .curses_helpers import (Color, LoadScreen, add_line, get_arrow, get_gold,
|
||||
show_notification, prompt_input)
|
||||
|
||||
__all__ = ['history', 'SubredditController', 'SubredditPage']
|
||||
_logger = logging.getLogger(__name__)
|
||||
history = load_history()
|
||||
from .terminal import Terminal
|
||||
|
||||
|
||||
@atexit.register
|
||||
def save_links():
|
||||
global history
|
||||
save_history(history)
|
||||
|
||||
|
||||
class SubredditController(BaseController):
|
||||
class SubredditController(PageController):
|
||||
character_map = {}
|
||||
|
||||
|
||||
class SubredditPage(BasePage):
|
||||
class SubredditPage(Page):
|
||||
|
||||
def __init__(self, stdscr, reddit, oauth, name):
|
||||
def __init__(self, reddit, term, config, oauth, name, url=None):
|
||||
"""
|
||||
Params:
|
||||
name (string): Name of subreddit to open
|
||||
url (string): Optional submission to load upon start
|
||||
"""
|
||||
super(SubredditPage, self).__init__(reddit, term, config, oauth)
|
||||
|
||||
self.content = SubredditContent.from_name(reddit, name, term.loader)
|
||||
self.controller = SubredditController(self)
|
||||
self.loader = LoadScreen(stdscr)
|
||||
self.oauth = oauth
|
||||
self.nav = Navigator(self.content.get)
|
||||
|
||||
content = SubredditContent.from_name(reddit, name, self.loader)
|
||||
super(SubredditPage, self).__init__(stdscr, reddit, content, oauth)
|
||||
|
||||
def loop(self):
|
||||
"Main control loop"
|
||||
|
||||
while True:
|
||||
self.draw()
|
||||
cmd = self.stdscr.getch()
|
||||
self.controller.trigger(cmd)
|
||||
if url:
|
||||
self.open_submission(url=url)
|
||||
|
||||
@SubredditController.register(curses.KEY_F5, 'r')
|
||||
def refresh_content(self, name=None, order=None):
|
||||
@@ -62,16 +48,10 @@ class SubredditPage(BasePage):
|
||||
if order == 'ignore':
|
||||
order = None
|
||||
|
||||
try:
|
||||
with self.term.loader():
|
||||
self.content = SubredditContent.from_name(
|
||||
self.reddit, name, self.loader, order=order)
|
||||
except AccountError:
|
||||
show_notification(self.stdscr, ['Not logged in'])
|
||||
except SubredditError:
|
||||
show_notification(self.stdscr, ['Invalid subreddit'])
|
||||
except requests.HTTPError:
|
||||
show_notification(self.stdscr, ['Could not reach subreddit'])
|
||||
else:
|
||||
self.reddit, name, self.term.loader, order=order)
|
||||
if not self.term.loader.exception:
|
||||
self.nav = Navigator(self.content.get)
|
||||
|
||||
@SubredditController.register('f')
|
||||
@@ -79,115 +59,114 @@ class SubredditPage(BasePage):
|
||||
"Open a prompt to search the given subreddit"
|
||||
|
||||
name = name or self.content.name
|
||||
prompt = 'Search {}:'.format(name)
|
||||
query = prompt_input(self.stdscr, prompt)
|
||||
if query is None:
|
||||
|
||||
query = self.term.prompt_input('Search {0}:'.format(name))
|
||||
if not query:
|
||||
return
|
||||
|
||||
try:
|
||||
with self.term.loader():
|
||||
self.content = SubredditContent.from_name(
|
||||
self.reddit, name, self.loader, query=query)
|
||||
except (IndexError, SubredditError): # if there are no submissions
|
||||
show_notification(self.stdscr, ['No results found'])
|
||||
else:
|
||||
self.reddit, name, self.term.loader, query=query)
|
||||
if not self.term.loader.exception:
|
||||
self.nav = Navigator(self.content.get)
|
||||
|
||||
@SubredditController.register('/')
|
||||
def prompt_subreddit(self):
|
||||
"Open a prompt to navigate to a different subreddit"
|
||||
prompt = 'Enter Subreddit: /r/'
|
||||
name = prompt_input(self.stdscr, prompt)
|
||||
|
||||
name = self.term.prompt_input('Enter Subreddit: /r/')
|
||||
if name is not None:
|
||||
self.refresh_content(name=name, order='ignore')
|
||||
|
||||
@SubredditController.register(curses.KEY_RIGHT, 'l')
|
||||
def open_submission(self):
|
||||
def open_submission(self, url=None):
|
||||
"Select the current submission to view posts"
|
||||
|
||||
data = self.content.get(self.nav.absolute_index)
|
||||
page = SubmissionPage(self.stdscr, self.reddit, self.oauth,
|
||||
url=data['permalink'])
|
||||
page.loop()
|
||||
if data['url_type'] == 'selfpost':
|
||||
global history
|
||||
history.add(data['url_full'])
|
||||
data = {}
|
||||
if url is None:
|
||||
data = self.content.get(self.nav.absolute_index)
|
||||
url = data['permalink']
|
||||
|
||||
@SubredditController.register(curses.KEY_ENTER, 10, 'o')
|
||||
with self.term.loader():
|
||||
page = SubmissionPage(
|
||||
self.reddit, self.term, self.config, self.oauth, url=url)
|
||||
if self.term.loader.exception:
|
||||
return
|
||||
|
||||
page.loop()
|
||||
|
||||
if data.get('url_type') in ('selfpost', 'x-post'):
|
||||
self.config.history.add(data['url_full'])
|
||||
|
||||
@SubredditController.register(curses.KEY_ENTER, Terminal.RETURN, 'o')
|
||||
def open_link(self):
|
||||
"Open a link with the webbrowser"
|
||||
data = self.content.get(self.nav.absolute_index)
|
||||
|
||||
url = data['url_full']
|
||||
global history
|
||||
history.add(url)
|
||||
if data['url_type'] in ['x-post', 'selfpost']:
|
||||
page = SubmissionPage(self.stdscr, self.reddit, self.oauth,
|
||||
url=url)
|
||||
page.loop()
|
||||
data = self.content.get(self.nav.absolute_index)
|
||||
if data['url_type'] in ('x-post', 'selfpost'):
|
||||
# Open links to other posts directly in RTV
|
||||
self.open_submission()
|
||||
else:
|
||||
open_browser(url)
|
||||
self.term.open_browser(data['url_full'])
|
||||
self.config.history.add(data['url_full'])
|
||||
|
||||
@SubredditController.register('c')
|
||||
@logged_in
|
||||
def post_submission(self):
|
||||
"Post a new submission to the given subreddit"
|
||||
|
||||
if not self.reddit.is_oauth_session():
|
||||
show_notification(self.stdscr, ['Not logged in'])
|
||||
# Check that the subreddit can be submitted to
|
||||
name = self.content.name
|
||||
if '+' in name or name in ('/r/all', '/r/front', '/r/me'):
|
||||
self.term.show_notification("Can't post to {0}".format(name))
|
||||
return
|
||||
|
||||
# Strips the subreddit to just the name
|
||||
# Make sure it is a valid subreddit for submission
|
||||
subreddit = self.reddit.get_subreddit(self.content.name)
|
||||
sub = str(subreddit).split('/')[2]
|
||||
if '+' in sub or sub in ('all', 'front', 'me'):
|
||||
show_notification(self.stdscr, ['Invalid subreddit'])
|
||||
submission_info = docs.SUBMISSION_FILE.format(name=name)
|
||||
text = self.term.open_editor(submission_info)
|
||||
if not text or '\n' not in text:
|
||||
self.term.show_notification('Aborted')
|
||||
return
|
||||
|
||||
# Open the submission window
|
||||
submission_info = SUBMISSION_FILE.format(name=subreddit, content='')
|
||||
curses.endwin()
|
||||
submission_text = open_editor(submission_info)
|
||||
curses.doupdate()
|
||||
|
||||
# Validate the submission content
|
||||
if not submission_text:
|
||||
show_notification(self.stdscr, ['Aborted'])
|
||||
return
|
||||
if '\n' not in submission_text:
|
||||
show_notification(self.stdscr, ['No content'])
|
||||
title, content = text.split('\n', 1)
|
||||
with self.term.loader(message='Posting', delay=0):
|
||||
submission = self.reddit.submit(name, title, text=content)
|
||||
# Give reddit time to process the submission
|
||||
time.sleep(2.0)
|
||||
if self.term.loader.exception:
|
||||
return
|
||||
|
||||
title, content = submission_text.split('\n', 1)
|
||||
with self.safe_call as s:
|
||||
with self.loader(message='Posting', delay=0):
|
||||
post = self.reddit.submit(sub, title, text=content)
|
||||
time.sleep(2.0)
|
||||
# Open the newly created post
|
||||
s.catch = False
|
||||
page = SubmissionPage(self.stdscr, self.reddit, self.oauth,
|
||||
submission=post)
|
||||
page.loop()
|
||||
self.refresh_content()
|
||||
# Open the newly created post
|
||||
with self.term.loader():
|
||||
page = SubmissionPage(
|
||||
self.reddit, self.term, self.config, self.oauth,
|
||||
submission=submission)
|
||||
if self.term.loader.exception:
|
||||
return
|
||||
|
||||
page.loop()
|
||||
|
||||
self.refresh_content()
|
||||
|
||||
@SubredditController.register('s')
|
||||
@logged_in
|
||||
def open_subscriptions(self):
|
||||
"Open user subscriptions page"
|
||||
|
||||
if not self.reddit.is_oauth_session():
|
||||
show_notification(self.stdscr, ['Not logged in'])
|
||||
with self.term.loader():
|
||||
page = SubscriptionPage(
|
||||
self.reddit, self.term, self.config, self.oauth)
|
||||
if self.term.loader.exception:
|
||||
return
|
||||
|
||||
# Open subscriptions page
|
||||
page = SubscriptionPage(self.stdscr, self.reddit, self.oauth)
|
||||
page.loop()
|
||||
|
||||
# When user has chosen a subreddit in the subscriptions list,
|
||||
# When the user has chosen a subreddit in the subscriptions list,
|
||||
# refresh content with the selected subreddit
|
||||
if page.selected_subreddit_data is not None:
|
||||
self.refresh_content(name=page.selected_subreddit_data['name'])
|
||||
if page.subreddit_data is not None:
|
||||
self.refresh_content(name=page.subreddit_data['name'],
|
||||
order='ignore')
|
||||
|
||||
@staticmethod
|
||||
def draw_item(win, data, inverted=False):
|
||||
def _draw_item(self, win, data, inverted=False):
|
||||
|
||||
n_rows, n_cols = win.getmaxyx()
|
||||
n_cols -= 1 # Leave space for the cursor in the first column
|
||||
@@ -199,33 +178,36 @@ class SubredditPage(BasePage):
|
||||
n_title = len(data['split_title'])
|
||||
for row, text in enumerate(data['split_title'], start=offset):
|
||||
if row in valid_rows:
|
||||
add_line(win, text, row, 1, curses.A_BOLD)
|
||||
self.term.add_line(win, text, row, 1, curses.A_BOLD)
|
||||
|
||||
row = n_title + offset
|
||||
if row in valid_rows:
|
||||
seen = (data['url_full'] in history)
|
||||
seen = (data['url_full'] in self.config.history)
|
||||
link_color = Color.MAGENTA if seen else Color.BLUE
|
||||
attr = curses.A_UNDERLINE | link_color
|
||||
add_line(win, u'{url}'.format(**data), row, 1, attr)
|
||||
self.term.add_line(win, '{url}'.format(**data), row, 1, attr)
|
||||
|
||||
row = n_title + offset + 1
|
||||
if row in valid_rows:
|
||||
add_line(win, u'{score} '.format(**data), row, 1)
|
||||
text, attr = get_arrow(data['likes'])
|
||||
add_line(win, text, attr=attr)
|
||||
add_line(win, u' {created} {comments} '.format(**data))
|
||||
self.term.add_line(win, '{score} '.format(**data), row, 1)
|
||||
text, attr = self.term.get_arrow(data['likes'])
|
||||
self.term.add_line(win, text, attr=attr)
|
||||
self.term.add_line(win, ' {created} {comments} '.format(**data))
|
||||
|
||||
if data['gold']:
|
||||
text, attr = get_gold()
|
||||
add_line(win, text, attr=attr)
|
||||
text, attr = self.term.guilded
|
||||
self.term.add_line(win, text, attr=attr)
|
||||
|
||||
if data['nsfw']:
|
||||
text, attr = 'NSFW', (curses.A_BOLD | Color.RED)
|
||||
add_line(win, text, attr=attr)
|
||||
self.term.add_line(win, text, attr=attr)
|
||||
|
||||
row = n_title + offset + 2
|
||||
if row in valid_rows:
|
||||
add_line(win, u'{author}'.format(**data), row, 1, curses.A_BOLD)
|
||||
add_line(win, u' /r/{subreddit}'.format(**data), attr=Color.YELLOW)
|
||||
text = '{author}'.format(**data)
|
||||
self.term.add_line(win, text, row, 1, curses.A_BOLD)
|
||||
text = ' /r/{subreddit}'.format(**data)
|
||||
self.term.add_line(win, text, attr=Color.YELLOW)
|
||||
if data['flair']:
|
||||
add_line(win, u' {flair}'.format(**data), attr=Color.RED)
|
||||
text = ' {flair}'.format(**data)
|
||||
self.term.add_line(win, text, attr=Color.RED)
|
||||
Reference in New Issue
Block a user