Stuff
This commit is contained in:
parent
275926dffe
commit
463abcb923
3 changed files with 90 additions and 55 deletions
19
cry/cli.py
19
cry/cli.py
|
|
@ -239,11 +239,13 @@ def unsubscribe(url):
|
|||
`list` command.)
|
||||
"""
|
||||
db = database.Database.local()
|
||||
count = db.set_feed_status(url, feed.FEED_STATUS_UNSUBSCRIBED)
|
||||
if count == 0:
|
||||
meta = db.load_meta(url)
|
||||
if meta is None:
|
||||
click.echo(f"Not subscribed to feed {url}")
|
||||
return 1
|
||||
|
||||
db.update_feed_status(meta, feed.FEED_STATUS_UNSUBSCRIBED)
|
||||
|
||||
|
||||
@cli.command("serve")
|
||||
def serve():
|
||||
|
|
@ -262,6 +264,11 @@ def serve():
|
|||
<head>
|
||||
<meta charset="utf8">
|
||||
<title>Subscribed Feeds</title>
|
||||
<style>
|
||||
body { margin-left: 4rem; }
|
||||
li.entry { display: inline; padding-right: 1rem; }
|
||||
li.entry:before { content: '\\2022'; padding-right: 0.5rem; }
|
||||
</style>
|
||||
</head>
|
||||
<h1>Feeds</h1>
|
||||
"""
|
||||
|
|
@ -272,17 +279,19 @@ def serve():
|
|||
ago = f" ({f.entries[0].time_ago()})"
|
||||
else:
|
||||
ago = ""
|
||||
buffer.write(f"<div class='feed'>")
|
||||
buffer.write(f'<h2><a href="{f.link}">{feed_title}</a>{ago}</h2>')
|
||||
buffer.write(f"<div>")
|
||||
if len(f.entries) > 0:
|
||||
buffer.write(f"<ul>")
|
||||
for entry in f.entries:
|
||||
title = html.escape(entry.title)
|
||||
buffer.write(
|
||||
f'<span class="entry">• <a href="{entry.link}">{title}</a> ({entry.time_ago()})</span> '
|
||||
f'<li class="entry"><a href="{entry.link}">{title}</a> ({entry.time_ago()})</li>'
|
||||
)
|
||||
buffer.write(f"</ul>")
|
||||
else:
|
||||
buffer.write("<i>No entries...</i>")
|
||||
buffer.write(f"</div>")
|
||||
buffer.write(f"</div>") # feed
|
||||
buffer.flush()
|
||||
text = buffer.getvalue()
|
||||
response = text.encode("utf-8")
|
||||
|
|
|
|||
118
cry/database.py
118
cry/database.py
|
|
@ -304,55 +304,12 @@ class Database:
|
|||
Returns the number of new entries inserted.
|
||||
"""
|
||||
with self.db:
|
||||
self.db.execute(
|
||||
"""
|
||||
INSERT INTO feeds (
|
||||
url,
|
||||
last_fetched_ts,
|
||||
retry_after_ts,
|
||||
status,
|
||||
etag,
|
||||
modified,
|
||||
title,
|
||||
link
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT DO UPDATE
|
||||
SET
|
||||
last_fetched_ts=excluded.last_fetched_ts,
|
||||
retry_after_ts=excluded.retry_after_ts,
|
||||
status=excluded.status,
|
||||
etag=excluded.etag,
|
||||
modified=excluded.modified,
|
||||
title=excluded.title,
|
||||
link=excluded.link
|
||||
""",
|
||||
[
|
||||
f.meta.url,
|
||||
f.meta.last_fetched_ts,
|
||||
f.meta.retry_after_ts,
|
||||
f.meta.status,
|
||||
f.meta.etag,
|
||||
f.meta.modified,
|
||||
f.title,
|
||||
f.link,
|
||||
],
|
||||
)
|
||||
self._insert_feed(f.meta, f.title, f.link)
|
||||
return self._insert_entries(f.meta.url, f.entries)
|
||||
|
||||
change_count = self._insert_entries(f.meta.url, f.entries)
|
||||
return change_count
|
||||
|
||||
def set_feed_status(self, url: str, status: int) -> int:
|
||||
def update_feed_status(self, meta: feed.FeedMeta, status: int) -> int:
|
||||
with self.db:
|
||||
cursor = self.db.execute(
|
||||
"""
|
||||
UPDATE feeds
|
||||
SET status = ?,
|
||||
last_fetched_ts = ?
|
||||
WHERE url = ?
|
||||
""",
|
||||
[status, int(time.time()), url],
|
||||
)
|
||||
return cursor.rowcount
|
||||
return self._update_feed_status(meta, status)
|
||||
|
||||
def redirect_feed(self, old_url: str, new_url: str):
|
||||
with self.db:
|
||||
|
|
@ -380,6 +337,7 @@ class Database:
|
|||
# bother.
|
||||
|
||||
# Mark the old feed unsubscribed.
|
||||
# TODO: Rebuild with helpers
|
||||
self.db.execute(
|
||||
"""
|
||||
UPDATE feeds
|
||||
|
|
@ -406,6 +364,59 @@ class Database:
|
|||
(prop, value),
|
||||
)
|
||||
|
||||
def _insert_feed(self, meta: feed.FeedMeta, title: str, link: str):
|
||||
"""Insert into the feeds table, handling collisions with UPSERT."""
|
||||
self.db.execute(
|
||||
"""
|
||||
INSERT INTO feeds (
|
||||
url,
|
||||
last_fetched_ts,
|
||||
retry_after_ts,
|
||||
status,
|
||||
etag,
|
||||
modified,
|
||||
title,
|
||||
link
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT DO UPDATE
|
||||
SET
|
||||
last_fetched_ts=MAX(last_fetched_ts, excluded.last_fetched_ts),
|
||||
retry_after_ts=MAX(retry_after_ts, excluded.retry_after_ts),
|
||||
-- For all other fields, take the value that was computed by the
|
||||
-- most recent fetch.
|
||||
status=CASE
|
||||
WHEN last_fetched_ts > excluded.last_fetched_ts THEN status
|
||||
ELSE excluded.status
|
||||
END,
|
||||
etag=CASE
|
||||
WHEN last_fetched_ts > excluded.last_fetched_ts THEN etag
|
||||
ELSE excluded.etag
|
||||
END,
|
||||
modified=CASE
|
||||
WHEN last_fetched_ts > excluded.last_fetched_ts THEN modified
|
||||
ELSE excluded.modified
|
||||
END,
|
||||
title=CASE
|
||||
WHEN last_fetched_ts > excluded.last_fetched_ts THEN title
|
||||
ELSE excluded.title
|
||||
END,
|
||||
link=CASE
|
||||
WHEN last_fetched_ts > excluded.last_fetched_ts THEN link
|
||||
ELSE excluded.link
|
||||
END
|
||||
""",
|
||||
[
|
||||
meta.url,
|
||||
meta.last_fetched_ts,
|
||||
meta.retry_after_ts,
|
||||
meta.status,
|
||||
meta.etag,
|
||||
meta.modified,
|
||||
title,
|
||||
link,
|
||||
],
|
||||
)
|
||||
|
||||
def _insert_entries(self, feed_url: str, entries: list[feed.Entry]) -> int:
|
||||
cursor = self.db.execute(
|
||||
"SELECT COUNT (*) FROM entries WHERE feed_url=?", [feed_url]
|
||||
|
|
@ -451,3 +462,16 @@ class Database:
|
|||
)
|
||||
end_count = cursor.fetchone()[0]
|
||||
return end_count - start_count
|
||||
|
||||
def _update_feed_status(self, meta: feed.FeedMeta, status: int) -> int:
|
||||
new_ts = max(int(time.time()), meta.last_fetched_ts + 1)
|
||||
cursor = self.db.execute(
|
||||
"""
|
||||
UPDATE feeds
|
||||
SET status = ?,
|
||||
last_fetched_ts = ?
|
||||
WHERE url = ?
|
||||
""",
|
||||
[status, new_ts, meta.url],
|
||||
)
|
||||
return cursor.rowcount
|
||||
|
|
|
|||
|
|
@ -252,16 +252,18 @@ def test_database_store_update_meta():
|
|||
assert db.load_all_meta()[0] == new_meta
|
||||
|
||||
|
||||
def test_database_set_feed_status():
|
||||
def test_database_update_feed_status():
|
||||
db = database.Database(":memory:", random_slug())
|
||||
db.ensure_database_schema()
|
||||
|
||||
db.store_feed(FEED)
|
||||
assert db.load_all_meta()[0].status != feed.FEED_STATUS_UNSUBSCRIBED
|
||||
|
||||
db.set_feed_status(FEED.meta.url, feed.FEED_STATUS_UNSUBSCRIBED)
|
||||
db.update_feed_status(
|
||||
FEED.meta,
|
||||
feed.FEED_STATUS_UNSUBSCRIBED,
|
||||
)
|
||||
|
||||
# TODO: Ensure that the updated time is touched too.
|
||||
assert db.load_all_meta()[0].status == feed.FEED_STATUS_UNSUBSCRIBED
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue