From 5b7122a2a1533c433659d18e1a668b1938aa36b7 Mon Sep 17 00:00:00 2001 From: Bretton Date: Sun, 16 Nov 2025 15:49:35 -0800 Subject: [PATCH] feat(aggregator): add source name field and improve formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add source_name field to Perspective model for better attribution - Extract source name from HTML anchor tags in parser - Display source name in rich text (e.g., "The Straits Times" vs generic "Source") - Improve spacing in highlights, perspectives, and sources lists (double newlines) - Better visual separation between list items 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- aggregators/kagi-news/src/html_parser.py | 9 ++++++--- aggregators/kagi-news/src/models.py | 1 + .../kagi-news/src/richtext_formatter.py | 18 +++++++++++------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/aggregators/kagi-news/src/html_parser.py b/aggregators/kagi-news/src/html_parser.py index 9d04b22..7ddc60e 100644 --- a/aggregators/kagi-news/src/html_parser.py +++ b/aggregators/kagi-news/src/html_parser.py @@ -78,7 +78,8 @@ class KagiHTMLParser: Perspective( actor=p['actor'], description=p['description'], - source_url=p['source_url'] + source_url=p['source_url'], + source_name=p.get('source_name', '') ) for p in parsed['perspectives'] ] @@ -230,9 +231,10 @@ class KagiHTMLParser: actor, rest = full_text.split(':', 1) actor = actor.strip() - # Find the tag for source URL + # Find the tag for source URL and name a_tag = li.find('a') source_url = a_tag['href'] if a_tag and a_tag.get('href') else "" + source_name = a_tag.get_text(strip=True) if a_tag else "" # Extract description (between colon and source link) # Remove the source citation part in parentheses @@ -250,7 +252,8 @@ class KagiHTMLParser: return { 'actor': actor, 'description': description, - 'source_url': source_url + 'source_url': source_url, + 'source_name': source_name } def _extract_sources(self, soup: BeautifulSoup) -> List[Dict]: diff --git a/aggregators/kagi-news/src/models.py b/aggregators/kagi-news/src/models.py index f3806a1..c8b3d39 100644 --- a/aggregators/kagi-news/src/models.py +++ b/aggregators/kagi-news/src/models.py @@ -20,6 +20,7 @@ class Perspective: actor: str description: str source_url: str + source_name: str = "" # Name of the source (e.g., "The Straits Times") @dataclass diff --git a/aggregators/kagi-news/src/richtext_formatter.py b/aggregators/kagi-news/src/richtext_formatter.py index 4e24f65..504d796 100644 --- a/aggregators/kagi-news/src/richtext_formatter.py +++ b/aggregators/kagi-news/src/richtext_formatter.py @@ -42,7 +42,7 @@ class RichTextFormatter: builder.add_bold("Highlights:") builder.add_text("\n") for highlight in story.highlights: - builder.add_text(f"• {highlight}\n") + builder.add_text(f"• {highlight}\n\n") builder.add_text("\n") # Perspectives (if present) @@ -53,12 +53,16 @@ class RichTextFormatter: # Bold the actor name actor_with_colon = f"{perspective.actor}:" builder.add_bold(actor_with_colon) - builder.add_text(f" {perspective.description} (") + builder.add_text(f" {perspective.description}") - # Add link to source - source_link_text = "Source" - builder.add_link(source_link_text, perspective.source_url) - builder.add_text(")\n") + # Add link to source if available + if perspective.source_url: + builder.add_text(" (") + source_link_text = perspective.source_name if perspective.source_name else "Source" + builder.add_link(source_link_text, perspective.source_url) + builder.add_text(")") + + builder.add_text("\n\n") builder.add_text("\n") # Quote (if present) @@ -74,7 +78,7 @@ class RichTextFormatter: for source in story.sources: builder.add_text("• ") builder.add_link(source.title, source.url) - builder.add_text(f" - {source.domain}\n") + builder.add_text(f" - {source.domain}\n\n") builder.add_text("\n") # Kagi News attribution -- 2.51.2