Skip to content

Commit

Permalink
local edges and nodes instead of neo4j
Browse files Browse the repository at this point in the history
  • Loading branch information
Mustafa Kerem Kurban committed Jun 7, 2024
1 parent 135885f commit 8587272
Show file tree
Hide file tree
Showing 9 changed files with 348 additions and 76 deletions.
89 changes: 13 additions & 76 deletions src/citation/flask/server.py
Original file line number Diff line number Diff line change
@@ -1,81 +1,18 @@
from flask import Flask, jsonify, render_template, request
from neo4j import GraphDatabase
import time
import os
"""Flask server for the citation graph."""
from flask import Flask, render_template

app = Flask(__name__, static_folder='../../../static', template_folder='../../../templates')
app = Flask(
__name__,
static_folder="../../../static",
template_folder="../../../templates",
)

# Neo4j connection details from environment variables
uri = os.getenv("NEO4J_URI", "bolt://localhost:7687")
username = os.getenv("NEO4J_USERNAME", "neo4j")
password = os.getenv("NEO4J_PASSWORD", "password")

driver = GraphDatabase.driver(uri, auth=(username, password))

def get_nodes_and_links(tx, limit=100):
nodes = []
links = []

# Query to get limited nodes with all properties
result = tx.run(f"""
MATCH (n:Paper)
RETURN n
LIMIT {limit}
""")
for record in result:
node = record["n"]
node_data = {
"uid": node["uid"],
"label": node["title"] if "title" in node else node.id,
"is_bbp": node.get("is_bbp", False),
}
nodes.append(node_data)

# Collect node UIDs
node_uids = [node["uid"] for node in nodes]

# Query to get relationships between the limited nodes
result = tx.run(f"""
MATCH (n:Paper)-[r:CITES]->(m:Paper)
WHERE n.uid IN {node_uids} AND m.uid IN {node_uids}
RETURN n.uid AS source, m.uid AS target, r
""")
for record in result:
link_data = {
"source": record["source"],
"target": record["target"],
}
links.append(link_data)

return nodes, links

def with_retry(session_func, *args, retries=5, delay=1):
for attempt in range(retries):
try:
with driver.session() as session:
return session_func(session, *args)
except InterruptedError:
if attempt < retries - 1:
time.sleep(delay)
continue
else:
raise

@app.route('/nodes')
def get_nodes():
limit = int(request.args.get('limit', 100)) # Get limit from query parameter or default to 100
nodes, _ = with_retry(lambda s: s.read_transaction(get_nodes_and_links, limit))
return jsonify(nodes)

@app.route('/links')
def get_links():
limit = int(request.args.get('limit', 100)) # Get limit from query parameter or default to 100
_, links = with_retry(lambda s: s.read_transaction(get_nodes_and_links, limit))
return jsonify(links)

@app.route('/')
@app.route("/")
def index():
return render_template('index.html')
"""Render the index page."""
return render_template("index.html")


if __name__ == '__main__':
app.run(debug=True)
if __name__ == "__main__":
app.run(debug=True)
81 changes: 81 additions & 0 deletions src/citation/flask/server_neo4j.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
from flask import Flask, jsonify, render_template, request
from neo4j import GraphDatabase
import time
import os

app = Flask(__name__, static_folder='../../../static', template_folder='../../../templates')

# Neo4j connection details from environment variables
uri = os.getenv("NEO4J_URI", "bolt://localhost:7687")
username = os.getenv("NEO4J_USERNAME", "neo4j")
password = os.getenv("NEO4J_PASSWORD", "password")

driver = GraphDatabase.driver(uri, auth=(username, password))

def get_nodes_and_links(tx, limit=100):
nodes = []
links = []

# Query to get limited nodes with all properties
result = tx.run(f"""
MATCH (n:Paper)
RETURN n
LIMIT {limit}
""")
for record in result:
node = record["n"]
node_data = {
"uid": node["uid"],
"label": node["title"] if "title" in node else node.id,
"is_bbp": node.get("is_bbp", False),
}
nodes.append(node_data)

# Collect node UIDs
node_uids = [node["uid"] for node in nodes]

# Query to get relationships between the limited nodes
result = tx.run(f"""
MATCH (n:Paper)-[r:CITES]->(m:Paper)
WHERE n.uid IN {node_uids} AND m.uid IN {node_uids}
RETURN n.uid AS source, m.uid AS target, r
""")
for record in result:
link_data = {
"source": record["source"],
"target": record["target"],
}
links.append(link_data)

return nodes, links

def with_retry(session_func, *args, retries=5, delay=1):
for attempt in range(retries):
try:
with driver.session() as session:
return session_func(session, *args)
except InterruptedError:
if attempt < retries - 1:
time.sleep(delay)
continue
else:
raise

@app.route('/nodes')
def get_nodes():
limit = int(request.args.get('limit', 100)) # Get limit from query parameter or default to 100
nodes, _ = with_retry(lambda s: s.read_transaction(get_nodes_and_links, limit))
return jsonify(nodes)

@app.route('/links')
def get_links():
limit = int(request.args.get('limit', 100)) # Get limit from query parameter or default to 100
_, links = with_retry(lambda s: s.read_transaction(get_nodes_and_links, limit))
return jsonify(links)

@app.route('/')
def index():
return render_template('index.html')

if __name__ == '__main__':
app.run(debug=True)
1 change: 1 addition & 0 deletions static/links.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions static/nodes.json

Large diffs are not rendered by default.

53 changes: 53 additions & 0 deletions templates/bloom.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>3D Force Graph</title>
<style>
body { margin: 0; }
</style>
<script src="//unpkg.com/3d-force-graph"></script>
<script type="importmap">{ "imports": { "three": "//unpkg.com/three/build/three.module.js" } }</script>
</head>
<body>
<div id="3d-graph"></div>
<script type="module">
import { UnrealBloomPass } from '//unpkg.com/three/examples/jsm/postprocessing/UnrealBloomPass.js';

const Graph = ForceGraph3D()
(document.getElementById('3d-graph'))
.backgroundColor('#000003')
.nodeLabel('label')
.nodeColor(node => node.is_bbp ? 'cyan' : 'red')
.nodeVal(node => node.is_bbp ? 10 : 5) // Set node size based on is_bbp
.linkDirectionalParticles(2) // Add particles to links for visibility
.linkDirectionalParticleWidth(0.5) // Set the width of link particles to make them less bright
.linkWidth(1); // Set the width of links

// Load data
fetch('/static/nodes.json')
.then(response => response.json())
.then(nodes => {
fetch('/static/links.json')
.then(response => response.json())
.then(links => {
// Combine nodes and links into a graph data object
const graphData = { nodes, links };

// Apply graph data to the 3D force graph
Graph.graphData(graphData);

// Apply UnrealBloomPass for post-processing
const bloomPass = new UnrealBloomPass();
bloomPass.strength = 0.5;
bloomPass.radius = 1;
bloomPass.threshold = 0;
Graph.postProcessingComposer().addPass(bloomPass);
});
});

// Adjust graph dimensions
Graph.width(window.innerWidth).height(window.innerHeight);
</script>
</body>
</html>
31 changes: 31 additions & 0 deletions templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>3D Force Graph</title>
<script src="https://unpkg.com/3d-force-graph"></script>
</head>
<body>
<div id="3d-graph"></div>
<script>
// Initialize 3D Force Graph
const Graph = ForceGraph3D()
(document.getElementById('3d-graph'))
.graphData({nodes: [], links: []});

// Load data
fetch('/static/nodes.json')
.then(response => response.json())
.then(nodes => {
fetch('/static/links.json')
.then(response => response.json())
.then(links => {
Graph.graphData({nodes, links});
});
});

// Adjust graph dimensions
Graph.width(window.innerWidth).height(window.innerHeight);
</script>
</body>
</html>
50 changes: 50 additions & 0 deletions templates/neo4j.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>3D Force Graph</title>
<style>
body { margin: 0; }
</style>
<script src="//unpkg.com/3d-force-graph"></script>
<script type="importmap">{ "imports": { "three": "//unpkg.com/three/build/three.module.js" } }</script>
</head>
<body>
<div id="3d-graph"></div>
<script type="module">
import { UnrealBloomPass } from '//unpkg.com/three/examples/jsm/postprocessing/UnrealBloomPass.js';

const Graph = ForceGraph3D()
(document.getElementById('3d-graph'))
.backgroundColor('#000003')
.nodeLabel('label')
.nodeColor(node => node.is_bbp ? 'blue' : 'yellow')
.nodeVal(node => node.is_bbp ? 10 : 5) // Set node size based on is_bbp
.linkDirectionalParticles(2) // Add particles to links for visibility
.linkDirectionalParticleWidth(0.5) // Set the width of link particles to make them less bright
.linkWidth(1); // Set the width of links

// Load data
Promise.all([
fetch('/nodes').then(response => response.json()),
fetch('/links').then(response => response.json())
]).then(([nodes, links]) => {
// Combine nodes and links into a graph data object
const graphData = { nodes, links };

// Apply graph data to the 3D force graph
Graph.graphData(graphData);

// Apply UnrealBloomPass for post-processing
const bloomPass = new UnrealBloomPass();
bloomPass.strength = 4;
bloomPass.radius = 1;
bloomPass.threshold = 0;
Graph.postProcessingComposer().addPass(bloomPass);
});

// Adjust graph dimensions
Graph.width(window.innerWidth).height(window.innerHeight);
</script>
</body>
</html>
52 changes: 52 additions & 0 deletions templates/neo4j_limit.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>3D Force Graph</title>
<style>
body { margin: 0; }
</style>
<script src="//unpkg.com/3d-force-graph"></script>
<script type="importmap">{ "imports": { "three": "//unpkg.com/three/build/three.module.js" } }</script>
</head>
<body>
<div id="3d-graph"></div>
<script type="module">
import { UnrealBloomPass } from '//unpkg.com/three/examples/jsm/postprocessing/UnrealBloomPass.js';

const limit = 100; // Set the limit for number of nodes to fetch

const Graph = ForceGraph3D()
(document.getElementById('3d-graph'))
.backgroundColor('#000003')
.nodeLabel('label')
.nodeColor(node => node.is_bbp ? 'blue' : 'yellow')
.nodeVal(node => node.is_bbp ? 10 : 5) // Set node size based on is_bbp
.linkDirectionalParticles(2) // Add particles to links for visibility
.linkDirectionalParticleWidth(0.5) // Set the width of link particles to make them less bright
.linkWidth(1); // Set the width of links

// Load data
Promise.all([
fetch(`/nodes?limit=${limit}`).then(response => response.json()),
fetch(`/links?limit=${limit}`).then(response => response.json())
]).then(([nodes, links]) => {
// Combine nodes and links into a graph data object
const graphData = { nodes, links };

// Apply graph data to the 3D force graph
Graph.graphData(graphData);

// Apply UnrealBloomPass for post-processing
const bloomPass = new UnrealBloomPass();
bloomPass.strength = 4;
bloomPass.radius = 1;
bloomPass.threshold = 0;
Graph.postProcessingComposer().addPass(bloomPass);
});

// Adjust graph dimensions
Graph.width(window.innerWidth).height(window.innerHeight);
</script>
</body>
</html>
Loading

0 comments on commit 8587272

Please sign in to comment.