Skip to content

Commit 3b7ea0a

Browse files
committed
Add scripts to visualize query results with GraphViz
1 parent f382905 commit 3b7ea0a

File tree

4 files changed

+204
-0
lines changed

4 files changed

+204
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ The [Code Structure Analysis Pipeline](./.github/workflows/java-code-analysis.ym
124124
- [openTSNE](https://github.com/pavlin-policar/openTSNE)
125125
- [wordcloud](https://github.com/amueller/word_cloud)
126126
- [Graph Visualization](./graph-visualization/README.md) uses [node.js](https://nodejs.org/de) and the dependencies listed in [package.json](./graph-visualization/package.json).
127+
- [HPCC-Systems (High Performance Computing Cluster) Web-Assembly (JavaScript)](https://github.com/hpcc-systems/hpcc-js-wasm) containing a wrapper for GraphViz to visualize graph structures.
128+
- [GraphViz](https://gitlab.com/graphviz/graphviz) for CLI Graph Visualization
127129
- [Check links in markdown documentation (GitHub workflow)](./.github/workflows/check-links-in-documentation.yml) uses [markdown-link-check](https://github.com/tcort/markdown-link-check).
128130

129131
**Big shout-out** 📣 to all the creators and contributors of these great libraries 👍. Projects like this wouldn't be possible without them. Feel free to [create an issue](https://github.com/JohT/code-graph-analysis-pipeline/issues/new/choose) if something is missing or wrong in the list.
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#!/usr/bin/env bash
2+
3+
# Converts a Cypher query result in CSV format to a GraphViz DOT (https://graphviz.org/doc/info/lang.html) file for Visualization including layout templates.
4+
#
5+
# The template file is used to define the layout of the graph.
6+
# It needs to contain the markers //Begin-Template and //End-Template to indicate the actual template content.
7+
# If no template is specified, "templates/default.template.gv" is used.
8+
9+
# Fail on any error ("-e" = exit on first error, "-o pipefail" exist on errors within piped commands)
10+
set -o errexit -o pipefail
11+
12+
## Get this "scripts/visualization" directory if not already set
13+
# Even if $BASH_SOURCE is made for Bourne-like shells it is also supported by others and therefore here the preferred solution.
14+
# CDPATH reduces the scope of the cd command to potentially prevent unintended directory changes.
15+
# This way non-standard tools like readlink aren't needed.
16+
VISUALIZATION_SCRIPTS_DIR=${VISUALIZATION_SCRIPTS_DIR:-$( CDPATH=. cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P )} # Repository directory containing the shell scripts for visualization
17+
echo "convertQueryResultCsvToGraphVizDotFile: VISUALIZATION_SCRIPTS_DIR=${VISUALIZATION_SCRIPTS_DIR}"
18+
19+
# Internal constants
20+
DEFAULT_TEMPLATE_FILE="templates/default.template.gv"
21+
22+
# Function to display script usage
23+
usage() {
24+
echo "Usage: $0 --filename path/to/query/result/file.csv [--name name-of-the-graph (default=name of the file)] [--template path/to/the/template/file (default=${DEFAULT_TEMPLATE_FILE})]"
25+
exit 1
26+
}
27+
28+
# Default values
29+
inputFilename=""
30+
graphName=""
31+
templateFile=""
32+
33+
# Parse command line arguments
34+
while [ $# -gt 0 ]; do
35+
key="$1"
36+
case $key in
37+
--filename)
38+
inputFilename="$2"
39+
shift
40+
;;
41+
--name)
42+
graphName="$2"
43+
shift
44+
;;
45+
--template)
46+
templateFile="$2"
47+
shift
48+
;;
49+
*)
50+
echo "convertQueryResultCsvToGraphVizDotFile: Error: Unknown option: ${key}"
51+
usage
52+
;;
53+
esac
54+
shift
55+
done
56+
57+
if [ -z "${inputFilename}" ]; then
58+
echo "convertQueryResultCsvToGraphVizDotFile: Error: Missing required option: --filename"
59+
echo "${USAGE}"
60+
exit 1
61+
fi
62+
63+
if [ ! -f "${inputFilename}" ]; then
64+
echo "convertQueryResultCsvToGraphVizDotFile: Error: CSV file not found: ${inputFilename}"
65+
echo "${USAGE}"
66+
exit 1
67+
fi
68+
69+
number_of_input_file_lines=$(wc -l < "${inputFilename}" | awk '{print $1}')
70+
if [ "${number_of_input_file_lines}" -le 1 ]; then
71+
echo "convertQueryResultCsvToGraphVizDotFile: Info: Input file is empty. Skipping *.dot and *.svg file generation."
72+
return 0
73+
fi
74+
75+
if [ -z "${graphName}" ]; then
76+
graphName=$(basename -- "${inputFilename}")
77+
graphName="${graphName%.*}"
78+
echo "convertQueryResultCsvToGraphVizDotFile: Info: Using default graph name: ${graphName}"
79+
fi
80+
81+
# Replace all dashes in the graphName by underscores
82+
graphName=${graphName//-/_}
83+
84+
if [ -z "${templateFile}" ]; then
85+
templateFile="${VISUALIZATION_SCRIPTS_DIR}/${DEFAULT_TEMPLATE_FILE}"
86+
echo "convertQueryResultCsvToGraphVizDotFile: Info: Using default template file: ${templateFile}"
87+
fi
88+
89+
if [ ! -f "${templateFile}" ]; then
90+
echo "convertQueryResultCsvToGraphVizDotFile: Error: Template file not found: ${templateFile}"
91+
echo "${USAGE}"
92+
exit 1
93+
fi
94+
95+
templateFileName=$(basename -- "${templateFile}")
96+
inputFilePath=$(dirname "${inputFilename}")
97+
templateFileNameWithoutExtension="${templateFileName%.*}"
98+
outputFilename="${inputFilePath}/${graphName}.gv"
99+
100+
{
101+
# Add a comment to the beginning of the file to indicate that it was generated by this script
102+
echo "// This GraphViz dot file was generated by the script convertQueryResultCsvToGraphVizDotFile.sh with ${templateFileNameWithoutExtension}"
103+
echo ""
104+
# Start the graph definition with the graph name
105+
echo "strict digraph ${graphName} {"
106+
# Extract the template content from the template file and remove the begin and end markers
107+
sed -n '/\/\/Begin-Template/,/\/\/End-Template/{//!p;}' "${templateFile}"
108+
# Remove the first (header) line of the CSV file, remove the enclosing double quotes and replace the escaped double quotes by double quotes
109+
awk -F ',' 'NR>1 {print "\t" $1}' "${inputFilename}" \
110+
| sed 's/^\t\"\"\"/\t"/' \
111+
| sed 's/^\t\"\\\"\"/\t"/' \
112+
| sed 's/\\\"\"/"/g' \
113+
| sed 's/\"\"/"/g' \
114+
| sed 's/\"$//'
115+
# End the graph definition
116+
echo "}"
117+
} > "${outputFilename}"
118+
119+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// This is a GraphViz dot template file for the visualization of a graph in lightblue color scheme.
2+
// The main part of the template is marked by the comments "Begin-Template" and "End-Template".
3+
// It also contains a simple example graph.
4+
//
5+
strict digraph lightblue_template {
6+
//Begin-Template
7+
fontname = "Helvetica,Arial,sans-serif";
8+
node [fontname = "Helvetica,Arial,sans-serif";];
9+
edge [fontname = "Helvetica,Arial,sans-serif"; fontsize = 10;];
10+
node [style = filled; fillcolor = "0.560 0.400 0.999";];
11+
node [color = "0.560 0.900 0.700";];
12+
edge [color = "0.560 0.900 0.700";];
13+
//End-Template
14+
"A" -> "B" [penwidth = 1.0; label = 1;];
15+
"A" -> "C" [penwidth = 3.0; label = 4;];
16+
"B" -> "V" [penwidth = 2.0; label = 2;];
17+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/usr/bin/env bash
2+
3+
# Visualizes the Cypher query result (CSV format) using GraphViz and outputs it as SVG image.
4+
#
5+
# Requires convertQueryResultCsvToGraphVizDotFile.sh
6+
#
7+
# Fail on any error ("-e" = exit on first error, "-o pipefail" exist on errors within piped commands)
8+
set -o errexit -o pipefail
9+
10+
## Get this "scripts/visualization" directory if not already set
11+
# Even if $BASH_SOURCE is made for Bourne-like shells it is also supported by others and therefore here the preferred solution.
12+
# CDPATH reduces the scope of the cd command to potentially prevent unintended directory changes.
13+
# This way non-standard tools like readlink aren't needed.
14+
VISUALIZATION_SCRIPTS_DIR=${VISUALIZATION_SCRIPTS_DIR:-$( CDPATH=. cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P )} # Repository directory containing the shell scripts for visualization
15+
echo "visualizeQueryResults: VISUALIZATION_SCRIPTS_DIR=${VISUALIZATION_SCRIPTS_DIR}"
16+
17+
# Read the first unnamed input argument containing the version of the project
18+
inputCsvFileName=""
19+
case "${1}" in
20+
"--"*) ;; # Skipping named command line options to forward them later to the "analyze" command
21+
*)
22+
inputCsvFileName="${1}"
23+
shift || true
24+
;;
25+
esac
26+
27+
if [ -z "${inputCsvFileName}" ]; then
28+
echo "visualizeQueryResults: Error: Please specify the CSV query result file as input parameter."
29+
exit 1
30+
fi
31+
32+
if [ ! -f "${inputCsvFileName}" ]; then
33+
echo "visualizeQueryResults: Error: CSV file not found: ${inputCsvFileName}"
34+
exit 1
35+
fi
36+
37+
number_of_input_file_lines=$(wc -l < "${inputCsvFileName}" | awk '{print $1}')
38+
if [ "${number_of_input_file_lines}" -le 1 ]; then
39+
echo "visualizeQueryResults: Info: Input file is empty. Skipping *.dot and *.svg file generation."
40+
return 0
41+
fi
42+
43+
echo "visualizeQueryResults: Info: CSV input file: ${inputCsvFileName}"
44+
45+
graphName=$(basename -- "${inputCsvFileName}")
46+
graphName="${graphName%.*}" # Remove file extension
47+
graphName=${graphName//-/_} # Replace all dashes in the graphName by underscores
48+
inputCsvFilePath=$(dirname "${inputCsvFileName}")
49+
50+
echo "visualizeQueryResults: Generating Visualization files ${inputCsvFilePath}/${graphName}.* ..."
51+
source "${VISUALIZATION_SCRIPTS_DIR}/convertQueryResultCsvToGraphVizDotFile.sh" "--filename" "${inputCsvFileName}" "${@}"
52+
53+
if command -v "dot" &> /dev/null ; then
54+
echo "visualizeQueryResults: Info: Using already installed GraphViz."
55+
dot -T svg "${inputCsvFilePath}/${graphName}.gv" > "${inputCsvFilePath}/${graphName}.svg"
56+
return 0
57+
fi
58+
59+
if ! command -v "npx" &> /dev/null ; then
60+
echo "visualizeQueryResults: Error: Command npx (to run npm locally) not found. It's needed for Graph visualization with GraphViz." >&2
61+
exit 1
62+
fi
63+
64+
# Run GraphViz command line interface (CLI) wrapped utilizing WASM (WebAssembly)
65+
# to convert the DOT file to SVG operating system independently.
66+
npx --yes @hpcc-js/[email protected] -T svg "${inputCsvFilePath}/${graphName}.gv" > "${inputCsvFilePath}/${graphName}.svg"

0 commit comments

Comments
 (0)