diff --git a/cypher/Cyclic_Dependencies/Cyclic_Dependencies_Breakdown_Backward_Only_for_Typescript.cypher b/cypher/Cyclic_Dependencies/Cyclic_Dependencies_Breakdown_Backward_Only_for_Typescript.cypher new file mode 100644 index 000000000..7a9e18cf0 --- /dev/null +++ b/cypher/Cyclic_Dependencies/Cyclic_Dependencies_Breakdown_Backward_Only_for_Typescript.cypher @@ -0,0 +1,42 @@ +// Cyclic Dependencies Breakdown Backward-Only for Typescript + +MATCH (module:TS:Module)-[:EXPORTS]->(forwardSource:TS)-[:DEPENDS_ON]->(forwardTarget:TS)<-[:EXPORTS]-(dependentModule:TS:Module) +MATCH (dependentModule)-[:EXPORTS]->(backwardSource:TS)-[:DEPENDS_ON]->(backwardTarget:TS)<-[:EXPORTS]-(module) +// Get the project of the module if available +OPTIONAL MATCH (project:Directory)<-[:HAS_ROOT]-(:TS:Project)-[:CONTAINS]->(module) +OPTIONAL MATCH (dependentProject:Directory)<-[:HAS_ROOT]-(:TS:Project)-[:CONTAINS]->(dependentModule) +WHERE module.globalFqn <> dependentModule.globalFqn + WITH project.absoluteFileName AS projectFileName + ,replace( + module.globalFqn + ,project.absoluteFileName + '/', '' + ) AS moduleName + ,dependentProject.absoluteFileName AS dependentProjectFileName + ,replace( + dependentModule.globalFqn + ,dependentProject.absoluteFileName + '/', '' + ) AS dependentModulePathName + ,collect(DISTINCT forwardSource.name + '->' + forwardTarget.name) AS forwardDependencies + ,collect(DISTINCT backwardTarget.name + '<-' + backwardSource.name) AS backwardDependencies + WITH projectFileName + ,moduleName + ,dependentProjectFileName + ,dependentModulePathName + ,forwardDependencies + ,backwardDependencies + ,size(forwardDependencies) AS numberOfForwardDependencies + ,size(backwardDependencies) AS numberOfBackwardDependencies + ,size(forwardDependencies) + size(backwardDependencies) AS numberOfAllCyclicDependencies +WHERE (size(forwardDependencies) > size(backwardDependencies) + OR (size(forwardDependencies) = size(backwardDependencies) + AND size(moduleName) >= size(dependentModulePathName))) +UNWIND backwardDependencies AS dependency +RETURN projectFileName + ,moduleName + ,dependentProjectFileName + ,dependentModulePathName + ,dependency + ,toFloat(ABS(numberOfForwardDependencies - numberOfBackwardDependencies)) / numberOfAllCyclicDependencies AS forwardToBackwardBalance + ,numberOfForwardDependencies AS numberForward + ,numberOfBackwardDependencies AS numberBackward +ORDER BY forwardToBackwardBalance DESC, moduleName ASC \ No newline at end of file diff --git a/cypher/Cyclic_Dependencies/Cyclic_Dependencies_Breakdown_for_Typescript.cypher b/cypher/Cyclic_Dependencies/Cyclic_Dependencies_Breakdown_for_Typescript.cypher new file mode 100644 index 000000000..a8d95b1ca --- /dev/null +++ b/cypher/Cyclic_Dependencies/Cyclic_Dependencies_Breakdown_for_Typescript.cypher @@ -0,0 +1,42 @@ +// Cyclic Dependencies Breakdown for Typescript + +MATCH (module:TS:Module)-[:EXPORTS]->(forwardSource:TS)-[:DEPENDS_ON]->(forwardTarget:TS)<-[:EXPORTS]-(dependentModule:TS:Module) +MATCH (dependentModule)-[:EXPORTS]->(backwardSource:TS)-[:DEPENDS_ON]->(backwardTarget:TS)<-[:EXPORTS]-(module) +// Get the project of the module if available +OPTIONAL MATCH (project:Directory)<-[:HAS_ROOT]-(:TS:Project)-[:CONTAINS]->(module) +OPTIONAL MATCH (dependentProject:Directory)<-[:HAS_ROOT]-(:TS:Project)-[:CONTAINS]->(dependentModule) +WHERE module.globalFqn <> dependentModule.globalFqn + WITH project.absoluteFileName AS projectFileName + ,replace( + module.globalFqn + ,project.absoluteFileName + '/', '' + ) AS moduleName + ,dependentProject.absoluteFileName AS dependentProjectFileName + ,replace( + dependentModule.globalFqn + ,dependentProject.absoluteFileName + '/', '' + ) AS dependentModulePathName + ,collect(DISTINCT forwardSource.name + '->' + forwardTarget.name) AS forwardDependencies + ,collect(DISTINCT backwardTarget.name + '<-' + backwardSource.name) AS backwardDependencies + WITH projectFileName + ,moduleName + ,dependentProjectFileName + ,dependentModulePathName + ,forwardDependencies + ,backwardDependencies + ,size(forwardDependencies) AS numberOfForwardDependencies + ,size(backwardDependencies) AS numberOfBackwardDependencies + ,size(forwardDependencies) + size(backwardDependencies) AS numberOfAllCyclicDependencies +WHERE (size(forwardDependencies) > size(backwardDependencies) + OR (size(forwardDependencies) = size(backwardDependencies) + AND size(moduleName) >= size(dependentModulePathName))) +UNWIND (backwardDependencies + forwardDependencies) AS dependency +RETURN projectFileName + ,moduleName + ,dependentProjectFileName + ,dependentModulePathName + ,dependency + ,toFloat(ABS(numberOfForwardDependencies - numberOfBackwardDependencies)) / numberOfAllCyclicDependencies AS forwardToBackwardBalance + ,numberOfForwardDependencies AS numberForward + ,numberOfBackwardDependencies AS numberBackward +ORDER BY forwardToBackwardBalance DESC, moduleName ASC \ No newline at end of file diff --git a/cypher/Cyclic_Dependencies/Cyclic_Dependencies_between_Artrifacts_as_unwinded_List.cypher b/cypher/Cyclic_Dependencies/Cyclic_Dependencies_between_Artifacts_as_unwinded_List.cypher similarity index 100% rename from cypher/Cyclic_Dependencies/Cyclic_Dependencies_between_Artrifacts_as_unwinded_List.cypher rename to cypher/Cyclic_Dependencies/Cyclic_Dependencies_between_Artifacts_as_unwinded_List.cypher diff --git a/cypher/Cyclic_Dependencies/Cyclic_Dependencies_for_Typescript.cypher b/cypher/Cyclic_Dependencies/Cyclic_Dependencies_for_Typescript.cypher new file mode 100644 index 000000000..eed65e82d --- /dev/null +++ b/cypher/Cyclic_Dependencies/Cyclic_Dependencies_for_Typescript.cypher @@ -0,0 +1,42 @@ +//Cyclic Dependencies as List for Typescript + +MATCH (module:TS:Module)-[:EXPORTS]->(forwardSource:TS)-[:DEPENDS_ON]->(forwardTarget:TS)<-[:EXPORTS]-(dependentModule:TS:Module) +MATCH (dependentModule)-[:EXPORTS]->(backwardSource:TS)-[:DEPENDS_ON]->(backwardTarget:TS)<-[:EXPORTS]-(module) +// Get the project of the module if available +OPTIONAL MATCH (project:Directory)<-[:HAS_ROOT]-(:TS:Project)-[:CONTAINS]->(module) +OPTIONAL MATCH (dependentProject:Directory)<-[:HAS_ROOT]-(:TS:Project)-[:CONTAINS]->(dependentModule) +WHERE module.globalFqn <> dependentModule.globalFqn + WITH project.absoluteFileName AS projectFileName + ,replace( + module.globalFqn + ,project.absoluteFileName + '/', '' + ) AS moduleName + ,dependentProject.absoluteFileName AS dependentProjectFileName + ,replace( + dependentModule.globalFqn + ,dependentProject.absoluteFileName + '/', '' + ) AS dependentModulePathName + ,collect(DISTINCT forwardSource.name + '->' + forwardTarget.name) AS forwardDependencies + ,collect(DISTINCT backwardTarget.name + '<-' + backwardSource.name) AS backwardDependencies + WITH projectFileName + ,moduleName + ,dependentProjectFileName + ,dependentModulePathName + ,forwardDependencies + ,backwardDependencies + ,size(forwardDependencies) AS numberOfForwardDependencies + ,size(backwardDependencies) AS numberOfBackwardDependencies + ,size(forwardDependencies) + size(backwardDependencies) AS numberOfAllCyclicDependencies +WHERE (size(forwardDependencies) > size(backwardDependencies) + OR (size(forwardDependencies) = size(backwardDependencies) + AND size(moduleName) >= size(dependentModulePathName))) +RETURN projectFileName + ,moduleName + ,dependentProjectFileName + ,dependentModulePathName + ,toFloat(ABS(numberOfForwardDependencies - numberOfBackwardDependencies)) / numberOfAllCyclicDependencies AS forwardToBackwardBalance + ,numberOfForwardDependencies AS numberForward + ,numberOfBackwardDependencies AS numberBackward + ,forwardDependencies[0..9] AS forwardDependencyExamples + ,backwardDependencies[0..9] AS backwardDependencyExamples +ORDER BY forwardToBackwardBalance DESC, moduleName ASC \ No newline at end of file diff --git a/cypher/Exploration/Explore_common_globalfqn_prefix.cypher b/cypher/Exploration/Explore_common_globalfqn_prefix.cypher new file mode 100644 index 000000000..53d88d198 --- /dev/null +++ b/cypher/Exploration/Explore_common_globalfqn_prefix.cypher @@ -0,0 +1,14 @@ +// Get common global full qualified name prefix + +MATCH (module:TS) +WHERE module.globalFqn STARTS WITH '/' + WITH count(module.globalFqn) AS numberOfAllModules + ,collect(module) AS modules +UNWIND modules AS module + WITH numberOfAllModules + ,module.globalFqn AS moduleFullQualifiedName + ,split(module.globalFqn, '/') AS paths +UNWIND paths AS path + WITH path, numberOfAllModules, count(*) AS numberOfModulesUsingThePath +WHERE numberOfAllModules = numberOfModulesUsingThePath +RETURN apoc.text.join(collect(path), '/') AS commonPath \ No newline at end of file diff --git a/cypher/Candidates_for_Interface_Segregation.cypher b/cypher/Internal_Dependencies/Candidates_for_Interface_Segregation.cypher similarity index 100% rename from cypher/Candidates_for_Interface_Segregation.cypher rename to cypher/Internal_Dependencies/Candidates_for_Interface_Segregation.cypher diff --git a/cypher/Internal_Dependencies/How_many_elements_compared_to_all_existing_are_used_by_dependent_modules_for_Typescript.cypher b/cypher/Internal_Dependencies/How_many_elements_compared_to_all_existing_are_used_by_dependent_modules_for_Typescript.cypher new file mode 100644 index 000000000..cc960478a --- /dev/null +++ b/cypher/Internal_Dependencies/How_many_elements_compared_to_all_existing_are_used_by_dependent_modules_for_Typescript.cypher @@ -0,0 +1,21 @@ +// How many elements compared to all existing are used by dependent Typescript modules? + +MATCH (sourceModule:TS&Module)-[:EXPORTS]-(sourceElement:TS) +MATCH (sourceElement)-[:DEPENDS_ON]->(dependentElement:TS&!Module&!ExternalModule) +MATCH (dependentModule:TS&Module)-[:EXPORTS]->(dependentElement) +WHERE sourceModule <> dependentModule +MATCH (dependentModule)-[:EXPORTS]->(dependentModuleElement:TS) + WITH sourceModule.name AS sourceModuleName + ,dependentModule.name AS dependentModuleName + ,COUNT(DISTINCT dependentElement.globalFqn) AS dependentElementsCount + ,COUNT(DISTINCT dependentModuleElement.globalFqn) AS dependentModuleElementsCount + ,collect(DISTINCT dependentElement.globalFqn)[0..4] AS dependentElementFullNameExamples + ,collect(DISTINCT dependentElement.name)[0..4] AS dependentElementNameExamples +RETURN sourceModuleName + ,dependentModuleName + ,dependentElementsCount + ,dependentModuleElementsCount + ,toFloat(dependentElementsCount) / (dependentModuleElementsCount + 1E-38) AS elementUsagePercentage + ,dependentElementFullNameExamples + ,dependentElementNameExamples +ORDER BY elementUsagePercentage ASC \ No newline at end of file diff --git a/cypher/List_all_existing_java_artifacts.cypher b/cypher/Internal_Dependencies/List_all_Java_artifacts.cypher similarity index 100% rename from cypher/List_all_existing_java_artifacts.cypher rename to cypher/Internal_Dependencies/List_all_Java_artifacts.cypher diff --git a/cypher/Internal_Dependencies/List_all_Typescript_modules.cypher b/cypher/Internal_Dependencies/List_all_Typescript_modules.cypher new file mode 100644 index 000000000..5728cdd62 --- /dev/null +++ b/cypher/Internal_Dependencies/List_all_Typescript_modules.cypher @@ -0,0 +1,9 @@ +// List all existing internal Typescript modules + +MATCH (internalModule:TS:Module)-[:EXPORTS]->(internalElement:TS) + WITH internalModule.name AS moduleName + ,internalModule.globalFqn AS moduleFullQualifiedName + ,internalModule.incomingDependencies AS incomingDependencies + ,internalModule.outgoingDependencies AS outgoingDependencies + ,COUNT(DISTINCT internalElement.globalFqn) AS numberOfElements +RETURN moduleName, numberOfElements, incomingDependencies, outgoingDependencies, moduleFullQualifiedName \ No newline at end of file diff --git a/cypher/Internal_Dependencies/List_elements_that_are_used_by_many_different_modules_for_Typescript.cypher b/cypher/Internal_Dependencies/List_elements_that_are_used_by_many_different_modules_for_Typescript.cypher new file mode 100644 index 000000000..1aad9a7ad --- /dev/null +++ b/cypher/Internal_Dependencies/List_elements_that_are_used_by_many_different_modules_for_Typescript.cypher @@ -0,0 +1,18 @@ +// List elements that are used by many different modules + +MATCH (sourceModule:TS&Module)-[:EXPORTS]->(sourceElement:TS) +MATCH (sourceElement)-[:DEPENDS_ON]->(dependentElement:TS&!Module&!ExternalModule) +MATCH (dependentModule:TS&Module)-[:EXPORTS]->(dependentElement) +WHERE sourceModule <> dependentModule + WITH dependentElement + ,labels(dependentElement) AS dependentElementLabels + ,COUNT(DISTINCT sourceModule.globalFqn) AS numberOfUsingModules +UNWIND dependentElementLabels AS dependentElementLabel + WITH * + WHERE NOT dependentElementLabel = 'TS' // Filter out generic TS label +RETURN dependentElement.globalFqn AS fullQualifiedDependentElementName + ,dependentElement.moduleName AS dependentElementModuleName + ,dependentElement.name AS dependentElementName + ,dependentElementLabel AS dependentElementLabels + ,numberOfUsingModules + ORDER BY numberOfUsingModules DESC, dependentElementName ASC \ No newline at end of file diff --git a/cypher/Metrics/Set_Incoming_Typescript_Module_Dependencies.cypher b/cypher/Metrics/Set_Incoming_Typescript_Module_Dependencies.cypher index 9d0863cd7..0b1fa54f8 100644 --- a/cypher/Metrics/Set_Incoming_Typescript_Module_Dependencies.cypher +++ b/cypher/Metrics/Set_Incoming_Typescript_Module_Dependencies.cypher @@ -1,8 +1,8 @@ // Set incoming Typscript Module dependencies -// Get the top level dependency between a Typescript module and an external modules it uses +// Get the top level dependency between a Typescript module and other modules that uses it MATCH (source:TS:Module) -OPTIONAL MATCH (source)<-[:RESOLVES_TO]-(sourceExternal:ExternalModule)<-[moduleDependency:DEPENDS_ON]-(target:TS:Module) +OPTIONAL MATCH (source)<-[moduleDependency:DEPENDS_ON]-(target:TS:Module) WHERE source <> target // Get the project of the external module if available OPTIONAL MATCH (projectdir:Directory)<-[:HAS_ROOT]-(project:TS:Project)-[:CONTAINS]->(target) @@ -21,9 +21,6 @@ OPTIONAL MATCH (projectdir:Directory)<-[:HAS_ROOT]-(project:TS:Project)-[:CONTAI ,source.incomingDependentAbstractTypeWeight = abstractTypeCardinality ,source.incomingDependentModules = externalModuleCount ,source.incomingDependentPackages = size(projectNames) - // Incoming dependencies properties can't easily be set on sourceExternal nodes - // since there might be more than one per source. If this is needed in future - // assure that there is no regression for the source nodes. RETURN source.globalFqn AS fullQualifiedModuleName ,source.name AS moduleName ,declarationCount AS incomingDependencies diff --git a/cypher/Typescript_Enrichment/Add_module_properties.cypher b/cypher/Typescript_Enrichment/Add_module_properties.cypher index 9053e5a2e..5dc54a781 100644 --- a/cypher/Typescript_Enrichment/Add_module_properties.cypher +++ b/cypher/Typescript_Enrichment/Add_module_properties.cypher @@ -1,20 +1,28 @@ -// Add "namespace", "module" and "name" properties to Typescript nodes that have a globalFqn property - +// Add extended properties to Typescript nodes with a globalFqn like "namespace", "module", "name" and "extension" as well as markers like "isNodeModule", "isUnresolvedImport" and "isExternalImport" MATCH (ts:TS) WHERE ts.globalFqn IS NOT NULL +OPTIONAL MATCH (class:TS:Class)-[:DECLARES]->(ts) WITH ts - ,replace(split(ts.globalFqn, '".')[0],'"', '') AS moduleName - ,replace(split(ts.globalFqn, '/index')[0],'"', '') AS moduleNameWithoutIndex - ,split(ts.globalFqn, '".')[1] AS symbolName - ,split(nullif(reverse(split(reverse(ts.globalFqn), '@')[0]), ts.globalFqn), '/')[0] AS namespaceName - ,(ts.globalFqn contains '/node_modules/') AS isNodeModule - ,((NOT ts.globalFqn STARTS WITH '/') AND size(split(ts.globalFqn, '/')) < 3) AS isUnresolvedImport + ,replace(split(ts.globalFqn, '".')[0],'"', '') AS modulePathName + ,reverse(split(reverse(replace(split(ts.globalFqn, '".')[0],'"', '')), '/')[0]) AS moduleName + ,replace(split(split(ts.globalFqn, '/index')[0], '.default')[0],'"', '') AS modulePathNameWithoutIndexAndDefault + ,nullif(split(ts.globalFqn, '".')[1], 'default') AS symbolName + ,split(nullif(reverse(split(reverse(ts.globalFqn), '@')[0]), ts.globalFqn), '/')[0] AS namespaceName + ,(ts.globalFqn contains '/node_modules/') AS isNodeModule + ,((NOT ts.globalFqn STARTS WITH '/') AND size(split(ts.globalFqn, '/')) < 3) AS isUnresolvedImport + ,reverse(split(reverse(class.globalFqn), '.')[0]) AS optionalClassName WITH * - ,reverse(split(reverse(moduleNameWithoutIndex), '/')[0]) AS indexedName - ,coalesce('@' + nullif(namespaceName, ''), '') AS namespaceNameWithAtPrefixed + ,split(reverse(split(reverse(modulePathNameWithoutIndexAndDefault), '/')[0]), '.')[0] AS indexAndExtensionOmittedName + ,replace(moduleName, nullif(split(moduleName, '.')[0], moduleName) + '.', '') AS moduleNameExtensionExtended + ,nullif(reverse(split(reverse(moduleName), '.')[0]), moduleName) AS moduleNameExtension + ,coalesce('@' + nullif(namespaceName, ''), '') AS namespaceNameWithAtPrefixed + ,replace(symbolName, coalesce(optionalClassName + '.', ''), '') AS symbolNameWithoutClassName SET ts.namespace = namespaceNameWithAtPrefixed - ,ts.module = moduleName - ,ts.name = coalesce(symbolName, indexedName) + ,ts.module = modulePathName + ,ts.moduleName = moduleName + ,ts.name = coalesce(symbolNameWithoutClassName, indexAndExtensionOmittedName) + ,ts.extensionExtended = moduleNameExtensionExtended + ,ts.extension = moduleNameExtension ,ts.isNodeModule = isNodeModule ,ts.isUnresolvedImport = isUnresolvedImport ,ts.isExternalImport = isNodeModule OR isUnresolvedImport diff --git a/jupyter/InternalDependenciesJava.ipynb b/jupyter/InternalDependenciesJava.ipynb index ddf240d61..1cf5d44b7 100644 --- a/jupyter/InternalDependenciesJava.ipynb +++ b/jupyter/InternalDependenciesJava.ipynb @@ -111,7 +111,7 @@ "List the artifacts this notebook is based on. Different sorting variations help finding artifacts by their features and support larger code bases where the list of all artifacts gets too long.\n", "\n", "Only the top 30 entries are shown. The whole table can be found in the following CSV report: \n", - "`List_all_existing_java_artifacts`" + "`List_all_Java_artifacts`" ] }, { @@ -121,7 +121,7 @@ "metadata": {}, "outputs": [], "source": [ - "artifacts = query_cypher_to_data_frame(\"../cypher/List_all_existing_java_artifacts.cypher\")" + "artifacts = query_cypher_to_data_frame(\"../cypher/Internal_Dependencies/List_all_Java_artifacts.cypher\")" ] }, { @@ -431,7 +431,7 @@ }, "outputs": [], "source": [ - "interface_segregation_candidates=query_cypher_to_data_frame(\"../cypher/Candidates_for_Interface_Segregation.cypher\", limit=40)\n", + "interface_segregation_candidates=query_cypher_to_data_frame(\"../cypher/Internal_Dependencies/Candidates_for_Interface_Segregation.cypher\", limit=40)\n", "interface_segregation_candidates" ] }, @@ -586,7 +586,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.4" + "version": "3.11.9" }, "title": "Object Oriented Design Quality Metrics for Java with Neo4j" }, diff --git a/jupyter/InternalDependenciesTypescript.ipynb b/jupyter/InternalDependenciesTypescript.ipynb new file mode 100644 index 000000000..077336712 --- /dev/null +++ b/jupyter/InternalDependenciesTypescript.ipynb @@ -0,0 +1,437 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "2f0eabc4", + "metadata": {}, + "source": [ + "# Internal Dependencies\n", + "
\n", + "\n", + "### References\n", + "- [Analyze java package metrics in a graph database](https://joht.github.io/johtizen/data/2023/04/21/java-package-metrics-analysis.html)\n", + "- [Calculate metrics](https://101.jqassistant.org/calculate-metrics/index.html)\n", + "- [Neo4j Python Driver](https://neo4j.com/docs/api/python-driver/current)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4191f259", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plot\n", + "from neo4j import GraphDatabase" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c5dab37", + "metadata": {}, + "outputs": [], + "source": [ + "# Please set the environment variable \"NEO4J_INITIAL_PASSWORD\" in your shell \n", + "# before starting jupyter notebook to provide the password for the user \"neo4j\". \n", + "# It is not recommended to hardcode the password into jupyter notebook for security reasons.\n", + "\n", + "driver = GraphDatabase.driver(uri=\"bolt://localhost:7687\", auth=(\"neo4j\", os.environ.get(\"NEO4J_INITIAL_PASSWORD\")))\n", + "driver.verify_connectivity()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c1db254b", + "metadata": {}, + "outputs": [], + "source": [ + "def get_cypher_query_from_file(cypherFileName):\n", + " with open(cypherFileName) as file:\n", + " return ' '.join(file.readlines())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59310f6f", + "metadata": {}, + "outputs": [], + "source": [ + "def query_cypher_to_data_frame(filename : str, limit: int = 10_000):\n", + " cypher_query_template = \"{query}\\nLIMIT {row_limit}\"\n", + " cypher_query = get_cypher_query_from_file(filename)\n", + " cypher_query = cypher_query_template.format(query = cypher_query, row_limit = limit)\n", + " records, summary, keys = driver.execute_query(cypher_query)\n", + " return pd.DataFrame([r.values() for r in records], columns=keys)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a56670c9", + "metadata": {}, + "outputs": [], + "source": [ + "#The following cell uses the build-in %html \"magic\" to override the CSS style for tables to a much smaller size.\n", + "#This is especially needed for PDF export of tables with multiple columns." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "006b9dc8", + "metadata": {}, + "outputs": [], + "source": [ + "%%html\n", + "" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "91d80bf7", + "metadata": {}, + "source": [ + "## 1 - Modules\n", + "\n", + "List the modules this notebook is based on. Different sorting variations help finding modules by their features and support larger code bases where the list of all modules gets very long.\n", + "\n", + "Only the top 30 entries are shown. The whole table can be found in the following CSV report: \n", + "`List_all_Typescript_modules`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc682db6", + "metadata": {}, + "outputs": [], + "source": [ + "internalModules = query_cypher_to_data_frame(\"../cypher/Internal_Dependencies/List_all_Typescript_modules.cypher\")" + ] + }, + { + "cell_type": "markdown", + "id": "e48ead1b", + "metadata": {}, + "source": [ + "### Table 1a - Top 30 modules with the highest element count" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f822ddca", + "metadata": {}, + "outputs": [], + "source": [ + "# Sort by number of modules descending\n", + "internalModules.sort_values(by=['numberOfElements','moduleName'], ascending=[False, True]).reset_index(drop=True).head(30)" + ] + }, + { + "cell_type": "markdown", + "id": "1e508a21", + "metadata": {}, + "source": [ + "### Table 1b - Top 30 modules with the highest number of incoming dependencies\n", + "\n", + "The following table lists the top 30 internal modules that are used the most by other modules (highest count of incoming dependencies, highest in-degree)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "107c0a1a", + "metadata": {}, + "outputs": [], + "source": [ + "# Sort by number of incoming dependencies descending\n", + "internalModules.sort_values(by=['incomingDependencies','moduleName'], ascending=[False, True]).reset_index(drop=True).head(30)" + ] + }, + { + "cell_type": "markdown", + "id": "52593397", + "metadata": {}, + "source": [ + "### Table 1c - Top 30 modules with the highest number of outgoing dependencies\n", + "\n", + "The following table lists the top 30 internal modules that are depending on the highest number of other modules (highest count of outgoing dependencies, highest out-degree)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eee13bd8", + "metadata": {}, + "outputs": [], + "source": [ + "# Sort by number of outgoing dependencies descending\n", + "internalModules.sort_values(by=['outgoingDependencies','moduleName'], ascending=[False, True]).reset_index(drop=True).head(30)" + ] + }, + { + "cell_type": "markdown", + "id": "8c928800", + "metadata": {}, + "source": [ + "### Table 1d - Top 30 modules with the lowest element count" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18d73c15", + "metadata": {}, + "outputs": [], + "source": [ + "# Sort by number of elements ascending\n", + "internalModules.sort_values(by=['numberOfElements','moduleName'], ascending=[True, True]).reset_index(drop=True).head(30)" + ] + }, + { + "cell_type": "markdown", + "id": "9f628024", + "metadata": {}, + "source": [ + "### Table 1e - Top 30 modules with the lowest number of incoming dependencies\n", + "\n", + "The following table lists the top 30 internal modules that are used the least by other modules (lowest count of incoming dependencies, lowest in-degree)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6100d597", + "metadata": {}, + "outputs": [], + "source": [ + "# Sort by number of incoming dependencies ascending\n", + "internalModules.sort_values(by=['incomingDependencies','moduleName'], ascending=[True, True]).reset_index(drop=True).head(30)" + ] + }, + { + "cell_type": "markdown", + "id": "ad07b654", + "metadata": {}, + "source": [ + "### Table 1f - Top 30 modules with the lowest number of outgoing dependencies\n", + "\n", + "The following table lists the top 30 internal modules that are depending on the lowest number of other modules (lowest count of outgoing dependencies, lowest out-degree)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cd096a38", + "metadata": {}, + "outputs": [], + "source": [ + "# Sort by number of outgoing dependencies ascending\n", + "internalModules.sort_values(by=['outgoingDependencies','moduleName'], ascending=[True, True]).reset_index(drop=True).head(30)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "d7cfd862", + "metadata": {}, + "source": [ + "## 2 - Cyclic Dependencies\n", + "\n", + "Cyclic dependencies occur when one module uses an elements of another module and vice versa. \n", + "These dependencies can lead to problems when one of these modules needs to be changed." + ] + }, + { + "cell_type": "markdown", + "id": "9112f590", + "metadata": {}, + "source": [ + "### Table 2a - Cyclic Dependencies Overview\n", + "\n", + "Show the top 40 cyclic dependencies sorted by the most promising to resolve first. This is done by calculating the number of forward dependencies (first cycle participant to second cycle participant) in relation to backward dependencies (second cycle participant back to first cycle participant). The higher this rate (approaching 1), the easier it should be to resolve the cycle by focussing on the few backward dependencies.\n", + "\n", + "Only the top 40 entries are shown. The whole table can be found in the following CSV report: \n", + "`Cyclic_Dependencies_for_Typescript`\n", + "\n", + "**Columns:**\n", + "- *projectFileName* identifies the project of the first participant of the cycle\n", + "- *modulePathName* identifies the module of the first participant of the cycle\n", + "- *dependentProjectFileName* identifies the project of the second participant of the cycle\n", + "- *dependentModulePathName* identifies the module of the second participant of the cycle\n", + "- *forwardToBackwardBalance* is between 0 and 1. High for many forward and few backward dependencies.\n", + "- *numberForward* contains the number of dependencies from the first participant of the cycle to the second one\n", + "- *numberBackward* contains the number of dependencies from the second participant of the cycle back to the first one\n", + "- *someForwardDependencies* lists some forward dependencies in the text format \"type1 -> type2\"\n", + "- *backwardDependencies* lists the backward dependencies in the format \"type1 <- type2\" that are recommended to get resolved" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fa634a4e", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "cyclic_dependencies = query_cypher_to_data_frame(\"../cypher/Cyclic_Dependencies/Cyclic_Dependencies_for_Typescript.cypher\")\n", + "cyclic_dependencies.head(40)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "02146c4d", + "metadata": {}, + "source": [ + "### Table 2b - Cyclic Dependencies Break Down\n", + "\n", + "Lists modules with cyclic dependencies with every dependency in a separate row sorted by the most promising dependency first.\n", + "\n", + "Only the top 40 entries are shown. The whole table can be found in the following CSV report: \n", + "`Cyclic_Dependencies_Breakdown_for_Typescript`\n", + "\n", + "**Columns in addition to Table 2a:**\n", + "- *dependency* shows the cycle dependency in the text format \"type1 -> type2\" (forward) or \"type2<-type1\" (backward)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cf49a164", + "metadata": {}, + "outputs": [], + "source": [ + "cyclic_dependencies_breakdown = query_cypher_to_data_frame(\"../cypher/Cyclic_Dependencies/Cyclic_Dependencies_Breakdown_for_Typescript.cypher\",limit=40)\n", + "cyclic_dependencies_breakdown" + ] + }, + { + "cell_type": "markdown", + "id": "52f46a62", + "metadata": {}, + "source": [ + "### Table 2c - Cyclic Dependencies Break Down - Backward Dependencies Only\n", + "\n", + "Lists modules with cyclic dependencies with every dependency in a separate row sorted by the most promising dependency first. This table only contains the backward dependencies from the second participant of the cycle back to the first one that are the most promising to resolve.\n", + "\n", + "Only the top 40 entries are shown. The whole table can be found in the following CSV report: \n", + "`Cyclic_Dependencies_Breakdown_BackwardOnly_for_Typescript`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "61521c88", + "metadata": {}, + "outputs": [], + "source": [ + "cyclic_dependencies_breakdown_backward = query_cypher_to_data_frame(\"../cypher/Cyclic_Dependencies/Cyclic_Dependencies_Breakdown_Backward_Only_for_Typescript.cypher\",limit=40)\n", + "cyclic_dependencies_breakdown_backward" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4be116e8", + "metadata": {}, + "source": [ + "## 3 - Module Usage" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bb185191", + "metadata": {}, + "source": [ + "### Table 3a - Elements that are used by multiple modules\n", + "\n", + "This table shows the top 40 modules that are used by the highest number of different modules. The whole table can be found in the CSV report `WidelyUsedTypescriptElements`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "36a34924", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "elements_used_by_many_modules=query_cypher_to_data_frame(\"../cypher/Internal_Dependencies/List_elements_that_are_used_by_many_different_modules_for_Typescript.cypher\", limit=40)\n", + "elements_used_by_many_modules" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "73dd1b13", + "metadata": {}, + "source": [ + "### Table 3b - Elements that are used by multiple modules\n", + "\n", + "This table shows the top 30 modules that only use a few (compared to all existing) elements of another module.\n", + "The whole table can be found in the CSV report `ModuleElementsUsageTypescript`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bf3d43c5", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "used_packages_of_dependent_artifact=query_cypher_to_data_frame(\"../cypher/Internal_Dependencies/How_many_elements_compared_to_all_existing_are_used_by_dependent_modules_for_Typescript.cypher\",limit=30)\n", + "used_packages_of_dependent_artifact" + ] + } + ], + "metadata": { + "authors": [ + { + "name": "JohT" + } + ], + "code_graph_analysis_pipeline_data_validation": "ValidateTypescriptModuleDependencies", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + }, + "title": "Object Oriented Design Quality Metrics for Java with Neo4j" + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/scripts/reports/InternalDependenciesCsv.sh b/scripts/reports/InternalDependenciesCsv.sh index 068bf03f0..7338f526f 100755 --- a/scripts/reports/InternalDependenciesCsv.sh +++ b/scripts/reports/InternalDependenciesCsv.sh @@ -39,16 +39,27 @@ mkdir -p "${FULL_REPORT_DIRECTORY}" CYCLIC_DEPENDENCIES_CYPHER_DIR="${CYPHER_DIR}/Cyclic_Dependencies" INTERNAL_DEPENDENCIES_CYPHER_DIR="${CYPHER_DIR}/Internal_Dependencies" +# Cyclic Dependencies Java execute_cypher "${CYCLIC_DEPENDENCIES_CYPHER_DIR}/Cyclic_Dependencies.cypher" > "${FULL_REPORT_DIRECTORY}/Cyclic_Dependencies.csv" execute_cypher "${CYCLIC_DEPENDENCIES_CYPHER_DIR}/Cyclic_Dependencies_Breakdown.cypher" > "${FULL_REPORT_DIRECTORY}/Cyclic_Dependencies_Breakdown.csv" execute_cypher "${CYCLIC_DEPENDENCIES_CYPHER_DIR}/Cyclic_Dependencies_Breakdown_Backward_Only.cypher" > "${FULL_REPORT_DIRECTORY}/Cyclic_Dependencies_Breakdown_Backward_Only.csv" -execute_cypher "${CYCLIC_DEPENDENCIES_CYPHER_DIR}/Cyclic_Dependencies_between_Artrifacts_as_unwinded_List.cypher" > "${FULL_REPORT_DIRECTORY}/CyclicArtifactDependenciesUnwinded.csv" +execute_cypher "${CYCLIC_DEPENDENCIES_CYPHER_DIR}/Cyclic_Dependencies_between_Artifacts_as_unwinded_List.cypher" > "${FULL_REPORT_DIRECTORY}/CyclicArtifactDependenciesUnwinded.csv" -execute_cypher "${CYPHER_DIR}/Candidates_for_Interface_Segregation.cypher" > "${FULL_REPORT_DIRECTORY}/InterfaceSegregationCandidates.csv" +execute_cypher "${INTERNAL_DEPENDENCIES_CYPHER_DIR}/Candidates_for_Interface_Segregation.cypher" > "${FULL_REPORT_DIRECTORY}/InterfaceSegregationCandidates.csv" +execute_cypher "${INTERNAL_DEPENDENCIES_CYPHER_DIR}/List_all_Java_artifacts.cypher" > "${FULL_REPORT_DIRECTORY}/List_all_Java_artifacts.csv" execute_cypher "${INTERNAL_DEPENDENCIES_CYPHER_DIR}/List_types_that_are_used_by_many_different_packages.cypher" > "${FULL_REPORT_DIRECTORY}/WidelyUsedTypes.csv" execute_cypher "${INTERNAL_DEPENDENCIES_CYPHER_DIR}/How_many_packages_compared_to_all_existing_are_used_by_dependent_artifacts.cypher" > "${FULL_REPORT_DIRECTORY}/ArtifactPackageUsage.csv" execute_cypher "${INTERNAL_DEPENDENCIES_CYPHER_DIR}/How_many_classes_compared_to_all_existing_in_the_same_package_are_used_by_dependent_packages_across_different_artifacts.cypher" > "${FULL_REPORT_DIRECTORY}/ClassesPerPackageUsageAcrossArtifacts.csv" +# Internal Dependencies for TypeScript +execute_cypher "${CYCLIC_DEPENDENCIES_CYPHER_DIR}/Cyclic_Dependencies_for_Typescript.cypher" > "${FULL_REPORT_DIRECTORY}/Cyclic_Dependencies_for_Typescript.csv" +execute_cypher "${CYCLIC_DEPENDENCIES_CYPHER_DIR}/Cyclic_Dependencies_Breakdown_for_Typescript.cypher" > "${FULL_REPORT_DIRECTORY}/Cyclic_Dependencies_Breakdown_for_Typescript.csv" +execute_cypher "${CYCLIC_DEPENDENCIES_CYPHER_DIR}/Cyclic_Dependencies_Breakdown_Backward_Only_for_Typescript.cypher" > "${FULL_REPORT_DIRECTORY}/Cyclic_Dependencies_Breakdown_Backward_Only_for_Typescript.csv" + +execute_cypher "${INTERNAL_DEPENDENCIES_CYPHER_DIR}/List_all_Typescript_modules.cypher" > "${FULL_REPORT_DIRECTORY}/List_all_Typescript_modules.csv" +execute_cypher "${INTERNAL_DEPENDENCIES_CYPHER_DIR}/List_elements_that_are_used_by_many_different_modules_for_Typescript.cypher" > "${FULL_REPORT_DIRECTORY}/WidelyUsedTypescriptElements.csv" +execute_cypher "${INTERNAL_DEPENDENCIES_CYPHER_DIR}/How_many_elements_compared_to_all_existing_are_used_by_dependent_modules_for_Typescript.cypher" > "${FULL_REPORT_DIRECTORY}/ModuleElementsUsageTypescript.csv" + # Clean-up after report generation. Empty reports will be deleted. source "${SCRIPTS_DIR}/cleanupAfterReportGeneration.sh" "${FULL_REPORT_DIRECTORY}" \ No newline at end of file