This commit is contained in:
2025-11-18 14:45:41 +01:00
parent 2e80d64ff6
commit 84fce9a82c
19 changed files with 2437 additions and 3 deletions

View File

@@ -0,0 +1,135 @@
"""
Personalization API routes for Munich News Daily.
Provides endpoints to test and preview personalized content.
"""
from flask import Blueprint, request, jsonify
from datetime import datetime, timedelta
from database import articles_collection
from services.personalization_service import (
rank_articles_for_user,
select_personalized_articles,
get_personalization_explanation,
get_personalization_stats
)
personalization_bp = Blueprint('personalization', __name__)
@personalization_bp.route('/api/personalize/preview/<email>', methods=['GET'])
def preview_personalized_newsletter(email):
"""
Preview personalized newsletter for a user.
Query parameters:
max_articles: Maximum articles to return (default: 10)
hours_lookback: Hours of articles to consider (default: 24)
Returns:
JSON with personalized article selection and statistics
"""
try:
max_articles = request.args.get('max_articles', 10, type=int)
hours_lookback = request.args.get('hours_lookback', 24, type=int)
# Get recent articles
cutoff_date = datetime.utcnow() - timedelta(hours=hours_lookback)
articles = list(articles_collection.find({
'created_at': {'$gte': cutoff_date},
'summary': {'$exists': True, '$ne': None}
}).sort('created_at', -1))
# Select personalized articles
personalized = select_personalized_articles(
articles,
email,
max_articles=max_articles
)
# Get statistics
stats = get_personalization_stats(personalized, email)
# Format response
articles_response = []
for article in personalized:
articles_response.append({
'title': article.get('title', ''),
'title_en': article.get('title_en'),
'summary': article.get('summary', ''),
'link': article.get('link', ''),
'category': article.get('category', 'general'),
'keywords': article.get('keywords', []),
'personalization_score': article.get('personalization_score', 0.0),
'published_at': article.get('published_at', '')
})
return jsonify({
'success': True,
'email': email,
'articles': articles_response,
'statistics': stats
}), 200
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
@personalization_bp.route('/api/personalize/explain', methods=['POST'])
def explain_recommendation():
"""
Explain why an article was recommended to a user.
Request body:
{
"email": "user@example.com",
"article_id": "article-id-here"
}
Returns:
JSON with explanation of recommendation
"""
try:
data = request.get_json()
if not data or 'email' not in data or 'article_id' not in data:
return jsonify({
'success': False,
'error': 'email and article_id required'
}), 400
email = data['email']
article_id = data['article_id']
# Get article
from bson import ObjectId
article = articles_collection.find_one({'_id': ObjectId(article_id)})
if not article:
return jsonify({
'success': False,
'error': 'Article not found'
}), 404
# Get user interests
from services.interest_profiling_service import get_user_interests
user_interests = get_user_interests(email)
# Generate explanation
explanation = get_personalization_explanation(article, user_interests)
return jsonify({
'success': True,
'email': email,
'article_title': article.get('title', ''),
'explanation': explanation
}), 200
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500