from flask import Flask, jsonify, request, render_template import numpy as np app = Flask(__name__) app.template_folder = "frontend/" app.static_folder = "frontend/assets/" 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} 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[node_b] 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 @app.route('/api/paths//', 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.keys()}) @app.route('/api/edges', methods=['GET']) def get_edges(): return jsonify({'edges': edges_to_json(graph)}) @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 "{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("/") def index(): return render_template("index.html", nodes=list(graph.keys()), edges=edges_to_json(graph)) if __name__ == '__main__': app.run(host="127.0.0.1", debug=True, port=5000)