def get_project_metrics(): STANDALONE_MODE = getenv("STANDALONE_MODE") owner = request.json['owner'] repo = request.json['repo'] project_name = owner + "/" + repo print(functions.log_time(), "Analyse request for:", project_name) if project_name is None: print(functions.log_time(), "Project name not set.") return jsonify({"error": "an error occurred"}), 500 status = "" last_status = "" if STANDALONE_MODE == "True": # Standalone mode functions.post_status_update(project_name, "in_progress", "Fetching project data.") project = functions.get_project_internal(project_name) if project.is_empty(): return jsonify({"error": "standalone mode, project cannot be parsed and is not in the database"}), 503 result = functions.compute_metrics(project_name, project) return jsonify(result), 200 else: while status != "error" and status != "ast_parsed": status = functions.get_parsing_status(project_name) if status != last_status: functions.post_status_update(project_name, status, "Processing project.") last_status = status time.sleep(5) if status == "error": return jsonify({"error": "an error occurred attempting to parse this project"}), 500 if status == "ast_parsed": functions.post_status_update(project_name, "in_progress", "Fetching project data.") project = functions.get_project_internal(project_name) result = functions.compute_metrics(project_name, project) return jsonify(result), 200
def __init__(self): # TODO Check if graph exists network.Network.__init__(self) records = self.__fetch_data() self.neo4j_to_network_artifact(records) print(functions.log_time(), "Fetched data and built graph.") self.__page_rank = self.compute_pagerank() print(functions.log_time(), "Calculated pagerank.")
def get_project_node(self): for node in self.graph.nodes(data=True): if node[1]['type'] == "Project": project_node = node return project_node print(functions.log_time(), "Project node not found.") return
def __fetch_dependent_data(self, project_name): print(functions.log_time(), "Fetching dependents.") # Dependent projects query = ("MATCH p = (parent:Project{id:$projectName})" "-[:Contains*0..1]->(child:Artifact)" "<-[:Depends*0..1]-(dep:Artifact)" + self.query_end) with self.db_driver.session() as session: result = session.run(query, parameters={"projectName": project_name}) return result.records()
def __fetch_data(self, project_name): print(functions.log_time(), "Fetching data for", project_name) query = ("MATCH p = (parent:Project{id:$projectName})" "-[ra:Contains]->(child:Package)" "-[rb:Contains]->(class:ClassOrInterface)" "-[rc:Contains]->(method:Method)" "-[rd:Calls*0..1]->(depMethod:Method) " + self.query_end) with self.db_driver.session() as session: result = session.run(query, parameters={"projectName": project_name}) return result.records()
def __fetch_data(self): print(functions.log_time(), "Fetching artifacts") query = ( "MATCH p=(:Artifact)" "UNWIND nodes(p) as allnodes WITH COLLECT(ID(allnodes)) AS ALLID " "MATCH (a)-[r2]-(b) " "WHERE ID(a) IN ALLID AND ID(b) IN ALLID " "WITH DISTINCT r2 " "RETURN id(startNode(r2)), type(r2), id(endNode(r2))") with self.db_driver.session() as session: result = session.run(query) return result.records()
status = "" last_status = "" if STANDALONE_MODE == "True": # Standalone mode functions.post_status_update(project_name, "in_progress", "Fetching project data.") project = functions.get_project_internal(project_name) if project.is_empty(): return jsonify({"error": "standalone mode, project cannot be parsed and is not in the database"}), 503 result = functions.compute_metrics(project_name, project) return jsonify(result), 200 else: while status != "error" and status != "ast_parsed": status = functions.get_parsing_status(project_name) if status != last_status: functions.post_status_update(project_name, status, "Processing project.") last_status = status time.sleep(5) if status == "error": return jsonify({"error": "an error occurred attempting to parse this project"}), 500 if status == "ast_parsed": functions.post_status_update(project_name, "in_progress", "Fetching project data.") project = functions.get_project_internal(project_name) result = functions.compute_metrics(project_name, project) return jsonify(result), 200 if __name__ == '__main__': print(functions.log_time(), "Starting software network analysis!") app.run(host='0.0.0.0', debug=True)
def get_artifacts(self): project_node = self.get_project_node() if project_node is None: print(functions.log_time(), "Error: project node not found") return project_artifacts = nx.ego_graph(self.graph, project_node[0], center=False) # Graph of all artifacts, without the project node (all relations should be "Depends") graph_of_artifacts_only = nx.ego_graph(self.graph, project_node[0], center=False, undirected=True, radius=100) reversed_graph = graph_of_artifacts_only.reverse() direct_dependencies = {} transitive_dependencies = {} dependents = {} project_artifact_details = {} # Search through direct dependencies and dependents for project_artifact in project_artifacts: project_artifact_details[project_artifact] = self.graph.nodes[ project_artifact] project_artifact_details[project_artifact][ "internal_id"] = project_artifact if self.graph.nodes[project_artifact]["type"] == "Artifact": # Outgoing nodes from artifact (dependencies) artifact_dependencies = nx.ego_graph(self.graph, project_artifact, center=False, radius=1) # Direct dependencies for artifact_dependency in artifact_dependencies: if self.graph.nodes[artifact_dependency]["type"] == "Artifact" \ and artifact_dependency not in direct_dependencies \ and artifact_dependency not in project_artifacts: direct_dependencies[ artifact_dependency] = self.graph.nodes[ artifact_dependency] direct_dependencies[artifact_dependency][ "internal_id"] = artifact_dependency # Incoming nodes into artifact (dependents) artifact_dependents = nx.ego_graph(reversed_graph, project_artifact) for artifact_dependent in artifact_dependents: if self.graph.nodes[artifact_dependent]["type"] == "Artifact" \ and artifact_dependent not in dependents: dependents[artifact_dependent] = self.graph.nodes[ artifact_dependent] dependents[artifact_dependent][ "internal_id"] = artifact_dependent # Search all nodes for nodes not direct dependency or project artifact for artifact in graph_of_artifacts_only: if self.graph.nodes[artifact]["type"] == "Artifact" and artifact not in direct_dependencies \ and artifact not in project_artifact_details and artifact not in dependents: transitive_dependencies[artifact] = self.graph.nodes[artifact] transitive_dependencies[artifact]["internal_id"] = artifact return { "Artifact": project_artifact_details, "DirectDependency": direct_dependencies, "TransitiveDependency": transitive_dependencies, "Dependent": dependents, }
import functions app = Flask(__name__) """Flask URL -> endpoints""" CORS(app) # Routes @app.route('/artifacts/pageranks', methods=['GET']) def get_pagerank(): artifacts = artifacts_network.ArtifactsNetwork() artifacts_pagerank = artifacts.compute_pagerank() if artifacts is not None: return jsonify(artifacts_pagerank), 200 return 500 @app.route('/artifacts/<string:owner>/<string:repo>', methods=['GET']) def get_project_artifacts(owner, repo): project_name = owner + "/" + repo artifact_deps = dependency_network.DependencyNetwork(project_name) if artifact_deps.is_empty(): return jsonify(""), 404 artifacts = artifact_deps.get_artifacts() return jsonify(artifacts), 200 if __name__ == '__main__': print(functions.log_time(), "Starting dependency network analysis!") app.run(host='0.0.0.0', debug=True)
def compute_metrics(self, project_name, project_node): node_metrics = {} # {a:{comp_a:x, comp_b:y}} project_child_nodes = nx.ego_graph(self.graph, project_node[0]) reversed_graph = self.graph.reverse() # For nWeakComp project_network_comp = 0 project_procedure_comp = 0 for project_child_node in project_child_nodes: # For each package package_network_comp = 0 if self.graph.nodes[project_child_node]["type"] != "Package": continue print(functions.log_time(), "Analysing metrics for package:", self.graph.nodes[project_child_node]["id"]) functions.post_status_update( project_name, "in_progress", "Analysing metrics for package:" + self.graph.nodes[project_child_node]["id"] + ".") package_child_nodes = nx.ego_graph(self.graph, project_child_node) for package_child_node in package_child_nodes: # For each class if self.graph.nodes[package_child_node][ "type"] != "ClassOrInterface": continue print(functions.log_time(), "Analysing metrics for class:", self.graph.nodes[package_child_node]["id"]) functions.post_status_update( project_name, "in_progress", "Analysing metrics for class:" + self.graph.nodes[package_child_node]["id"] + ".") class_network_comp = 0 class_child_nodes = nx.ego_graph(self.graph, package_child_node) for class_child_node in class_child_nodes: # For each method if self.graph.nodes[class_child_node]["type"] == "Method": node_metrics[class_child_node] = {} method_network_comp = self.get_network_comp( class_child_node, reversed_graph) procedure_comp = self.procedure_complexity( self.graph.in_degree(class_child_node), self.graph.out_degree(class_child_node)) node_metrics[class_child_node][ "network_comp"] = method_network_comp node_metrics[class_child_node][ "procedure_comp"] = procedure_comp project_procedure_comp += procedure_comp class_network_comp += method_network_comp node_metrics[package_child_node] = {} node_metrics[package_child_node][ "network_comp"] = class_network_comp package_network_comp += class_network_comp node_metrics[project_child_node] = {} node_metrics[project_child_node][ "network_comp"] = package_network_comp project_network_comp += package_network_comp node_metrics[project_node[0]] = {} node_metrics[project_node[0]]["code_churn"] = round( functions.compute_avg_code_change(project_name), 8) node_metrics[project_node[0]]["network_comp"] = project_network_comp node_metrics[ project_node[0]]["procedure_comp"] = project_procedure_comp functions.post_status_update(project_name, "in_progress", "Project analysed.") print(functions.log_time(), "Internal metrics analysed.") return node_metrics