|
|
|
@ -6,53 +6,71 @@ app.template_folder = "frontend/" |
|
|
|
|
app.static_folder = "frontend/assets/" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Graph(object): |
|
|
|
|
nodes = set() # set of strings |
|
|
|
|
edges = dict() # (string,string) -> float |
|
|
|
|
graph = { |
|
|
|
|
"A": {"B":10}, |
|
|
|
|
"B": {"E":10, "D":10, "C":10, "A":10}, |
|
|
|
|
"C": {"B":10, "D":10}, |
|
|
|
|
"D": {"B":10, "C":10}, |
|
|
|
|
"E": {"B":10} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def bellmann_ford(graph, node_a, node_b): |
|
|
|
|
"""Calculate distance from node_a to node_b assuming no negative cycles.""" |
|
|
|
|
# weights |
|
|
|
|
from_a_to = {**[(node, np.inf) for node in graph.nodes]} |
|
|
|
|
from_a_to = {node: np.inf for node in graph} |
|
|
|
|
from_a_to[node_a] = 0 |
|
|
|
|
# save path so we dont have to backtrace later |
|
|
|
|
path_from_a_to = {**[(node, []) for node in graph.nodes]} |
|
|
|
|
path_from_a_to = {node: [] for node in graph} |
|
|
|
|
path_from_a_to[node_a] = [node_a] |
|
|
|
|
|
|
|
|
|
for i in range(len(graph.nodes)-1): |
|
|
|
|
for e in graph.edges: |
|
|
|
|
# because edges are not directed |
|
|
|
|
for (x,y) in [e,e[::-1]]: |
|
|
|
|
if from_a_to[x] + graph.edges[(x,y)] < from_a_to[y]: |
|
|
|
|
path_from_a_to[y] = path_from_a_to + [y] |
|
|
|
|
from_a_to[y] = from_a_to[x] + graph.edges[(x,y)] |
|
|
|
|
|
|
|
|
|
for i in range(len(graph.keys())-1): |
|
|
|
|
for s,e in [(s,e) for s in graph for e in graph[s]]: |
|
|
|
|
if from_a_to[s] + graph[s][e] < from_a_to[e]: |
|
|
|
|
path_from_a_to[e] = path_from_a_to[s] + [e] |
|
|
|
|
from_a_to[e] = from_a_to[s] + graph[s][e] |
|
|
|
|
return path_from_a_to[node_b] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
graph = Graph() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def error(message): |
|
|
|
|
return jsonify({"error": message}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def edges_to_json(edges): |
|
|
|
|
def edges_to_json(graph): |
|
|
|
|
return_list = [] |
|
|
|
|
for e in edges: |
|
|
|
|
return_list.append({"start":e[0],"end":e[1],"weight":edges[e]}) |
|
|
|
|
done = [] |
|
|
|
|
for s in graph: |
|
|
|
|
for e in graph[s]: |
|
|
|
|
if (e,s) not in done: |
|
|
|
|
return_list.append({"start":s,"end":e,"weight":graph[s][e]}) |
|
|
|
|
done.append((s,e)) |
|
|
|
|
return return_list |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/api/paths/<start>/<end>', methods=['GET']) |
|
|
|
|
def get_path(start, end): |
|
|
|
|
if start == end: |
|
|
|
|
return error("Start and end node must be different."), 400 |
|
|
|
|
if start not in graph: |
|
|
|
|
return error("Start node does not exist."), 400 |
|
|
|
|
if end not in graph: |
|
|
|
|
return error("End node does not exist."), 400 |
|
|
|
|
|
|
|
|
|
path = bellmann_ford(graph, start, end) |
|
|
|
|
if len(path) == 0: |
|
|
|
|
return error("There exists no path."), 400 |
|
|
|
|
|
|
|
|
|
return jsonify({'path': path}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/api/nodes', methods=['GET']) |
|
|
|
|
def get_nodes(): |
|
|
|
|
return jsonify({'nodes': graph.nodes}) |
|
|
|
|
return jsonify({'nodes': graph.keys()}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/api/edges', methods=['GET']) |
|
|
|
|
def get_edges(): |
|
|
|
|
return jsonify({'edges': edges_to_json(graph.edges)}) |
|
|
|
|
return jsonify({'edges': edges_to_json(graph)}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/api/nodes', methods=['POST']) |
|
|
|
@ -65,10 +83,10 @@ def create_node(): |
|
|
|
|
name = request.json['name'] |
|
|
|
|
if name == "": |
|
|
|
|
return error("Name cannot be empty."), 400 |
|
|
|
|
if name in graph.nodes: |
|
|
|
|
if name in graph: |
|
|
|
|
return error(f'Node with name "{name}" already exist.'), 400 |
|
|
|
|
|
|
|
|
|
graph.nodes.add(name) |
|
|
|
|
graph[name] = {} |
|
|
|
|
return jsonify(name), 201 |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -94,22 +112,23 @@ def create_edge(): |
|
|
|
|
return error("Weight must be positive."), 400 |
|
|
|
|
if start == end: |
|
|
|
|
return error("Start and end node must be different."), 400 |
|
|
|
|
if start not in graph.nodes: |
|
|
|
|
if start not in graph: |
|
|
|
|
return error("Start node does not exist."), 400 |
|
|
|
|
if end not in graph.nodes: |
|
|
|
|
if end not in graph: |
|
|
|
|
return error("End node does not exist."), 400 |
|
|
|
|
if (start,end) in graph.edges or (end,start) in graph.edges: |
|
|
|
|
if start in graph and end in graph[start]: |
|
|
|
|
return error("Edge already exists."), 400 |
|
|
|
|
|
|
|
|
|
edge = {"start":start,"end":end, "weight": weight} |
|
|
|
|
graph.edges[(start,end)] = weight |
|
|
|
|
graph[start][end] = weight |
|
|
|
|
graph[end][start] = weight |
|
|
|
|
|
|
|
|
|
return jsonify(edge), 201 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route("/") |
|
|
|
|
def index(): |
|
|
|
|
return render_template("index.html", nodes=graph.nodes, edges=edges_to_json(graph.edges)) |
|
|
|
|
return render_template("index.html", nodes=list(graph.keys()), edges=edges_to_json(graph)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
|
|