retronews

a featureful fork of the luke8086/retronews hn+lobste.rs tui
Log | Files | Refs | README | LICENSE

commit 4d87c2dde87223e06874f213fb078253ca282619
parent 39a6af7e1eb2be1e689a783802452da1caad67ee
Author: luke8086 <55237178+luke8086@users.noreply.github.com>
Date:   Wed, 27 Jul 2022 20:54:30 +0000

Rename stories to threads

Diffstat:
Mretronews.py | 106++++++++++++++++++++++++++++++++++++++++----------------------------------------
1 file changed, 53 insertions(+), 53 deletions(-)

diff --git a/retronews.py b/retronews.py @@ -76,7 +76,7 @@ class MessageFlags: @dataclasses.dataclass class Message: msg_id: str - story_id: str + thread_id: str content_location: str date: datetime author: str @@ -305,8 +305,8 @@ def cmd_open(app: AppState) -> None: if (msg := app.selected_message) is None: return - if msg.msg_id == msg.story_id: - app_open_story(app, msg) + if msg.msg_id == msg.thread_id: + app_open_thread(app, msg) else: app_select_message(app, msg, show_pager=True) @@ -315,7 +315,7 @@ def cmd_close(app: AppState) -> None: if app.pager_visible: app.pager_visible = False else: - app_close_story(app) + app_close_thread(app) def cmd_star(app: AppState) -> None: @@ -371,7 +371,7 @@ def db_init() -> sqlite3.Connection: create_table_sql = """ CREATE TABLE IF NOT EXISTS messages ( id TEXT NOT NULL PRIMARY KEY, - story_id TEXT NOT NULL, + thread_id TEXT NOT NULL, flags JSON NOT NULL) """ @@ -384,9 +384,9 @@ def db_init() -> sqlite3.Connection: def db_save_message(db: sqlite3.Connection, message: Message) -> None: - sql = """INSERT OR REPLACE INTO messages (id, story_id, flags) VALUES (?, ?, ?)""" + sql = """INSERT OR REPLACE INTO messages (id, thread_id, flags) VALUES (?, ?, ?)""" flags_json = json.dumps(dataclasses.asdict(message.flags)) - db.execute(sql, (message.msg_id, message.story_id, flags_json)) + db.execute(sql, (message.msg_id, message.thread_id, flags_json)) db.commit() @@ -400,18 +400,18 @@ def db_load_message_flags(db: sqlite3.Connection, messages_by_id: Dict[str, Mess def db_load_read_comments(db: sqlite3.Connection, messages_by_id: Dict[str, Message]) -> None: - stories_by_id = {msg.msg_id: msg for msg in messages_by_id.values() if msg.msg_id == msg.story_id} - story_ids = list(stories_by_id.keys()) + threads_by_id = {msg.msg_id: msg for msg in messages_by_id.values() if msg.msg_id == msg.thread_id} + thread_ids = list(threads_by_id.keys()) sql = f""" - SELECT story_id, COUNT(*) AS count + SELECT thread_id, COUNT(*) AS count FROM messages - WHERE story_id IN ({','.join('?' for _ in story_ids)}) AND JSON_EXTRACT(flags, '$.read') - GROUP BY story_id + WHERE thread_id IN ({','.join('?' for _ in thread_ids)}) AND JSON_EXTRACT(flags, '$.read') + GROUP BY thread_id """ - for row in db.execute(sql, story_ids): - stories_by_id[row["story_id"]].read_comments = row["count"] + for row in db.execute(sql, thread_ids): + threads_by_id[row["thread_id"]].read_comments = row["count"] def app_safe_run(app: AppState, fn: Callable[[], T], flash: Optional[str]) -> Optional[T]: @@ -451,7 +451,7 @@ def app_select_message(app: AppState, message: Optional[Message], show_pager: bo if app.pager_visible: message.flags.read = True db_save_message(app.db, message) - db_load_read_comments(app.db, {message.story_id: app.messages_by_id[message.story_id]}) + db_load_read_comments(app.db, {message.thread_id: app.messages_by_id[message.thread_id]}) app.pager_offset = 0 @@ -483,41 +483,41 @@ def app_load_messages( def app_fetch_stories(app: AppState) -> None: - fn = partial(group_search_stories, app.group) + fn = partial(group_search_threads, app.group) flash = f"Fetching stories from '{app.group.label}' (page {app.group.page})..." if (messages := app_safe_run(app, fn, flash=flash)) is not None: app_load_messages(app, messages) -def app_close_story(app: AppState) -> None: - selected_story_id = app.selected_message.story_id if app.selected_message else None - filtered_messages = [msg for msg in app.messages if msg.msg_id == msg.story_id] +def app_close_thread(app: AppState) -> None: + selected_thread_id = app.selected_message.thread_id if app.selected_message else None + filtered_messages = [msg for msg in app.messages if msg.msg_id == msg.thread_id] for msg in filtered_messages: msg.children = [] - app_load_messages(app, filtered_messages, selected_message_id=selected_story_id) + app_load_messages(app, filtered_messages, selected_message_id=selected_thread_id) -def app_open_story(app: AppState, story_message: Message) -> None: - fn = partial(group_fetch_story, story_message.story_id) - flash = f"Fetching story '{story_message.story_id}'..." +def app_open_thread(app: AppState, thread_message: Message) -> None: + fn = partial(group_fetch_thread, thread_message.thread_id) + flash = f"Fetching thread '{thread_message.thread_id}'..." - if (new_story_message := app_safe_run(app, fn, flash=flash)) is None: + if (new_thread_message := app_safe_run(app, fn, flash=flash)) is None: return - app_close_story(app) + app_close_thread(app) - index_pos = story_message.index_position - story_messages = list(app_flatten_story(new_story_message, prefix="", is_last_child=False, is_top=True)) - new_story_message.total_comments = len(story_messages) - messages = app.messages[:index_pos] + story_messages + app.messages[index_pos + 1 :] # noqa: E203 + index_pos = thread_message.index_position + thread_messages = list(app_flatten_thread(new_thread_message, prefix="", is_last_child=False, is_top=True)) + new_thread_message.total_comments = len(thread_messages) + messages = app.messages[:index_pos] + thread_messages + app.messages[index_pos + 1 :] # noqa: E203 - app_load_messages(app, messages, selected_message_id=story_message.msg_id, show_pager=True) + app_load_messages(app, messages, selected_message_id=thread_message.msg_id, show_pager=True) -def app_flatten_story(msg: Message, prefix, is_last_child, is_top=False) -> Generator[Message, None, None]: +def app_flatten_thread(msg: Message, prefix, is_last_child, is_top=False) -> Generator[Message, None, None]: msg.index_tree = "" if is_top else f"{prefix}{'└─' if is_last_child else '├─'}> " yield msg @@ -525,7 +525,7 @@ def app_flatten_story(msg: Message, prefix, is_last_child, is_top=False) -> Gene for i, child_node in enumerate(msg.children): tree_prefix = "" if is_top else f"{prefix}{' ' if is_last_child else '│ '}" - for child in app_flatten_story(child_node, tree_prefix, i == children_count - 1): + for child in app_flatten_thread(child_node, tree_prefix, i == children_count - 1): yield child @@ -580,12 +580,12 @@ def app_render_index_row(app: AppState, row: int, message: Message) -> None: cols = app.layout.cols date = message.date.strftime("%Y-%m-%d %H:%M") author = message.author[:10].ljust(10) - is_response = message.title.startswith("Re:") and message.msg_id != message.story_id + is_response = message.title.startswith("Re:") and message.msg_id != message.thread_id title = "" if is_response and row > app.layout.index_start else message.title unread = ( str(max(min(message.total_comments - message.read_comments, 9999), 0)).rjust(4) - if message.msg_id == message.story_id + if message.msg_id == message.thread_id else " " ) @@ -632,12 +632,12 @@ def app_render_middle_menu(app: AppState) -> None: if (message := app.selected_message) is None: return - if (story_message := app.messages_by_id.get(message.story_id)) is None: + if (thread_message := app.messages_by_id.get(message.thread_id)) is None: return cols = app.layout.cols - total = story_message.total_comments - unread = total - story_message.read_comments + total = thread_message.total_comments + unread = total - thread_message.read_comments text = f"--({unread}/{total} unread)--"[:cols].ljust(cols, "-") @@ -735,7 +735,7 @@ def msg_build_lines(msg: Message) -> List[str]: def hn_parse_search_hit(hit: HNSearchHit) -> Message: return Message( msg_id=f"{hit['objectID']}@hn", - story_id=f"{hit['objectID']}@hn", + thread_id=f"{hit['objectID']}@hn", content_location=f"https://news.ycombinator.com/item?id={hit['objectID']}", date=datetime.fromtimestamp(hit["created_at_i"]), author=hit["author"], @@ -744,45 +744,45 @@ def hn_parse_search_hit(hit: HNSearchHit) -> Message: ) -def hn_parse_entry(entry: HNEntry, story_id: str = "", parent_title: str = "") -> Message: - story_id = story_id or str(entry["id"]) +def hn_parse_entry(entry: HNEntry, thread_id: str = "", parent_title: str = "") -> Message: + thread_id = thread_id or str(entry["id"]) body = f"<p>{entry['url']}</p>" if entry["url"] else "" body = f"{body}{entry['text']}" if entry["text"] else body return Message( msg_id=f"{entry['id']}@hn", - story_id=f"{story_id}@hn", + thread_id=f"{thread_id}@hn", content_location=f"https://news.ycombinator.com/item?id={entry['id']}", date=datetime.fromtimestamp(entry["created_at_i"]), author=entry["author"] or "unknown", title=entry["title"] or f"Re: {parent_title}", body=body, - children=[hn_parse_entry(child, story_id, entry["title"] or parent_title) for child in entry["children"]], + children=[hn_parse_entry(child, thread_id, entry["title"] or parent_title) for child in entry["children"]], ) -def hn_search_stories(group: str = "news", page: int = 1) -> List[Message]: +def hn_search_threads(group: str = "news", page: int = 1) -> List[Message]: rex = re.compile(r'href="item\?id=(\d+)"') html = fetch(f"https://news.ycombinator.com/{group}?p={page}") - story_ids = set(match.group(1) for match in rex.finditer(html)) + thread_ids = set(match.group(1) for match in rex.finditer(html)) - story_tags = ",".join(f"story_{x}" for x in story_ids) + story_tags = ",".join(f"story_{x}" for x in thread_ids) url = f"https://hn.algolia.com/api/v1/search_by_date?hitsPerPage=200&tags=story,({story_tags})" hits = json.loads(fetch(url))["hits"] return [hn_parse_search_hit(hit) for hit in hits] -def hn_search_new_stories(_: str, page: int = 1) -> List[Message]: +def hn_search_new_threads(_: str, page: int = 1) -> List[Message]: url = f"https://hn.algolia.com/api/v1/search_by_date?tags=story&hitsPerPage=30&page={page}" hits = json.loads(fetch(url))["hits"] return [hn_parse_search_hit(hit) for hit in hits] -def hn_fetch_story(entry_id: Union[str, int]) -> Message: +def hn_fetch_thread(entry_id: Union[str, int]) -> Message: resp = fetch(f"http://hn.algolia.com/api/v1/items/{entry_id}") entry: HNEntry = json.loads(resp) return hn_parse_entry(entry) @@ -792,18 +792,18 @@ def group_advance_page(group: Group, offset: int = 1) -> Group: return dataclasses.replace(group, page=max(0, group.page + offset)) -def group_search_stories(group: Group) -> List[Message]: +def group_search_threads(group: Group) -> List[Message]: searchers: Dict[str, Callable[[str, int], List[Message]]] = { - "hn": hn_search_stories, - "hn-new": hn_search_new_stories, + "hn": hn_search_threads, + "hn-new": hn_search_new_threads, } searcher = searchers[group.provider] return searcher(group.name, group.page) -def group_fetch_story(story_id: str) -> Message: - (source_id, provider) = story_id.split("@") - return {"hn": hn_fetch_story}[provider](source_id) +def group_fetch_thread(thread_id: str) -> Message: + (source_id, provider) = thread_id.split("@") + return {"hn": hn_fetch_thread}[provider](source_id) def main(screen: "curses._CursesWindow") -> None: