Added basic article retriever
This commit is contained in:
parent
8a23bcc58a
commit
e8673fb0f6
12
articleServer/Dockerfile
Normal file
12
articleServer/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM python:3.9-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
CMD ["python", "run_server.py"]
|
||||
188
articleServer/app.py
Normal file
188
articleServer/app.py
Normal file
@ -0,0 +1,188 @@
|
||||
import os
|
||||
import json
|
||||
import datetime
|
||||
from pathlib import Path
|
||||
from flask import Flask, request, jsonify
|
||||
from datetime import timedelta
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
try:
|
||||
# Read the RSS feeds to get all available news outlets
|
||||
rss_feeds_path = os.path.join(
|
||||
os.path.dirname(__file__), "..", "scraper", "rss_feeds.json"
|
||||
)
|
||||
with open(rss_feeds_path, "r") as f:
|
||||
rss_feeds = json.load(f)
|
||||
|
||||
# Get all available news outlets from the RSS feeds
|
||||
NEWS_OUTLETS = list(rss_feeds["rss_feeds"].keys())
|
||||
except Exception as e:
|
||||
print(f"Error loading RSS feeds: {e}")
|
||||
NEWS_OUTLETS = []
|
||||
|
||||
# Configuration - can be overridden by environment variable
|
||||
ARTICLE_DIR = os.environ.get("ARTICLE_DIR", "/Volumes/WORKDIR/articles/")
|
||||
|
||||
# Validate article directory exists
|
||||
if not os.path.exists(ARTICLE_DIR):
|
||||
print(f"Warning: Article directory does not exist: {ARTICLE_DIR}")
|
||||
|
||||
|
||||
def get_files_in_directory(directory_path):
|
||||
"""Get all files in a directory recursively."""
|
||||
files = []
|
||||
for root, _, filenames in os.walk(directory_path):
|
||||
for filename in filenames:
|
||||
file_path = os.path.join(root, filename)
|
||||
files.append(file_path)
|
||||
return files
|
||||
|
||||
|
||||
def get_file_create_time(file_path):
|
||||
"""Get the creation time of a file."""
|
||||
stat = os.stat(file_path)
|
||||
# On Unix systems, we use the creation time (ctime) or modification time (mtime)
|
||||
# On some systems like macOS, ctime might be more appropriate
|
||||
return datetime.datetime.fromtimestamp(stat.st_ctime)
|
||||
|
||||
|
||||
def is_file_in_time_range(file_path, start_time):
|
||||
"""Check if a file was created after the start_time."""
|
||||
try:
|
||||
create_time = get_file_create_time(file_path)
|
||||
return create_time >= start_time
|
||||
except Exception:
|
||||
# If we can't get the creation time, assume it's not in range
|
||||
return False
|
||||
|
||||
|
||||
def filter_articles_by_outlets(articles, outlets):
|
||||
"""Filter articles based on specified outlets."""
|
||||
if not outlets or not isinstance(outlets, list):
|
||||
return articles
|
||||
|
||||
# Normalize outlet names for comparison (remove extra spaces, make lowercase)
|
||||
normalized_outlets = [outlet.strip().lower() for outlet in outlets]
|
||||
|
||||
filtered_articles = []
|
||||
for article in articles:
|
||||
# Extract the news outlet from the file path
|
||||
# Article paths are like: /path/to/articles/Reuters – Business News/article_name.txt
|
||||
path_parts = Path(article).parts
|
||||
if len(path_parts) >= 2:
|
||||
outlet_name = path_parts[-2] # Outlet name is second to last part
|
||||
if outlet_name.lower() in normalized_outlets:
|
||||
filtered_articles.append(article)
|
||||
|
||||
return filtered_articles
|
||||
|
||||
|
||||
def get_articles_in_time_range(time_range, outlets=None):
|
||||
"""
|
||||
Get articles within a specified time range from the article directory.
|
||||
|
||||
Args:
|
||||
time_range (str): Time range ('hour', 'day', 'week', 'month')
|
||||
outlets (list, optional): List of news outlets to filter by
|
||||
|
||||
Returns:
|
||||
list: List of article file paths matching criteria
|
||||
"""
|
||||
# Validate that the article directory exists
|
||||
if not os.path.exists(ARTICLE_DIR):
|
||||
return []
|
||||
|
||||
# Get the start time based on the time range
|
||||
now = datetime.datetime.now()
|
||||
|
||||
if time_range == "hour":
|
||||
start_time = now - timedelta(hours=1)
|
||||
elif time_range == "day":
|
||||
start_time = now - timedelta(days=1)
|
||||
elif time_range == "week":
|
||||
start_time = now - timedelta(weeks=1)
|
||||
elif time_range == "month":
|
||||
start_time = now - timedelta(days=30)
|
||||
else:
|
||||
# Default to last hour if not specified correctly
|
||||
start_time = now - timedelta(hours=1)
|
||||
|
||||
# Get all files under the article directory
|
||||
all_articles = get_files_in_directory(ARTICLE_DIR)
|
||||
|
||||
# Filter for articles within time range
|
||||
filtered_articles = []
|
||||
for article_path in all_articles:
|
||||
if is_file_in_time_range(article_path, start_time):
|
||||
filtered_articles.append(article_path)
|
||||
|
||||
# Filter by outlets if provided
|
||||
if outlets:
|
||||
filtered_articles = filter_articles_by_outlets(filtered_articles, outlets)
|
||||
|
||||
return filtered_articles
|
||||
|
||||
|
||||
@app.route("/articles", methods=["GET"])
|
||||
def articles_endpoint():
|
||||
"""HTTP endpoint to get articles."""
|
||||
try:
|
||||
# Get query parameters
|
||||
time_range = request.args.get("time_range", "hour").lower()
|
||||
outlet_param = request.args.get("outlets")
|
||||
|
||||
# Parse outlets if provided
|
||||
outlets = None
|
||||
if outlet_param:
|
||||
outlets = [o.strip() for o in outlet_param.split(",") if o.strip()]
|
||||
|
||||
# Validate time range
|
||||
valid_time_ranges = ["hour", "day", "week", "month"]
|
||||
if time_range not in valid_time_ranges:
|
||||
return jsonify(
|
||||
{"error": f"Invalid time_range. Must be one of {valid_time_ranges}"}
|
||||
), 400
|
||||
|
||||
# Get articles
|
||||
articles = get_articles_in_time_range(time_range, outlets)
|
||||
|
||||
response_data = {
|
||||
"articles": [
|
||||
{
|
||||
"path": article,
|
||||
"name": os.path.basename(article),
|
||||
"outlet": os.path.basename(os.path.dirname(article)),
|
||||
"created_at": get_file_create_time(article).isoformat(),
|
||||
}
|
||||
for article in articles
|
||||
],
|
||||
"count": len(articles),
|
||||
"time_range": time_range,
|
||||
"outlets": outlets if outlets else "all",
|
||||
}
|
||||
|
||||
return jsonify(response_data)
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
|
||||
@app.route("/outlets", methods=["GET"])
|
||||
def outlets_endpoint():
|
||||
"""HTTP endpoint to get all available news outlets."""
|
||||
try:
|
||||
response_data = {"news_outlets": NEWS_OUTLETS, "count": len(NEWS_OUTLETS)}
|
||||
return jsonify(response_data)
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
|
||||
@app.route("/health", methods=["GET"])
|
||||
def health_check():
|
||||
"""Health check endpoint."""
|
||||
return jsonify({"status": "healthy"})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=5008, debug=True)
|
||||
5
articleServer/pyvenv.cfg
Normal file
5
articleServer/pyvenv.cfg
Normal file
@ -0,0 +1,5 @@
|
||||
home = /opt/homebrew/opt/python@3.13/bin
|
||||
include-system-site-packages = false
|
||||
version = 3.13.7
|
||||
executable = /opt/homebrew/Cellar/python@3.13/3.13.7/Frameworks/Python.framework/Versions/3.13/bin/python3.13
|
||||
command = /opt/homebrew/opt/python@3.13/bin/python3.13 -m venv /Users/user/Projects/StockDocs/articleServer
|
||||
1
articleServer/requirements.txt
Normal file
1
articleServer/requirements.txt
Normal file
@ -0,0 +1 @@
|
||||
Flask
|
||||
16
articleServer/run_server.py
Executable file
16
articleServer/run_server.py
Executable file
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Add the current directory to Python path
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from app import app
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Ensure ARTICLE_DIR environment variable is set if not already
|
||||
if "ARTICLE_DIR" not in os.environ:
|
||||
print("Warning: ARTICLE_DIR environment variable not set. Using default path.")
|
||||
|
||||
app.run(host="0.0.0.0", port=5000, debug=True)
|
||||
Loading…
x
Reference in New Issue
Block a user