240 lines
6.3 KiB
Python
240 lines
6.3 KiB
Python
"""
|
|
User Interest Profile API routes for Munich News Daily.
|
|
Provides endpoints to view and manage user interest profiles.
|
|
"""
|
|
|
|
from flask import Blueprint, request, jsonify
|
|
from services.interest_profiling_service import (
|
|
get_user_interests,
|
|
get_top_interests,
|
|
build_interests_from_history,
|
|
decay_user_interests,
|
|
get_interest_statistics,
|
|
delete_user_interests
|
|
)
|
|
|
|
interests_bp = Blueprint('interests', __name__)
|
|
|
|
|
|
@interests_bp.route('/api/interests/<email>', methods=['GET'])
|
|
def get_interests(email):
|
|
"""
|
|
Get user interest profile.
|
|
|
|
Args:
|
|
email: Email address of the user
|
|
|
|
Returns:
|
|
JSON response with user interest profile
|
|
"""
|
|
try:
|
|
profile = get_user_interests(email)
|
|
|
|
if not profile:
|
|
return jsonify({
|
|
'success': False,
|
|
'error': 'User profile not found'
|
|
}), 404
|
|
|
|
# Remove MongoDB _id field
|
|
if '_id' in profile:
|
|
del profile['_id']
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'profile': profile
|
|
}), 200
|
|
|
|
except Exception as e:
|
|
return jsonify({
|
|
'success': False,
|
|
'error': str(e)
|
|
}), 500
|
|
|
|
|
|
@interests_bp.route('/api/interests/<email>/top', methods=['GET'])
|
|
def get_top_user_interests(email):
|
|
"""
|
|
Get user's top interests sorted by score.
|
|
|
|
Query parameters:
|
|
top_n: Number of top interests to return (default: 10)
|
|
|
|
Args:
|
|
email: Email address of the user
|
|
|
|
Returns:
|
|
JSON response with top categories and keywords
|
|
"""
|
|
try:
|
|
top_n = request.args.get('top_n', 10, type=int)
|
|
|
|
top_interests = get_top_interests(email, top_n)
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'email': email,
|
|
'top_categories': [
|
|
{'category': cat, 'score': score}
|
|
for cat, score in top_interests['top_categories']
|
|
],
|
|
'top_keywords': [
|
|
{'keyword': kw, 'score': score}
|
|
for kw, score in top_interests['top_keywords']
|
|
]
|
|
}), 200
|
|
|
|
except Exception as e:
|
|
return jsonify({
|
|
'success': False,
|
|
'error': str(e)
|
|
}), 500
|
|
|
|
|
|
@interests_bp.route('/api/interests/<email>/rebuild', methods=['POST'])
|
|
def rebuild_interests(email):
|
|
"""
|
|
Rebuild user interest profile from click history.
|
|
|
|
Request body (optional):
|
|
{
|
|
"days_lookback": 30 // Number of days of history to analyze
|
|
}
|
|
|
|
Args:
|
|
email: Email address of the user
|
|
|
|
Returns:
|
|
JSON response with rebuilt profile
|
|
"""
|
|
try:
|
|
data = request.get_json() or {}
|
|
days_lookback = data.get('days_lookback', 30)
|
|
|
|
# Validate days_lookback
|
|
if not isinstance(days_lookback, int) or days_lookback < 1:
|
|
return jsonify({
|
|
'success': False,
|
|
'error': 'days_lookback must be a positive integer'
|
|
}), 400
|
|
|
|
profile = build_interests_from_history(email, days_lookback)
|
|
|
|
# Remove MongoDB _id field
|
|
if '_id' in profile:
|
|
del profile['_id']
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'message': f'Profile rebuilt from {days_lookback} days of history',
|
|
'profile': profile
|
|
}), 200
|
|
|
|
except Exception as e:
|
|
return jsonify({
|
|
'success': False,
|
|
'error': str(e)
|
|
}), 500
|
|
|
|
|
|
@interests_bp.route('/api/interests/decay', methods=['POST'])
|
|
def decay_interests():
|
|
"""
|
|
Decay interest scores for inactive users.
|
|
|
|
Request body (optional):
|
|
{
|
|
"decay_factor": 0.95, // Multiplier for scores (default: 0.95)
|
|
"days_threshold": 7 // Only decay profiles older than N days
|
|
}
|
|
|
|
Returns:
|
|
JSON response with decay statistics
|
|
"""
|
|
try:
|
|
data = request.get_json() or {}
|
|
decay_factor = data.get('decay_factor', 0.95)
|
|
days_threshold = data.get('days_threshold', 7)
|
|
|
|
# Validate parameters
|
|
if not isinstance(decay_factor, (int, float)) or decay_factor <= 0 or decay_factor > 1:
|
|
return jsonify({
|
|
'success': False,
|
|
'error': 'decay_factor must be between 0 and 1'
|
|
}), 400
|
|
|
|
if not isinstance(days_threshold, int) or days_threshold < 1:
|
|
return jsonify({
|
|
'success': False,
|
|
'error': 'days_threshold must be a positive integer'
|
|
}), 400
|
|
|
|
result = decay_user_interests(decay_factor, days_threshold)
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'message': f'Decayed interests for profiles older than {days_threshold} days',
|
|
'statistics': result
|
|
}), 200
|
|
|
|
except Exception as e:
|
|
return jsonify({
|
|
'success': False,
|
|
'error': str(e)
|
|
}), 500
|
|
|
|
|
|
@interests_bp.route('/api/interests/statistics', methods=['GET'])
|
|
def get_statistics():
|
|
"""
|
|
Get statistics about user interests across all users.
|
|
|
|
Returns:
|
|
JSON response with interest statistics
|
|
"""
|
|
try:
|
|
stats = get_interest_statistics()
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'statistics': stats
|
|
}), 200
|
|
|
|
except Exception as e:
|
|
return jsonify({
|
|
'success': False,
|
|
'error': str(e)
|
|
}), 500
|
|
|
|
|
|
@interests_bp.route('/api/interests/<email>', methods=['DELETE'])
|
|
def delete_interests(email):
|
|
"""
|
|
Delete user interest profile (GDPR compliance).
|
|
|
|
Args:
|
|
email: Email address of the user
|
|
|
|
Returns:
|
|
JSON response with confirmation
|
|
"""
|
|
try:
|
|
deleted = delete_user_interests(email)
|
|
|
|
if not deleted:
|
|
return jsonify({
|
|
'success': False,
|
|
'error': 'User profile not found'
|
|
}), 404
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'message': f'Interest profile deleted for {email}'
|
|
}), 200
|
|
|
|
except Exception as e:
|
|
return jsonify({
|
|
'success': False,
|
|
'error': str(e)
|
|
}), 500
|