From 6e0ea8d6c871bd79e80b87de6cc88ea1cc5e6118 Mon Sep 17 00:00:00 2001 From: Arjun Satarkar Date: Sat, 29 Jul 2023 03:56:31 +0530 Subject: Add filtering by tag and feed, improve modularity of some HTML --- README.md | 3 +-- serve.py | 19 +++++++++++++-- tagrss.py | 62 +++++++++++++++++++++++++++++++++++++++++------- views/add_feed.tpl | 2 +- views/hover_help.tpl | 1 + views/index.tpl | 35 ++++++++++++++++++++++++++- views/list_feeds.tpl | 4 +++- views/manage_feed.tpl | 2 +- views/tag_hover_help.tpl | 1 + 9 files changed, 113 insertions(+), 16 deletions(-) create mode 100644 views/hover_help.tpl create mode 100644 views/tag_hover_help.tpl diff --git a/README.md b/README.md index 58300e2..2fe658a 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,9 @@ See `LICENSE.txt` in the root of this repository for the text of the license. ## To do -* Add filtering by tag/feed +* Add JS to make the feed/tag input situation work like one would normally expect rather than like it's 1985. (Progressive enhancement, though.) * Do more user input validation * Handle more `requests` and `feedparser` error conditions -* Add logging * Add some reasonably high internal limit on tag count * Add support for authentication * Allow specifying update interval on a per-feed basis diff --git a/serve.py b/serve.py index f1061b1..be650a7 100755 --- a/serve.py +++ b/serve.py @@ -71,9 +71,20 @@ def index(): per_page: int = min(MAX_PER_PAGE_ENTRIES, int(bottle.request.query.get("per_page", DEFAULT_PER_PAGE_ENTRIES))) # type: ignore page_num = int(bottle.request.query.get("page_num", 1)) # type: ignore offset = (page_num - 1) * per_page + included_feeds_str: typing.Optional[str] = bottle.request.query.get("included_feeds", None) # type: ignore + included_feeds: typing.Optional[list[int]] = None + if included_feeds_str: + try: + included_feeds = [int(feed_id) for feed_id in included_feeds_str.split(" ")] + except ValueError: + pass + included_tags_str: typing.Optional[str] = bottle.request.query.get("included_tags", None) # type: ignore + included_tags: typing.Optional[list[str]] = None + if included_tags_str: + included_tags = parse_space_separated_tags(included_tags_str) with core_lock: - total_pages: int = max(1, math.ceil(core.get_entry_count() / per_page)) - entries = core.get_entries(limit=per_page, offset=offset) + total_pages: int = max(1, math.ceil(core.get_entry_count(included_feeds=included_feeds) / per_page)) + entries = core.get_entries(limit=per_page, offset=offset, included_feeds=included_feeds, included_tags=included_tags) return bottle.template( "index", entries=entries, @@ -82,6 +93,10 @@ def index(): total_pages=total_pages, per_page=per_page, max_per_page=MAX_PER_PAGE_ENTRIES, + included_feeds=included_feeds, + included_tags=included_tags, + included_feeds_str=included_feeds_str, + included_tags_str=included_tags_str, core=core, ) diff --git a/tagrss.py b/tagrss.py index 7c37bec..6ac5270 100644 --- a/tagrss.py +++ b/tagrss.py @@ -89,13 +89,34 @@ class TagRss: self.store_feed_entries(feed_id, parsed_feed, epoch_downloaded) def get_entries( - self, *, limit: int, offset: int = 0 + self, + *, + limit: int, + offset: int = 0, + included_feeds: typing.Optional[typing.Collection[int]] = None, + included_tags: typing.Optional[typing.Collection[str]] = None, ) -> list[dict[str, typing.Any]]: + where_clause: str = "" + if included_feeds or included_tags: + where_clause = "WHERE 1" + if included_feeds: + where_clause += f" AND feed_id IN ({','.join('?' * len(included_feeds))})" + if included_tags: + where_clause += ( + " AND feed_id IN (SELECT feed_id FROM feed_tags WHERE tag = ?)" + * len(included_tags) + ) with self.connection: resp = self.connection.execute( - "SELECT id, feed_id, title, link, epoch_published, epoch_updated FROM entries \ + f"SELECT id, feed_id, title, link, epoch_published, epoch_updated FROM entries \ + {where_clause} \ ORDER BY id DESC LIMIT ? OFFSET ?;", - (limit, offset), + ( + *(included_feeds if included_feeds else ()), + *(included_tags if included_tags else ()), + limit, + offset, + ), ).fetchall() entries = [] @@ -177,11 +198,36 @@ class TagRss: ) return feeds - def get_entry_count(self) -> int: - with self.connection: - return self.connection.execute("SELECT count from entry_count;").fetchone()[ - 0 - ] + def get_entry_count( + self, + *, + included_feeds: typing.Optional[typing.Collection[int]] = None, + included_tags: typing.Optional[typing.Collection[str]] = None, + ) -> int: + if not (included_feeds or included_tags): + with self.connection: + return self.connection.execute( + "SELECT count from entry_count;" + ).fetchone()[0] + else: + where_clause: str = "WHERE 1" + if included_feeds: + where_clause += ( + f" AND feed_id IN ({','.join('?' * len(included_feeds))})" + ) + if included_tags: + where_clause += ( + " AND feed_id IN (SELECT feed_id FROM feed_tags WHERE tag = ?)" + * len(included_tags) + ) + with self.connection: + return self.connection.execute( + f"SELECT COUNT(*) FROM entries {where_clause};", + ( + *(included_feeds if included_feeds else ()), + *(included_tags if included_tags else ()), + ), + ).fetchone()[0] def get_feed_count(self) -> int: with self.connection: diff --git a/views/add_feed.tpl b/views/add_feed.tpl index b08ea0e..0110bac 100644 --- a/views/add_feed.tpl +++ b/views/add_feed.tpl @@ -21,7 +21,7 @@
- 🛈 + % include("tag_hover_help.tpl")
diff --git a/views/hover_help.tpl b/views/hover_help.tpl new file mode 100644 index 0000000..5d5eb97 --- /dev/null +++ b/views/hover_help.tpl @@ -0,0 +1 @@ +🛈 diff --git a/views/index.tpl b/views/index.tpl index acb6fd1..52e997f 100644 --- a/views/index.tpl +++ b/views/index.tpl @@ -51,6 +51,33 @@ List feeds

+
+ Filter +
+
+ + % include("hover_help.tpl", text="Space-separated feed IDs.") +
+
+ + % include("tag_hover_help.tpl") +
+ + + +
+
+ + + + + +
+
@@ -92,7 +119,7 @@ @@ -107,6 +134,12 @@ + % if included_feeds: + + % end + % if included_tags: + + % end % include("footer.tpl") diff --git a/views/list_feeds.tpl b/views/list_feeds.tpl index 6377289..3c60f06 100644 --- a/views/list_feeds.tpl +++ b/views/list_feeds.tpl @@ -13,6 +13,7 @@ + @@ -22,7 +23,8 @@ % for i, feed in enumerate(feeds): - + + diff --git a/views/manage_feed.tpl b/views/manage_feed.tpl index c110ad2..0ff127e 100644 --- a/views/manage_feed.tpl +++ b/views/manage_feed.tpl @@ -46,7 +46,7 @@ - 🛈 + % include("tag_hover_help.tpl") diff --git a/views/tag_hover_help.tpl b/views/tag_hover_help.tpl new file mode 100644 index 0000000..9007e55 --- /dev/null +++ b/views/tag_hover_help.tpl @@ -0,0 +1 @@ +% include("hover_help.tpl", text="Space-separated. Backslashes escape spaces.") -- cgit v1.2.3-57-g22cb
âš™ - {{core.get_feed_title(entry["feed_id"])}} + {{core.get_feed_title(entry["feed_id"])}} ({{entry["feed_id"]}})
#ID Feed Source Manage
{{i + 1 + offset}}{{feed["title"]}}{{feed["id"]}}{{feed["title"]}} (filter) 🔗 ⚙