diff --git a/cry/cli.py b/cry/cli.py index bc78c03..499f664 100644 --- a/cry/cli.py +++ b/cry/cli.py @@ -117,3 +117,26 @@ def refresh(url): new_count = new_count + db.store_feed(f) click.echo(f"Fetched {new_count} new entries.") + + +@cli.command(name="show") +def show(): + """Show feeds.""" + + db = database.Database.local() + feeds = db.load_all() + + def feed_sort_key(f: feed.Feed) -> int: + if len(f.entries) > 0: + return max(e.inserted_at for e in f.entries) + return -1 + + feeds.sort(key=feed_sort_key) + for f in feeds: + click.echo(f"{f.title}") + if len(f.entries) > 0: + for entry in f.entries: + click.echo(f" {entry.title}") + else: + click.echo(f" ") + click.echo() diff --git a/cry/database.py b/cry/database.py index 51cc057..1bb303c 100644 --- a/cry/database.py +++ b/cry/database.py @@ -153,6 +153,72 @@ class Database: for url, last_fetched_ts, retry_after_ts, status, etag, modified in rows ] + def load_all(self, feed_limit: int = 20) -> list[feed.Feed]: + cursor = self.db.execute( + """ + SELECT + url, + last_fetched_ts, + retry_after_ts, + status, + etag, + modified, + title, + link + FROM feeds + """ + ) + rows = cursor.fetchall() + + almost_feeds = [] + for row in rows: + ( + url, + last_fetched_ts, + retry_after_ts, + status, + etag, + modified, + title, + link, + ) = row + meta = feed.FeedMeta( + url=url, + last_fetched_ts=last_fetched_ts, + retry_after_ts=retry_after_ts, + status=status, + etag=etag, + modified=modified, + origin=self.origin, + ) + almost_feeds.append((meta, title, link)) + + feeds = [] + for meta, title, link in almost_feeds: + cursor = self.db.execute( + """ + SELECT + id, + inserted_at, + title, + link + FROM entries + WHERE feed_url=? + ORDER BY inserted_at DESC + LIMIT ? + """, + [meta.url, feed_limit], + ) + rows = cursor.fetchall() + entries = [ + feed.Entry(id=id, inserted_at=inserted_at, title=title, link=link) + for id, inserted_at, title, link in rows + ] + f = feed.Feed(meta=meta, title=title, link=link, entries=entries) + feeds.append(f) + + return feeds + def load_feed(self, url: str) -> feed.Feed | None: cursor = self.db.execute( """ diff --git a/cry/feed.py b/cry/feed.py index 37c330c..9e7bb60 100644 --- a/cry/feed.py +++ b/cry/feed.py @@ -144,6 +144,7 @@ async def fetch_feed( Regardless, the new FeedMeta has the latest state of the feed. """ if feed.status == FEED_STATUS_DEAD: + LOG.info(f"{feed.url} is dead") return (None, feed) if time.time() < feed.retry_after_ts: @@ -198,6 +199,7 @@ async def fetch_feed( # permanently redirected URL, not just whatever the last thing # is... e.g. imagine a permanent followed by a temporary # redirect, then what? + LOG.info(f"{feed.url} permanently redirected to {response.url}") assert response.url is not None feed = dataclasses.replace(feed, url=response.url)