This commit is contained in:
2025-11-12 22:33:56 +01:00
parent 95669fd211
commit 45df834d5b
12 changed files with 1172 additions and 240 deletions

View File

@@ -162,6 +162,7 @@ def get_latest_articles(max_articles=10, hours=24):
'link': doc.get('link', ''),
'summary': cluster.get('neutral_summary', doc.get('summary', '')),
'source': doc.get('source', ''),
'category': doc.get('category', 'general'),
'published_at': doc.get('published_at', ''),
'is_clustered': True,
'sources': sources,
@@ -177,6 +178,7 @@ def get_latest_articles(max_articles=10, hours=24):
'link': doc.get('link', ''),
'summary': doc.get('summary', ''),
'source': doc.get('source', ''),
'category': doc.get('category', 'general'),
'published_at': doc.get('published_at', ''),
'is_clustered': False
})
@@ -190,6 +192,7 @@ def get_latest_articles(max_articles=10, hours=24):
'link': doc.get('link', ''),
'summary': doc.get('summary', ''),
'source': doc.get('source', ''),
'category': doc.get('category', 'general'),
'published_at': doc.get('published_at', ''),
'is_clustered': False
})
@@ -206,22 +209,29 @@ def get_latest_articles(max_articles=10, hours=24):
def get_active_subscribers():
"""
Get all active subscribers from database
Get all active subscribers from database with their category preferences
Returns:
list: Email addresses of active subscribers
list: Subscriber dictionaries with email and categories
"""
cursor = subscribers_collection.find({'status': 'active'})
return [doc['email'] for doc in cursor]
subscribers = []
for doc in cursor:
subscribers.append({
'email': doc['email'],
'categories': doc.get('categories', None) # None means all categories
})
return subscribers
def render_newsletter_html(articles, tracking_enabled=False, pixel_tracking_id=None,
link_tracking_map=None, api_url=None):
def render_newsletter_html(articles, subscriber_categories=None, tracking_enabled=False,
pixel_tracking_id=None, link_tracking_map=None, api_url=None):
"""
Render newsletter HTML from template with optional tracking integration
Args:
articles: List of article dictionaries
subscriber_categories: List of categories the subscriber wants (None = all)
tracking_enabled: Whether to inject tracking pixel and replace links
pixel_tracking_id: Tracking ID for the email open pixel
link_tracking_map: Dictionary mapping original URLs to tracking IDs
@@ -237,10 +247,39 @@ def render_newsletter_html(articles, tracking_enabled=False, pixel_tracking_id=N
template = Template(template_content)
# Split articles into sections
# Top 3 are "trending", rest are "other articles"
trending_articles = articles[:3] if len(articles) >= 3 else articles
other_articles = articles[3:] if len(articles) > 3 else []
# Filter articles by subscriber's category preferences
if subscriber_categories:
filtered_articles = [a for a in articles if a.get('category', 'general') in subscriber_categories]
else:
filtered_articles = articles
# Group articles by category (max 3 per category)
from collections import defaultdict
articles_by_category = defaultdict(list)
for article in filtered_articles:
category = article.get('category', 'general')
if len(articles_by_category[category]) < 3:
articles_by_category[category].append(article)
# Convert to list of category sections
category_sections = []
category_names = {
'general': {'name': 'Top Trending', 'icon': '🔥'},
'local': {'name': 'Local Events', 'icon': '🏛️'},
'sports': {'name': 'Sports', 'icon': ''},
'science': {'name': 'Science & Tech', 'icon': '🔬'}
}
for category, category_articles in sorted(articles_by_category.items()):
if category_articles:
cat_info = category_names.get(category, {'name': category.title(), 'icon': '📄'})
category_sections.append({
'id': category,
'name': cat_info['name'],
'icon': cat_info['icon'],
'articles': category_articles
})
# Get weather data
from weather_service import get_munich_weather
@@ -248,13 +287,14 @@ def render_newsletter_html(articles, tracking_enabled=False, pixel_tracking_id=N
# Prepare template data
now = datetime.now()
total_articles = sum(len(section['articles']) for section in category_sections)
template_data = {
'date': now.strftime('%A, %B %d, %Y'),
'year': now.year,
'article_count': len(articles),
'trending_articles': trending_articles,
'other_articles': other_articles,
'article_count': total_articles,
'category_sections': category_sections,
'unsubscribe_link': f'{Config.WEBSITE_URL}/unsubscribe',
'preferences_link': f'{Config.WEBSITE_URL}/preferences.html',
'website_link': Config.WEBSITE_URL,
'tracking_enabled': tracking_enabled,
'weather': weather
@@ -358,7 +398,8 @@ def send_newsletter(max_articles=None, test_email=None):
# Get subscribers
if test_email:
subscribers = [test_email]
# For test mode, send with all categories
subscribers = [{'email': test_email, 'categories': None}]
print(f"\n🧪 Test mode: Sending to {test_email} only")
else:
print("\nFetching active subscribers...")
@@ -386,7 +427,10 @@ def send_newsletter(max_articles=None, test_email=None):
failed_count = 0
errors = []
for i, email in enumerate(subscribers, 1):
for i, subscriber in enumerate(subscribers, 1):
email = subscriber['email']
categories = subscriber['categories']
print(f"[{i}/{len(subscribers)}] Sending to {email}...", end=' ')
# Generate tracking data for this subscriber if tracking is enabled
@@ -399,9 +443,10 @@ def send_newsletter(max_articles=None, test_email=None):
tracking_service=tracking_service
)
# Render newsletter with tracking
# Render newsletter with tracking and subscriber's category preferences
html_content = render_newsletter_html(
articles=articles,
subscriber_categories=categories,
tracking_enabled=True,
pixel_tracking_id=tracking_data['pixel_tracking_id'],
link_tracking_map=tracking_data['link_tracking_map'],
@@ -410,10 +455,10 @@ def send_newsletter(max_articles=None, test_email=None):
except Exception as e:
print(f"⚠ Tracking error: {e}, sending without tracking...", end=' ')
# Fallback: send without tracking
html_content = render_newsletter_html(articles)
html_content = render_newsletter_html(articles, subscriber_categories=categories)
else:
# Render newsletter without tracking
html_content = render_newsletter_html(articles)
# Render newsletter without tracking but with subscriber's preferences
html_content = render_newsletter_html(articles, subscriber_categories=categories)
# Send email
success, error = send_email(email, subject, html_content)