Added basic article retriever

This commit is contained in:
Jarian Cottingham 2025-10-17 00:42:35 -05:00
parent 8a23bcc58a
commit e8673fb0f6
6 changed files with 222 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

12
articleServer/Dockerfile Normal file
View 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
View 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
View 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

View File

@ -0,0 +1 @@
Flask

16
articleServer/run_server.py Executable file
View 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)