|
|
from flask import Blueprint, request, jsonify, current_app |
|
|
from flask_jwt_extended import jwt_required, get_jwt_identity |
|
|
from backend.services.content_service import ContentService |
|
|
import pandas as pd |
|
|
|
|
|
sources_bp = Blueprint('sources', __name__) |
|
|
|
|
|
@sources_bp.route('/', methods=['OPTIONS']) |
|
|
@sources_bp.route('', methods=['OPTIONS']) |
|
|
def handle_options(): |
|
|
"""Handle OPTIONS requests for preflight CORS checks.""" |
|
|
return '', 200 |
|
|
|
|
|
@sources_bp.route('/', methods=['GET']) |
|
|
@sources_bp.route('', methods=['GET']) |
|
|
@jwt_required() |
|
|
def get_sources(): |
|
|
""" |
|
|
Get all sources for the current user. |
|
|
|
|
|
Returns: |
|
|
JSON: List of sources |
|
|
""" |
|
|
try: |
|
|
user_id = get_jwt_identity() |
|
|
|
|
|
|
|
|
if not hasattr(current_app, 'supabase') or current_app.supabase is None: |
|
|
|
|
|
response_data = jsonify({ |
|
|
'success': False, |
|
|
'message': 'Database connection not initialized' |
|
|
}) |
|
|
response_data.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000') |
|
|
response_data.headers.add('Access-Control-Allow-Credentials', 'true') |
|
|
return response_data, 500 |
|
|
|
|
|
|
|
|
response = ( |
|
|
current_app.supabase |
|
|
.table("Source") |
|
|
.select("*") |
|
|
.eq("user_id", user_id) |
|
|
.execute() |
|
|
) |
|
|
|
|
|
sources = response.data if response.data else [] |
|
|
|
|
|
|
|
|
response_data = jsonify({ |
|
|
'success': True, |
|
|
'sources': sources |
|
|
}) |
|
|
response_data.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000') |
|
|
response_data.headers.add('Access-Control-Allow-Credentials', 'true') |
|
|
return response_data, 200 |
|
|
|
|
|
except Exception as e: |
|
|
current_app.logger.error(f"Get sources error: {str(e)}") |
|
|
|
|
|
response_data = jsonify({ |
|
|
'success': False, |
|
|
'message': 'An error occurred while fetching sources' |
|
|
}) |
|
|
response_data.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000') |
|
|
response_data.headers.add('Access-Control-Allow-Credentials', 'true') |
|
|
return response_data, 500 |
|
|
|
|
|
@sources_bp.route('/', methods=['POST']) |
|
|
@sources_bp.route('', methods=['POST']) |
|
|
@jwt_required() |
|
|
def add_source(): |
|
|
""" |
|
|
Add a new source for the current user. |
|
|
|
|
|
Request Body: |
|
|
source (str): Source URL |
|
|
|
|
|
Returns: |
|
|
JSON: Add source result |
|
|
""" |
|
|
try: |
|
|
user_id = get_jwt_identity() |
|
|
data = request.get_json() |
|
|
|
|
|
|
|
|
if not data or 'source' not in data: |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'message': 'Source URL is required' |
|
|
}), 400 |
|
|
|
|
|
source_url = data['source'] |
|
|
|
|
|
|
|
|
try: |
|
|
content_service = ContentService() |
|
|
result = content_service.add_rss_source(source_url, user_id) |
|
|
|
|
|
return jsonify({ |
|
|
'success': True, |
|
|
'message': result |
|
|
}), 201 |
|
|
except Exception as e: |
|
|
|
|
|
current_app.logger.warning(f"Content service failed, storing in database directly: {str(e)}") |
|
|
|
|
|
|
|
|
|
|
|
response = ( |
|
|
current_app.supabase |
|
|
.table("Source") |
|
|
.insert({ |
|
|
"source": source_url, |
|
|
"user_id": user_id, |
|
|
"created_at": "now()" |
|
|
}) |
|
|
.execute() |
|
|
) |
|
|
|
|
|
if response.data: |
|
|
|
|
|
return jsonify({ |
|
|
'success': True, |
|
|
'source': response.data[0] |
|
|
}), 201 |
|
|
else: |
|
|
raise Exception("Failed to store source in database") |
|
|
|
|
|
except Exception as e: |
|
|
current_app.logger.error(f"Add source error: {str(e)}") |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'message': f'An error occurred while adding source: {str(e)}' |
|
|
}), 500 |
|
|
|
|
|
@sources_bp.route('/<source_id>', methods=['OPTIONS']) |
|
|
def handle_source_options(source_id): |
|
|
"""Handle OPTIONS requests for preflight CORS checks for specific source.""" |
|
|
return '', 200 |
|
|
|
|
|
@sources_bp.route('/keyword-analysis', methods=['OPTIONS']) |
|
|
def handle_keyword_analysis_options(): |
|
|
"""Handle OPTIONS requests for preflight CORS checks for keyword analysis.""" |
|
|
return '', 200 |
|
|
|
|
|
|
|
|
@sources_bp.route('/keyword-analysis', methods=['POST']) |
|
|
@jwt_required() |
|
|
def analyze_keyword(): |
|
|
""" |
|
|
Analyze keyword frequency in RSS feeds and posts. |
|
|
|
|
|
Request Body: |
|
|
keyword (str): The keyword to analyze |
|
|
date_range (str): The date range to analyze ('daily', 'weekly', 'monthly'), default is 'monthly' |
|
|
|
|
|
Returns: |
|
|
JSON: Keyword frequency analysis data |
|
|
""" |
|
|
try: |
|
|
user_id = get_jwt_identity() |
|
|
data = request.get_json() |
|
|
|
|
|
|
|
|
if not data or 'keyword' not in data: |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'message': 'Keyword is required' |
|
|
}), 400 |
|
|
|
|
|
keyword = data['keyword'] |
|
|
date_range = data.get('date_range', 'monthly') |
|
|
|
|
|
|
|
|
valid_date_ranges = ['daily', 'weekly', 'monthly'] |
|
|
if date_range not in valid_date_ranges: |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'message': f'Invalid date_range. Must be one of: {valid_date_ranges}' |
|
|
}), 400 |
|
|
|
|
|
|
|
|
try: |
|
|
content_service = ContentService() |
|
|
analysis_data = content_service.analyze_keyword_frequency(keyword, user_id, date_range) |
|
|
|
|
|
return jsonify({ |
|
|
'success': True, |
|
|
'data': analysis_data, |
|
|
'keyword': keyword, |
|
|
'date_range': date_range |
|
|
}), 200 |
|
|
except Exception as e: |
|
|
current_app.logger.error(f"Keyword analysis error: {str(e)}") |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'message': f'An error occurred during keyword analysis: {str(e)}' |
|
|
}), 500 |
|
|
|
|
|
except Exception as e: |
|
|
current_app.logger.error(f"Analyze keyword error: {str(e)}") |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'message': f'An error occurred while analyzing keyword: {str(e)}' |
|
|
}), 500 |
|
|
|
|
|
|
|
|
@sources_bp.route('/keyword-frequency-pattern', methods=['POST']) |
|
|
@jwt_required() |
|
|
def analyze_keyword_frequency_pattern(): |
|
|
""" |
|
|
Analyze keyword frequency pattern in RSS feeds and posts. |
|
|
Determines if keyword follows a daily, weekly, monthly, or rare pattern based on recency and frequency. |
|
|
|
|
|
Request Body: |
|
|
keyword (str): The keyword to analyze |
|
|
|
|
|
Returns: |
|
|
JSON: Keyword frequency pattern analysis data |
|
|
""" |
|
|
try: |
|
|
user_id = get_jwt_identity() |
|
|
data = request.get_json() |
|
|
|
|
|
|
|
|
if not data or 'keyword' not in data: |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'message': 'Keyword is required' |
|
|
}), 400 |
|
|
|
|
|
keyword = data['keyword'] |
|
|
|
|
|
|
|
|
try: |
|
|
content_service = ContentService() |
|
|
analysis_result = content_service.analyze_keyword_frequency_pattern(keyword, user_id) |
|
|
|
|
|
return jsonify({ |
|
|
'success': True, |
|
|
'data': analysis_result, |
|
|
'keyword': keyword |
|
|
}), 200 |
|
|
except Exception as e: |
|
|
current_app.logger.error(f"Keyword frequency pattern analysis error: {str(e)}") |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'message': f'An error occurred during keyword frequency pattern analysis: {str(e)}' |
|
|
}), 500 |
|
|
|
|
|
except Exception as e: |
|
|
current_app.logger.error(f"Analyze keyword frequency pattern error: {str(e)}") |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'message': f'An error occurred while analyzing keyword frequency pattern: {str(e)}' |
|
|
}), 500 |
|
|
|
|
|
|
|
|
@sources_bp.route('/<source_id>', methods=['DELETE']) |
|
|
@jwt_required() |
|
|
def delete_source(source_id): |
|
|
""" |
|
|
Delete a source. |
|
|
|
|
|
Path Parameters: |
|
|
source_id (str): Source ID |
|
|
|
|
|
Returns: |
|
|
JSON: Delete source result |
|
|
""" |
|
|
try: |
|
|
user_id = get_jwt_identity() |
|
|
|
|
|
|
|
|
response = ( |
|
|
current_app.supabase |
|
|
.table("Source") |
|
|
.delete() |
|
|
.eq("id", source_id) |
|
|
.eq("user_id", user_id) |
|
|
.execute() |
|
|
) |
|
|
|
|
|
if response.data: |
|
|
return jsonify({ |
|
|
'success': True, |
|
|
'message': 'Source deleted successfully' |
|
|
}), 200 |
|
|
else: |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'message': 'Source not found or unauthorized' |
|
|
}), 404 |
|
|
|
|
|
except Exception as e: |
|
|
current_app.logger.error(f"Delete source error: {str(e)}") |
|
|
return jsonify({ |
|
|
'success': False, |
|
|
'message': 'An error occurred while deleting source' |
|
|
}), 500 |