#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import unicode_literals from __future__ import print_function import os import sys import time import curses import locale import threading from types import MethodType from collections import Counter from vcr import VCR from six.moves.urllib.parse import urlparse, parse_qs from ttrv.theme import Theme, ThemeList from ttrv.config import Config from ttrv.packages import praw from ttrv.oauth import OAuthHelper from ttrv.terminal import Terminal from ttrv.objects import curses_session from ttrv.subreddit_page import SubredditPage from ttrv.submission_page import SubmissionPage from ttrv.subscription_page import SubscriptionPage try: from unittest import mock except ImportError: import mock def initialize_vcr(): def auth_matcher(r1, r2): return (r1.headers.get('authorization') == r2.headers.get('authorization')) def uri_with_query_matcher(r1, r2): p1, p2 = urlparse(r1.uri), urlparse(r2.uri) return (p1[:3] == p2[:3] and parse_qs(p1.query, True) == parse_qs(p2.query, True)) cassette_dir = os.path.join(os.path.dirname(__file__), 'cassettes') if not os.path.exists(cassette_dir): os.makedirs(cassette_dir) filename = os.path.join(cassette_dir, 'demo_theme.yaml') if os.path.exists(filename): record_mode = 'none' else: record_mode = 'once' vcr = VCR( record_mode=record_mode, filter_headers=[('Authorization', '**********')], filter_post_data_parameters=[('refresh_token', '**********')], match_on=['method', 'uri_with_query', 'auth', 'body'], cassette_library_dir=cassette_dir) vcr.register_matcher('auth', auth_matcher) vcr.register_matcher('uri_with_query', uri_with_query_matcher) return vcr # Patch the getch method so we can display multiple notifications or # other elements that require a keyboard input on the screen at the # same time without blocking the main thread. def notification_getch(self): if self.pause_getch: return -1 return 0 def prompt_getch(self): while self.pause_getch: time.sleep(1) return 0 def draw_screen(stdscr, reddit, config, theme, oauth): threads = [] max_y, max_x = stdscr.getmaxyx() mid_x = int(max_x / 2) tall_y, short_y = int(max_y / 3 * 2), int(max_y / 3) stdscr.clear() stdscr.refresh() # =================================================================== # Submission Page # =================================================================== win1 = stdscr.derwin(tall_y - 1, mid_x - 1, 0, 0) term = Terminal(win1, config) term.set_theme(theme) oauth.term = term url = 'https://www.reddit.com/r/Python/comments/4dy7xr' with term.loader('Loading'): page = SubmissionPage(reddit, term, config, oauth, url=url) # Tweak the data in order to demonstrate the full range of settings data = page.content.get(-1) data['object'].link_flair_text = 'flair' data['object'].gilded = 1 data['object'].over_18 = True data['object'].saved = True data.update(page.content.strip_praw_submission(data['object'])) data = page.content.get(0) data['object'].author.name = 'kafoozalum' data['object'].stickied = True data['object'].author_flair_text = 'flair' data['object'].likes = True data.update(page.content.strip_praw_comment(data['object'])) data = page.content.get(1) data['object'].saved = True data['object'].likes = False data['object'].score_hidden = True data['object'].gilded = 1 data.update(page.content.strip_praw_comment(data['object'])) data = page.content.get(2) data['object'].author.name = 'kafoozalum' data['object'].body = data['object'].body[:100] data.update(page.content.strip_praw_comment(data['object'])) page.content.toggle(9) page.content.toggle(5) page.draw() # =================================================================== # Subreddit Page # =================================================================== win2 = stdscr.derwin(tall_y - 1, mid_x - 1, 0, mid_x + 1) term = Terminal(win2, config) term.set_theme(theme) oauth.term = term with term.loader('Loading'): page = SubredditPage(reddit, term, config, oauth, '/u/saved') # Tweak the data in order to demonstrate the full range of settings data = page.content.get(3) data['object'].hide_score = True data['object'].author = None data['object'].saved = False data.update(page.content.strip_praw_submission(data['object'])) page.content.order = 'rising' page.nav.cursor_index = 1 page.draw() term.pause_getch = True term.getch = MethodType(notification_getch, term) thread = threading.Thread(target=term.show_notification, args=('Success',), kwargs={'style': 'Success'}) thread.start() threads.append((thread, term)) # =================================================================== # Subscription Page # =================================================================== win3 = stdscr.derwin(short_y, mid_x - 1, tall_y, 0) term = Terminal(win3, config) term.set_theme(theme) oauth.term = term with term.loader('Loading'): page = SubscriptionPage(reddit, term, config, oauth, 'popular') page.nav.cursor_index = 1 page.draw() term.pause_getch = True term.getch = MethodType(notification_getch, term) thread = threading.Thread(target=term.show_notification, args=('Error',), kwargs={'style': 'Error'}) thread.start() threads.append((thread, term)) # =================================================================== # Multireddit Page # =================================================================== win4 = stdscr.derwin(short_y, mid_x - 1, tall_y, mid_x + 1) term = Terminal(win4, config) term.set_theme(theme) oauth.term = term with term.loader('Loading'): page = SubscriptionPage(reddit, term, config, oauth, 'multireddit') page.nav.cursor_index = 1 page.draw() term.pause_getch = True term.getch = MethodType(notification_getch, term) thread = threading.Thread(target=term.show_notification, args=('Info',), kwargs={'style': 'Info'}) thread.start() threads.append((thread, term)) term = Terminal(win4, config) term.set_theme(theme) term.pause_getch = True term.getch = MethodType(prompt_getch, term) thread = threading.Thread(target=term.prompt_y_or_n, args=('Prompt: ',)) thread.start() threads.append((thread, term)) time.sleep(0.5) curses.curs_set(0) return threads def main(): locale.setlocale(locale.LC_ALL, '') if len(sys.argv) > 1: theme = Theme.from_name(sys.argv[1]) else: theme = Theme() vcr = initialize_vcr() with vcr.use_cassette('demo_theme.yaml') as cassette, \ curses_session() as stdscr: config = Config() if vcr.record_mode == 'once': config.load_refresh_token() else: config.refresh_token = 'mock_refresh_token' reddit = praw.Reddit(user_agent='TTRV Theme Demo', decode_html_entities=False, disable_update_check=True) reddit.config.api_request_delay = 0 config.history.add('https://api.reddit.com/comments/6llvsl/_/djutc3s') config.history.add('http://i.imgur.com/Z9iGKWv.gifv') config.history.add('https://www.reddit.com/r/Python/comments/6302cj/rpython_official_job_board/') term = Terminal(stdscr, config) term.set_theme() oauth = OAuthHelper(reddit, term, config) oauth.authorize() theme_list = ThemeList() while True: term = Terminal(stdscr, config) term.set_theme(theme) threads = draw_screen(stdscr, reddit, config, theme, oauth) try: ch = term.show_notification(theme.display_string) except KeyboardInterrupt: ch = Terminal.ESCAPE for thread, term in threads: term.pause_getch = False thread.join() if vcr.record_mode == 'once': break else: cassette.play_counts = Counter() theme_list.reload() if ch == curses.KEY_RIGHT: theme = theme_list.next(theme) elif ch == curses.KEY_LEFT: theme = theme_list.previous(theme) elif ch == Terminal.ESCAPE: break else: # Force the theme to reload theme = theme_list.next(theme) theme = theme_list.previous(theme) sys.exit(main())