Skip to content

Commit

Permalink
add Python backend
Browse files Browse the repository at this point in the history
  • Loading branch information
zhou325 committed Mar 15, 2020
1 parent 198bfaf commit 406e603
Show file tree
Hide file tree
Showing 58 changed files with 332 additions and 39,584 deletions.
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: gunicorn -w 4 -b 0.0.0.0:$PORT -k gevent app:app
13 changes: 13 additions & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from flask import Flask
import os

APP_ROOT = os.path.dirname(os.path.abspath(__file__)) # refers to application_top
APP_STATIC = os.path.join(APP_ROOT, 'static')
UPLOAD_FOLDER = os.path.join(APP_STATIC,'uploads')
ALLOWED_EXTENSIONS = set(['txt'])
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['ASSETS_DEBUG'] = True
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
from .util import assets
from app import views
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion web_components/js/barcode.js → app/static/js/barcode.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class Barcode{
constructor(barcode_data, linegraph){
this.barcode = barcode_data.barcode;
this.barcode = barcode_data;
this.linegraph = linegraph;
console.log(this.barcode)

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 2 additions & 0 deletions app/static/js/jquery-3.4.1.min.js

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions web_components/js/linegraph.js → app/static/js/linegraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ class Linegraph{
.force("link", d3.forceLink(this.links).distance(d => d.distance).id(d => d.id))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(this.svg_width/2, this.svg_height/2));
// .force("x", d3.forceX().strength(0.01))
// .force("y", d3.forceY().strength(0.01));

let ng = this.nodes_group.selectAll("g").data(this.nodes);
ng.exit().remove();
Expand Down
109 changes: 109 additions & 0 deletions app/static/js/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
$("#import").click(function(){
$("#files").click();
});

d3.select("#files")
.on("change", ()=>{
let files = $('#files')[0].files[0]
let fileReader = new FileReader();
fileReader.onload = function(fileLoadedEvent) {
let textFromFileLoaded = fileLoadedEvent.target.result;
let form = $('#upload')[0];
let content = new FormData(form);
$.ajax({
type: "POST",
// enctype: "application/x-www-form-urlencoded",
url: "/import",
data: textFromFileLoaded,
// processData: false, //prevent jQuery from automatically transforming the data into a query string
// contentType: false,
// cache: false,
dataType:'text',
success: function (response) {
// console.log(response)
response = JSON.parse(response);
let hyper_data = response.hyper_data;
let line_data = response.line_data;
let barcode_data = response.barcode_data;
let data = {"hyper_data":hyper_data, "line_data":line_data, "barcode_data":barcode_data}
console.log(data)
initialize_data(data);
},
error: function (error) {
console.log("error",error);
}
});

}
fileReader.readAsText(files, "UTF-8");
// console.log(data)
})

async function loadData() {
// let hyper_data = await d3.json('data/hypergraph.json');
// let line_data = await d3.json('data/linegraph.json');
// let barcode_data = await d3.json('data/barcode.json');

let hyper_data = await d3.json('static/uploads/hypergraph.json');
let line_data = await d3.json('static/uploads/linegraph.json');
let barcode_data = await d3.json('static/uploads/barcode.json');
return {
'hyper_data': hyper_data,
'line_data': line_data,
'barcode_data': barcode_data.barcode
};
}

function initialize_data(data) {
$('#barcode-svg').remove();
$('#hypergraph-svg').remove();
$('#linegraph-svg').remove();
$('#simplified-hypergraph-svg').remove();
$('#simplified-linegraph-svg').remove();
$('#vis-barcode').append('<svg id="barcode-svg"></svg>');
$('#vis-hypergraph').append('<svg id="hypergraph-svg"></svg>');
$('#vis-linegraph').append('<svg id="linegraph-svg"></svg>');
$('#vis-simplified-hypergraph').append('<svg id="simplified-hypergraph-svg"></svg>');
$('#vis-simplified-linegraph').append('<svg id="simplified-linegraph-svg"></svg>');

console.log(data)

let line_nodes_new = [];
let line_links_new = [];
data.line_data.nodes.forEach(n=>{
let node_new = {};
node_new.vertices = n.vertices.slice(0);
node_new.id = n.id;
node_new.index = n.index;
line_nodes_new.push(node_new);
})
data.line_data.links.forEach(l=>{
let link_new = {};
link_new.intersection_size = l.intersection_size;
link_new.source = l.source;
link_new.target = l.target;
link_new.index = l.index;
line_links_new.push(link_new);
})
let hypergraph = new Hypergraph(data.hyper_data);
let simplified_hypergraph = new Simplified_Hypergraph();
let linegraph = new Linegraph(data.line_data, hypergraph, "linegraph");
let simplified_linegraph = new Linegraph({"nodes": line_nodes_new, "links": line_links_new}, simplified_hypergraph, "simplified-linegraph");
let barcode = new Barcode(data.barcode_data, simplified_linegraph);

d3.select("#visual-encoding-form")
.on("change", ()=>{
let encoding_type = d3.select('input[name="visual-type"]:checked').node().value;
if(encoding_type === "bipartite"){
d3.select("#hull-group").style("visibility","hidden");
d3.select("#simplified-hull-group").style("visibility","hidden");
} else if(encoding_type === "convex"){
d3.select("#hull-group").style("visibility","visible");
d3.select("#simplified-hull-group").style("visibility","visible");
}
})
}

loadData().then(data=>{
initialize_data(data);
})
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
34 changes: 22 additions & 12 deletions web_components/view.html → app/templates/HyperVis.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,13 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Hypergraph Visualization</title>
<!-- <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous"> -->
<!-- <link rel="stylesheet" href="css/layout.css"> -->
<link rel="stylesheet" href="css/colors.css">
<link rel="stylesheet" href="css/bootstrap.css">
<link rel="stylesheet" href="css/layout-bootstrap.css">
<script src="https://d3js.org/d3.v5.min.js"></script>
<script type="text/javascript" src="js/script.js"></script>
<script type="text/javascript" src="js/hypergraph.js"></script>
<script type="text/javascript" src="js/simplified_hypergraph.js"></script>
<script type="text/javascript" src="js/linegraph.js"></script>
<script type="text/javascript" src="js/barcode.js"></script>

{% assets "js" %}
<script type="text/javascript" src="{{ ASSET_URL }}" defer></script>
{% endassets %}
{% assets "css" %}
<link rel="stylesheet" href="{{ ASSET_URL }}" />
{% endassets %}
</head>

<body>
Expand All @@ -38,7 +34,6 @@ <h4>
<div class="row">
<div class="col-md-6 outline">
Hypergraph
<input type="checkbox" id="hgraph-labels">Show labels</input>
<div id='vis-hypergraph' class="svg-container">
<svg id="hypergraph-svg"></svg>
</div>
Expand Down Expand Up @@ -72,9 +67,24 @@ <h4>

</div>
<div id="control-area" class="col-sm-2">
<h6>Control Panel</h6>
<!-- <div>
Import a dataset
</div> -->
<br>
<div id="files_group">
<div class="import-group">
<form method="post" id="upload" enctype="multipart/form-data" action="" >
<input type="file" style="display:none" id="files" name="files">
<input type="button" class="btn btn-outline-primary" id="import" value="Import A Dataset">
</form>
</div>
</div>
<br>
<div style="padding-bottom: 20px;">
<input type="checkbox" id="hgraph-labels">Show labels</input>

</div>
<div>
Hypergraph Visual Encoding
<div class="visual-encoding-group" style="padding-top:15px; padding-left:5px;">
Expand Down
28 changes: 28 additions & 0 deletions app/util/assets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from flask import Flask
from flask_assets import Bundle, Environment
from .. import app

bundles = {
'js': Bundle(
'js/d3.v5.js',
'js/jquery-3.4.1.min.js',
'js/bootstrap.min.js',
'js/hypergraph.js',
'js/simplified_hypergraph.js',
'js/linegraph.js',
'js/barcode.js',
'js/script.js',
output='gen/script.js'
),

'css': Bundle(
'css/colors.css',
'css/bootstrap.css',
'css/layout-bootstrap.css',
output='gen/styles.css'
)
}

assets = Environment(app)

assets.register(bundles)
141 changes: 141 additions & 0 deletions app/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
from flask import render_template,request, url_for, jsonify, redirect, Response, send_from_directory
from app import app
from app import APP_STATIC
from app import APP_ROOT
import json
import numpy as np
import pandas as pd
import hypernetx as hnx
import re
import matplotlib.pyplot as plt
import networkx as nx
from tqdm import tqdm
from os import path

def process_graph_edges(edge_str: str):
"""
Convert a string representation of the hypergraph into a python dictionary
:param edge_str: string representation of the hypergraph
:type edge_str: str
:return: dictionary representing the hypergraph
:rtype: dict
"""

edge_str = edge_str.strip().replace('\'', '\"')
converted_edge_str = edge_str[1:-1].replace('{', '[').replace('}', ']')
return json.loads('{' + converted_edge_str + '}')

def process_hypergraph(hyper_data: str):

hgraphs = []

# Separate the hypergraphs based on this regex:
# newline followed by one or more whitespace followed by newline
file_contents = re.split(r'\n\s+\n', hyper_data)

num_hgraphs = len(file_contents)

for i in tqdm(range(0, num_hgraphs)):
# The name and graph are separated by '='
graph_name, graph_dict = file_contents[i].split('=')
graph_dict = process_graph_edges(graph_dict)
# print(graph_name)
# print(graph_dict)
# hgraphs.append({'graph_dict':graph_dict, 'graph_name':graph_name})
hgraphs.append(hnx.Hypergraph(graph_dict, name=graph_name))
# print(hgraphs)

return hgraphs

def convert_to_line_graph(hypergraph):
print(hypergraph)
# Line-graph is a NetworkX graph
line_graph = nx.Graph()

# Nodes of the line-graph are nodes of the dual graph
# OR equivalently edges of the original hypergraph
[line_graph.add_node(edge, vertices=list(vertices)) for edge, vertices in hypergraph.incidence_dict.items()]

node_list = list(hypergraph.edges)

# For all pairs of edges (e1, e2), add edges such that
# intersection(e1, e2) is not empty
for node_idx_1, node1 in enumerate(node_list):
for node_idx_2, node2 in enumerate(node_list[node_idx_1 + 1:]):
vertices1 = hypergraph.edges[node1].elements
vertices2 = hypergraph.edges[node2].elements
# Compute the intersection size
intersection_size = len(set(vertices1) & set(vertices2))
if intersection_size > 0:
line_graph.add_edge(node1, node2, intersection_size=str(intersection_size))
line_graph = nx.readwrite.json_graph.node_link_data(line_graph)
return line_graph

def write_d3_graph(graph, path):
# Write to d3 like graph format
node_link_json = nx.readwrite.json_graph.node_link_data(graph)
# print(node_link_json)
with open(path, 'w') as f:
f.write(json.dumps(node_link_json, indent=4))

def find_cc_index(components, vertex_id):
for i in range(len(components)):
if vertex_id in components[i]:
return i

def compute_barcode(graph_data):
# with open(graph_path) as json_file:
# data = json.load(json_file)
nodes = graph_data['nodes']
links = graph_data['links']
components = []
barcode = []
for node in nodes:
components.append([node['id']])
for link in links:
link['intersection_size'] = int(link['intersection_size'])
links = sorted(links, key=lambda item: 1 / item['intersection_size'])
for link in links:
source_id = link['source']
target_id = link['target']
weight = 1 / link['intersection_size']
source_cc_idx = find_cc_index(components, source_id)
target_cc_idx = find_cc_index(components, target_id)
if source_cc_idx != target_cc_idx:
source_cc = components[source_cc_idx]
target_cc = components[target_cc_idx]
components = [components[i] for i in range(len(components)) if i not in [source_cc_idx, target_cc_idx]]
components.append(source_cc + target_cc)
barcode.append({'birth': 0, 'death': weight, 'edge': link})
for cc in components:
barcode.append({'birth': 0, 'death': -1, 'edge': 'undefined'})
return barcode


@app.route('/')
@app.route('/Hypergraph-Vis-app')
def index():
return render_template('HyperVis.html')

@app.route('/import', methods=['POST','GET'])
def import_file():
jsdata = request.get_data().decode('utf-8')
hgraphs = process_hypergraph(jsdata)
hgraph = hgraphs[0]
# lgraph = convert_to_line_graph(hnx.Hypergraph(hgraph['graph_dict'], name=hgraph['graph_name']))
lgraph = convert_to_line_graph(hgraph)
hgraph = nx.readwrite.json_graph.node_link_data(hgraph.bipartite())
# write_d3_graph(hgraph.bipartite(), path.join(APP_STATIC,"uploads/hypergraph.json"))
# write_d3_graph(lgraph, path.join(APP_STATIC,"uploads/linegraph.json"))
barcode = compute_barcode(lgraph)
# with open(path.join(APP_STATIC,"uploads/barcode.json"), 'w') as f:
# f.write(json.dumps({'barcode': barcode}, indent=4))
# filename = path.join(APP_STATIC,"assets/",jsdata.filename)
# with open(filename) as f:
# data = json.load(f)
# f.close()
print(barcode)
return jsonify(hyper_data=hgraph, line_data=lgraph, barcode_data=barcode)


11 changes: 0 additions & 11 deletions index.html

This file was deleted.

10 changes: 10 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Flask==1.0.0
Flask-Assets==0.12
Flask-Uploads==0.2.1
gunicorn==19.9.0
numpy==1.16.4
pandas==0.23.4
gevent==1.3.7
webassets==0.12.1
HyperNetX==0.2.5
tqdm==4.40.0
Loading

0 comments on commit 406e603

Please sign in to comment.