From 9ad0a31afc72a55d1a56512cc2d727e1f5f1c9a24a0d0781c6ed762d451ee08e Mon Sep 17 00:00:00 2001 From: Moses Rolston Date: Thu, 6 Mar 2025 15:50:44 -0800 Subject: [PATCH] add node endpoint to list nodes of type --- api.py | 97 ++++++++++++++---------------------------- api/api.py | 44 +++++++++++++++++++ api/app.py | 55 ++++++++++++++++++++++++ api/endpoints/nodes.py | 30 +++++++++++++ 4 files changed, 162 insertions(+), 64 deletions(-) create mode 100644 api/api.py create mode 100644 api/app.py create mode 100644 api/endpoints/nodes.py diff --git a/api.py b/api.py index 16b006d..f4a6387 100644 --- a/api.py +++ b/api.py @@ -1,80 +1,49 @@ -from flask import Flask, request, jsonify -import requests -from neo4j import GraphDatabase -from dotenv import load_dotenv +# app.py +from flask import Flask, jsonify, request import os import logging -from cachetools import TTLCache +from neo4j import GraphDatabase +from dotenv import load_dotenv +from werkzeug.middleware.proxy_fix import ProxyFix +import importlib.util -# Set up basic configuration for logging -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) - -# Load environment variables from .env file load_dotenv() -# Retrieve Neo4j connection details and API key -NEO4J_URI = os.getenv('NEO4J_URI') -NEO4J_USER = os.getenv('NEO4J_USER') -NEO4J_PASSWORD = os.getenv('NEO4J_PASSWORD') -CONGRESS_API_KEY = os.getenv('CONGRESS_API_KEY') - -if not NEO4J_URI or not NEO4J_USER or not NEO4J_PASSWORD: - raise ValueError("Neo4j connection details not found in .env file") -if not CONGRESS_API_KEY: - raise ValueError("Congress API key not found in .env file") - -# Initialize Neo4j driver -driver = GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USER, NEO4J_PASSWORD)) - -# Initialize caches -neo4j_cache = TTLCache(maxsize=100, ttl=3600) # Cache for 1 hour -congress_api_cache = TTLCache(maxsize=100, ttl=3600) # Cache for 1 hour - app = Flask(__name__) +app.wsgi_app = ProxyFix(app.wsgi_app) -@app.route('/sponsored_legislation', methods=['GET']) -def get_sponsored_legislation(): - bioguide_id = request.args.get('bioguideId') - if not bioguide_id: - return jsonify({"error": "bioguideId is required"}), 400 +# Configure logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') - logger.info(f"Fetching sponsored legislation for member with bioguideId {bioguide_id}") +# Neo4j configuration +NEO4J_URI = os.getenv("NEO4J_URI") +NEO4J_USER = os.getenv("NEO4J_USER") +NEO4J_PASSWORD = os.getenv("NEO4J_PASSWORD") - # Check cache before making API request - cached_legislation = congress_api_cache.get(bioguide_id) - if cached_legislation: - logger.info(f"Using cached sponsored legislation for bioguideId {bioguide_id}") - return jsonify({"message": "Sponsored legislation retrieved from cache", "legislations": cached_legislation}) +def get_driver(): + return GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USER, NEO4J_PASSWORD)) +# Function to dynamically import and register blueprints +def load_blueprints_from_directory(directory): + for filename in os.listdir(directory): + if filename.endswith('.py') and not filename.startswith('__'): + module_name = filename[:-3] # Remove .py extension + file_path = os.path.join(directory, filename) + spec = importlib.util.spec_from_file_location(module_name, file_path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) -@app.route('/persons', methods=['GET']) -def list_persons(): - logger.info("Listing all person nodes from Neo4j") + if hasattr(module, 'bp'): + app.register_blueprint(module.bp) + logging.info(f'Registered blueprint: {module.bp.name}') - # Check cache before querying Neo4j - cached_persons = neo4j_cache.get('all_persons') - if cached_persons: - logger.info("Using cached list of persons") - return jsonify({"message": "Persons retrieved from cache", "persons": cached_persons}) +# Load blueprints from the endpoints directory +load_blueprints_from_directory(os.path.join(os.path.dirname(__file__), 'endpoints')) - with driver.session() as session: - query = "MATCH (p:Person) RETURN p" - logger.info(f"Executing Neo4j query to list all person nodes: {query}") - result = session.run(query) - - persons = [record['p'] for record in result] - person_list = [] - for person in persons: - node_properties = {key: value for key, value in person.items()} - person_list.append(node_properties) - - # Cache the response - neo4j_cache['all_persons'] = person_list - logger.info("Cached list of all persons") - - return jsonify({"message": "Persons listed successfully", "persons": person_list}) +@app.route('/') +def index(): + return jsonify({"message": "Welcome to the Flask API!"}) if __name__ == '__main__': - app.run(debug=True, host='0.0.0.0', port=5000) + app.run(debug=True) diff --git a/api/api.py b/api/api.py new file mode 100644 index 0000000..b824eb7 --- /dev/null +++ b/api/api.py @@ -0,0 +1,44 @@ +# app.py +from flask import Flask, jsonify, request +import os +import logging +from neo4j import GraphDatabase +from dotenv import load_dotenv +from werkzeug.middleware.proxy_fix import ProxyFix +import importlib.util + +load_dotenv() + +app = Flask(__name__) +app.wsgi_app = ProxyFix(app.wsgi_app) + +# Configure logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + +# Neo4j configuration +NEO4J_URI = os.getenv("NEO4J_URI") +NEO4J_USER = os.getenv("NEO4J_USER") +NEO4J_PASSWORD = os.getenv("NEO4J_PASSWORD") + +def get_driver(): + return GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USER, NEO4J_PASSWORD)) + +# Function to dynamically import and register blueprints +def load_blueprints_from_directory(directory): + for filename in os.listdir(directory): + if filename.endswith('.py') and not filename.startswith('__'): + module_name = filename[:-3] # Remove .py extension + file_path = os.path.join(directory, filename) + + spec = importlib.util.spec_from_file_location(module_name, file_path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + if hasattr(module, 'bp'): + app.register_blueprint(module.bp) + +# Load blueprints +load_blueprints_from_directory('endpoints') + +if __name__ == '__main__': + app.run(debug=True) diff --git a/api/app.py b/api/app.py new file mode 100644 index 0000000..1c17e23 --- /dev/null +++ b/api/app.py @@ -0,0 +1,55 @@ +# app.py +from flask import Flask, jsonify, request +import os +import logging +from neo4j import GraphDatabase +from dotenv import load_dotenv +from werkzeug.middleware.proxy_fix import ProxyFix +import importlib.util + +load_dotenv() + +app = Flask(__name__) +app.wsgi_app = ProxyFix(app.wsgi_app) + +# Configure logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + +# Neo4j configuration +NEO4J_URI = os.getenv("NEO4J_URI") +NEO4J_USER = os.getenv("NEO4J_USER") +NEO4J_PASSWORD = os.getenv("NEO4J_PASSWORD") + +def get_driver(): + return GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USER, NEO4J_PASSWORD)) + +# Custom logger for Neo4j queries +neo4j_logger = logging.getLogger('Neo4jLogger') +neo4j_logger.setLevel(logging.INFO) +neo4j_handler = logging.StreamHandler() +neo4j_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') +neo4j_handler.setFormatter(neo4j_formatter) +neo4j_logger.addHandler(neo4j_handler) + +# Attach the logger to the app instance +app.neo4j_logger = neo4j_logger + +# Function to dynamically import and register blueprints +def load_blueprints_from_directory(directory): + for filename in os.listdir(directory): + if filename.endswith('.py') and not filename.startswith('__'): + module_name = filename[:-3] # Remove .py extension + file_path = os.path.join(directory, filename) + + spec = importlib.util.spec_from_file_location(module_name, file_path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + if hasattr(module, 'bp'): + app.register_blueprint(module.bp) + +# Load blueprints +load_blueprints_from_directory('endpoints') + +if __name__ == '__main__': + app.run(debug=True) diff --git a/api/endpoints/nodes.py b/api/endpoints/nodes.py new file mode 100644 index 0000000..c5b776b --- /dev/null +++ b/api/endpoints/nodes.py @@ -0,0 +1,30 @@ +# endpoints/nodes.py +from flask import Blueprint, request, jsonify +from app import get_driver, neo4j_logger + +bp = Blueprint('nodes', __name__) + +@bp.route('/nodes') +def get_nodes(): + node_type = request.args.get('type') + + if not node_type: + return jsonify({"error": "Node type is required"}), 400 + + driver = get_driver() + with driver.session() as session: + query = f"MATCH (n:{node_type}) RETURN n" + neo4j_logger.info(f"Executing query: {query}") + nodes = session.run(query) + + # Convert the nodes to a list of dictionaries + nodes_list = [ + { + 'id': record['n'].id, + 'labels': list(record['n'].labels), + **{key: value for key, value in record['n'].items()} + } + for record in nodes + ] + + return jsonify({"nodes": nodes_list})