The most basic HTML UI

This commit is contained in:
John Doty 2024-07-11 10:18:40 +09:00
parent 02232c9c3e
commit fb9bfe0084
2 changed files with 73 additions and 6 deletions

View file

@ -1,5 +1,8 @@
# https://simonwillison.net/2023/Sep/30/cli-tools-python/
import asyncio
import html
import http.server
import io
import logging
import click
@ -144,12 +147,7 @@ def show(pattern, count):
db = database.Database.local()
feeds = db.load_all(feed_limit=count, pattern=pattern or "")
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, reverse=True)
feeds.sort(key=feed.sort_key, reverse=True)
for f in feeds:
click.echo(f"{f.title}")
if len(f.entries) > 0:
@ -193,3 +191,44 @@ def unsubscribe(url):
if count == 0:
click.echo(f"Not subscribed to feed {url}")
return 1
@cli.command("serve")
def serve():
class Handler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
db = database.Database.local()
feeds = db.load_all(feed_limit=10)
feeds.sort(key=feed.sort_key, reverse=True)
buffer = io.StringIO()
buffer.write("<head>")
buffer.write('<meta charset="utf-8"><title>Subscribed Feeds</title>')
buffer.write("</head>")
buffer.write("<h1>Feeds</h1>")
for f in feeds:
feed_title = html.escape(f.title)
buffer.write(f'<h2><a href="{f.link}">{feed_title}</a></h2>')
buffer.write(f"<div>")
if len(f.entries) > 0:
for entry in f.entries:
title = html.escape(entry.title)
buffer.write(
f'<span>&bull; <a href="{entry.link}">{title}</a> ({entry.time_ago()})</span> '
)
else:
buffer.write("<i>No entries...</i>")
buffer.write(f"</div>")
buffer.flush()
text = buffer.getvalue()
response = text.encode("utf-8")
self.send_response(200)
self.send_header("content-type", "text/html")
self.send_header("content-length", str(len(response)))
self.end_headers()
self.wfile.write(response)
with http.server.HTTPServer(("", 8000), Handler) as server:
click.echo("Serving at http://127.0.0.1:8000/")
server.serve_forever()

View file

@ -294,6 +294,27 @@ class Entry:
title = clean_text(str(title))
return Entry(id=id, inserted_at=insert_time, title=title, link=link)
def time_ago(self) -> str:
inserted = self.inserted_at / 1000
seconds = int(time.time()) - inserted
if seconds <= 90:
return f"{seconds}s"
minutes = int(seconds / 60)
if minutes <= 90:
return f"{minutes}m"
hours = int(minutes / 60)
if hours < 24:
return f"{hours}h"
days = int(hours / 24)
if days <= 7:
return f"{days}d"
weeks = int(days / 7)
if weeks < 52:
return f"{weeks}w"
years = int(weeks / 52)
return f"{years}y"
@dataclasses.dataclass(frozen=True)
class Feed:
@ -402,3 +423,10 @@ def merge_feeds(a: Feed, b: Feed) -> Feed:
link=source_feed.link,
entries=entries,
)
def sort_key(f: Feed) -> int:
"""A sort key for sorting feeds by recency."""
if len(f.entries) > 0:
return max(e.inserted_at for e in f.entries)
return -1