def feeds(): broken = request.args.get('broken') broken = {None: None, 'no': False, 'yes': True}[broken] updates_enabled = request.args.get('updates-enabled') updates_enabled = {None: None, 'no': False, 'yes': True}[updates_enabled] sort = request.args.get('sort', 'title') assert sort in ('title', 'added') error = None args = request.args.copy() tags_str = tags = args.pop('tags', None) if tags is None: pass elif not tags.strip(): # if tags is '', it's not a tag filter return redirect(url_for('.feeds', **args)) else: try: tags = yaml.safe_load(tags) except yaml.YAMLError as e: error = f"invalid tag query: invalid YAML: {e}: {tags_str}" return stream_template('feeds.html', feed_data=[], error=error) reader = get_reader() kwargs = dict(broken=broken, tags=tags, updates_enabled=updates_enabled) with_counts = request.args.get('counts') with_counts = {None: None, 'no': False, 'yes': True}[with_counts] counts = reader.get_feed_counts(**kwargs) if with_counts else None feed_data = [] try: feeds = reader.get_feeds(sort=sort, **kwargs) feed_data = (( feed, list(reader.get_feed_tags(feed)), reader.get_entry_counts(feed=feed) if with_counts else None, ) for feed in feeds) except ValueError as e: # TODO: there should be a better way of matching this kind of error if 'tag' in str(e).lower(): error = f"invalid tag query: {e}: {tags_str}" else: raise # Ensure flashed messages get removed from the session. # https://github.com/lemon24/reader/issues/81 get_flashed_messages() return stream_template('feeds.html', feed_data=feed_data, error=error, counts=counts)
def update_feed_tags(data): feed_url = data['feed-url'] feed_tags = set(data['feed-tags'].split()) reader = get_reader() tags = set(reader.get_feed_tags(feed_url)) for tag in tags - feed_tags: reader.remove_feed_tag(feed_url, tag) for tag in feed_tags - tags: reader.add_feed_tag(feed_url, tag)
def iter_tags(): for tag in itertools.chain([None, True, False], reader.get_feed_tags()): feed_counts = None entry_counts = None if with_counts: tags_arg = [tag] if tag is not None else tag feed_counts = reader.get_feed_counts(tags=tags_arg) entry_counts = reader.get_entry_counts(feed_tags=tags_arg) yield tag, feed_counts, entry_counts
def tags(): reader = get_reader() with_counts = request.args.get('counts') with_counts = {None: None, 'no': False, 'yes': True}[with_counts] tags = (( tag, reader.get_feed_counts(tags=[tag]) if with_counts else None, reader.get_entry_counts(feed_tags=[tag]) if with_counts else None, ) for tag in itertools.chain([True, False], reader.get_feed_tags())) return render_template('tags.html', tags=tags)
def entries(): show = request.args.get('show', 'unread') read = {'all': None, 'unread': False, 'read': True}[show] has_enclosures = request.args.get('has-enclosures') has_enclosures = {None: None, 'no': False, 'yes': True}[has_enclosures] important = request.args.get('important') important = {None: None, 'no': False, 'yes': True}[important] if not request.args.get('q'): sort = request.args.get('sort', 'recent') assert sort in ('recent', 'random') else: sort = request.args.get('sort', 'relevant') assert sort in ('relevant', 'recent', 'random') reader = get_reader() feed_url = request.args.get('feed') feed = None feed_tags = None if feed_url: feed = reader.get_feed(feed_url, None) if not feed: abort(404) feed_tags = list(reader.get_feed_tags(feed)) args = request.args.copy() query = args.pop('q', None) if query is None: def get_entries(**kwargs): yield from reader.get_entries(sort=sort, **kwargs) get_entry_counts = reader.get_entry_counts elif not query: # if the query is '', it's not a search args.pop('sort', None) return redirect(url_for('.entries', **args)) else: def get_entries(**kwargs): for sr in reader.search_entries(query, sort=sort, **kwargs): yield EntryProxy(sr, reader.get_entry(sr)) def get_entry_counts(**kwargs): return reader.search_entry_counts(query, **kwargs) # TODO: render the actual search result, not the entry # TODO: catch and flash syntax errors # TODO: don't show search box if search is not enabled error = None # TODO: duplicated from feeds() tags_str = tags = args.pop('tags', None) if tags is None: pass elif not tags.strip(): # if tags is '', it's not a tag filter return redirect(url_for('.entries', **args)) else: try: tags = yaml.safe_load(tags) except yaml.YAMLError as e: error = f"invalid tag query: invalid YAML: {e}: {tags_str}" return stream_template( 'entries.html', feed=feed, feed_tags=feed_tags, error=error ) kwargs = dict( feed=feed_url, read=read, has_enclosures=has_enclosures, important=important, feed_tags=tags, ) entries = get_entries(**kwargs, limit=request.args.get('limit', type=int)) with_counts = request.args.get('counts') with_counts = {None: None, 'no': False, 'yes': True}[with_counts] counts = get_entry_counts(**kwargs) if with_counts else None try: first = next(entries) entries = itertools.chain([first], entries) except StopIteration: pass except InvalidSearchQueryError as e: error = f"invalid search query: {e}" except ValueError as e: # TODO: there should be a better way of matching this kind of error if 'tag' in str(e).lower(): error = f"invalid tag query: {e}: {tags_str}" else: raise entries = list(entries) entries_data = None if feed_url: entries_data = [e.id for e in entries] # Ensure flashed messages get removed from the session, # otherwise they keep adding up and never disappear. # Assumes the template will call get_flashed_messages() at some point. # https://github.com/lemon24/reader/issues/81 get_flashed_messages() return stream_template( 'entries.html', entries=entries, feed=feed, feed_tags=feed_tags, entries_data=entries_data, error=error, counts=counts, )