|
|
|
import json, os
|
|
|
|
from flask import Flask, jsonify, request, render_template, escape
|
|
|
|
|
|
|
|
app = Flask(__name__)
|
|
|
|
app.template_folder = "frontend/"
|
|
|
|
app.static_folder = "frontend/assets/"
|
|
|
|
PATH = "graph.json"
|
|
|
|
|
|
|
|
graph = {}
|
|
|
|
|
|
|
|
|
|
|
|
def bellmann_ford(graph, node_a):
|
|
|
|
"""
|
|
|
|
Calculate distance from node_a to all other nodes assuming no negative
|
|
|
|
cycles.
|
|
|
|
|
|
|
|
Kommentar:
|
|
|
|
Auch wenn Dijkstra der effizientere Algorithmus in diesem Falle wäre,
|
|
|
|
habe mich dazu entschieden Bellmann Ford zu implementieren.
|
|
|
|
Dijkstra musste ich schon mindestens 4 mal in meiner Ausbildung
|
|
|
|
implementieren. Ich hatte mal wieder Lust auf Dynamische Programmierung.
|
|
|
|
"""
|
|
|
|
# weights
|
|
|
|
from_a_to = {node: float("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}
|
|
|
|
path_from_a_to[node_a] = [node_a]
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
def error(message):
|
|
|
|
return jsonify({"error": message})
|
|
|
|
|
|
|
|
|
|
|
|
def edges_to_json(graph):
|
|
|
|
return_list = []
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# ------------------------------API--------------------------------------------
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
@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=['POST'])
|
|
|
|
def create_node():
|
|
|
|
if not request.json:
|
|
|
|
return error("Request needs to be JSON."), 400
|
|
|
|
if 'name' not in request.json:
|
|
|
|
return error("Name must be set."), 400
|
|
|
|
|
|
|
|
name = request.json['name']
|
|
|
|
if name == "":
|
|
|
|
return error("Name cannot be empty."), 400
|
|
|
|
if name in graph:
|
|
|
|
return error(f'Node with name "{escape(name)}" already exist.'), 400
|
|
|
|
|
|
|
|
graph[name] = {}
|
|
|
|
return jsonify(name), 201
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/api/edges', methods=['POST'])
|
|
|
|
def create_edge():
|
|
|
|
if not request.json:
|
|
|
|
return error("Request needs to be JSON."), 400
|
|
|
|
if 'start' not in request.json:
|
|
|
|
return error("Start node must be set."), 400
|
|
|
|
if 'end' not in request.json:
|
|
|
|
return error("End node must be set."), 400
|
|
|
|
if 'weight' not in request.json:
|
|
|
|
return error("Edge weight must be set."), 400
|
|
|
|
|
|
|
|
start = request.json['start']
|
|
|
|
end = request.json['end']
|
|
|
|
weight = request.json['weight']
|
|
|
|
try:
|
|
|
|
weight = float(weight)
|
|
|
|
except ValueError:
|
|
|
|
return error("Weight must be a number."), 400
|
|
|
|
if weight < 0:
|
|
|
|
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:
|
|
|
|
return error("Start node does not exist."), 400
|
|
|
|
if end not in graph:
|
|
|
|
return error("End node does not exist."), 400
|
|
|
|
if start in graph and end in graph[start]:
|
|
|
|
return error("Edge already exists."), 400
|
|
|
|
|
|
|
|
edge = {"start":start,"end":end, "weight": weight}
|
|
|
|
graph[start][end] = weight
|
|
|
|
graph[end][start] = weight
|
|
|
|
|
|
|
|
return jsonify(edge), 201
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/api/edges/<start>/<end>', methods=['DELETE'])
|
|
|
|
def delete_edge(start, end):
|
|
|
|
if start in graph and end in graph[start]:
|
|
|
|
del graph[start][end]
|
|
|
|
del graph[end][start]
|
|
|
|
|
|
|
|
return jsonify({"start":start,"end":end}), 200
|
|
|
|
|
|
|
|
|
|
|
|
@app.route("/save")
|
|
|
|
def save():
|
|
|
|
try:
|
|
|
|
with open(PATH, 'w') as f:
|
|
|
|
json.dump(graph, f)
|
|
|
|
return jsonify({"info":"Saved graph",**graph}), 200
|
|
|
|
except Exception:
|
|
|
|
return error("Internal Server Error"), 500
|
|
|
|
|
|
|
|
|
|
|
|
@app.route("/clear")
|
|
|
|
def clear():
|
|
|
|
global graph
|
|
|
|
graph = {}
|
|
|
|
return "", 204
|
|
|
|
|
|
|
|
|
|
|
|
@app.route("/")
|
|
|
|
def index():
|
|
|
|
return render_template("index.html", nodes=list(graph.keys()), edges=edges_to_json(graph))
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
if os.path.isfile(PATH) and os.access(PATH, os.R_OK):
|
|
|
|
with open(PATH, 'r') as f:
|
|
|
|
graph = json.load(f)
|
|
|
|
app.run(host="127.0.0.1", debug= True, port=5000)
|