Skip to content

Commit 9820732

Browse files
committed
rendering graph example with class
1 parent 70f66c5 commit 9820732

16 files changed

+366
-16
lines changed

README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
how to render a pipeline flow with graph and time chart
33

44
# usage
5-
to run the pipeline through
5+
The pipeline can bun run with
66

7-
python runner.py
7+
run
8+
9+
which will execute `runner.py` from venv
810

911
testing containers e.g. graphviz rendering
1012

cmd.cmd

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@echo off
2+
python containers\run.py %*

containers/docker-compose.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,9 @@ services:
55
dockerfile: graphviz.Dockerfile
66
volumes:
77
- ../cache:/data
8+
apache:
9+
image: httpd:latest
10+
volumes:
11+
- ../view:/usr/local/apache2/htdocs/
12+
ports:
13+
- 80:80

containers/run.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,21 @@
33
import os
44

55
def graphviz(filename):
6+
os.chdir(docker_compose_dir)
67
command_line = f"docker compose run --rm graphviz -Tsvg {filename} -o {filename}.svg"
78
subprocess.run(command_line.split(" "))
89
return
910

1011
def run(command):
1112
# Path to the directory containing docker-compose.yml
12-
docker_compose_dir = os.path.join(os.path.dirname(__file__))
13-
# Change the working directory
14-
os.chdir(docker_compose_dir)
15-
1613
if(command == "graphviz"):
1714
filename = sys.argv[2]
1815
graphviz(filename)
16+
elif(command == "server"):
17+
os.chdir(docker_compose_dir)
18+
subprocess.run("docker compose up apache", shell=True, check=True)
19+
20+
docker_compose_dir = os.path.join(os.path.dirname(__file__))
1921

2022
if __name__ == '__main__':
2123
# Default command if no argument is provided

graph_utils.py

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
2+
def int_to_alphabet(n):
3+
"""
4+
Converts an integer to a string where numbers are mapped as follows:
5+
1 -> A, 2 -> B, ..., 26 -> Z, 27 -> AA, 28 -> AB, and so on.
6+
"""
7+
result = []
8+
while n > 0:
9+
n -= 1 # Decrement n by 1 to make the modulus operation zero-indexed
10+
remainder = n % 26
11+
result.append(chr(remainder + 65)) # 65 is the ASCII value for 'A'
12+
n = n // 26 # Move to the next 'digit'
13+
14+
return ''.join(reversed(result))
15+
16+
def graph_to_dot(graph):
17+
dot_string = "digraph G {\n"
18+
nodes_map = {}
19+
for index,node in enumerate(graph["nodes"],1):
20+
nodes_map[node] = int_to_alphabet(index)
21+
for node in graph["nodes"]:
22+
dot_string += f' {nodes_map[node]} [label="{node}"];\n'
23+
for edge in graph["edges"]:
24+
source = nodes_map[edge["source"]]
25+
target = nodes_map[edge["target"]]
26+
dot_string += f' {source} -> {target};\n'
27+
dot_string += "}"
28+
return dot_string
29+
30+
def add_edge(source,target):
31+
global graph
32+
if(source not in graph["nodes"]):
33+
graph["nodes"].append(source)
34+
if(target not in graph["nodes"]):
35+
graph["nodes"].append(target)
36+
graph["edges"].append({
37+
"source":source,
38+
"target":target
39+
})
40+
return
41+
42+
def get_dot_graph():
43+
global graph
44+
dot_string = graph_to_dot(graph)
45+
return dot_string
46+
47+
def get_graph():
48+
global graph
49+
return graph
50+
51+
graph = {
52+
"nodes":[],
53+
"edges":[]
54+
}

pipeline.py

+9
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,22 @@ def fetch():
1313
def calculate():
1414
print("- hello from calc2")
1515
new_data = run.get_artifact("content")
16+
time.sleep(0.3)
1617
print(new_data)
18+
run.set_artifact({"stats":5},"fetch/stats.json")
1719
return
1820

1921
def compute():
2022
print("- hellor from comp3")
23+
new_data = run.get_artifact("content")
24+
time.sleep(0.4)
25+
run.set_artifact({"result":6},"fetch/result.json")
2126
return
2227

2328
def build():
2429
print("- hi build4")
30+
stats = run.get_artifact("stats")
31+
result = run.get_artifact("result")
32+
time.sleep(0.5)
33+
run.set_artifact({"web":10},"fetch/website.json")
2534
return

requirements.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pyyaml
2+
networkx

run.cmd

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
@echo off
2-
python containers\run.py %*
2+
venv\Scripts\python runner.py

runner.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from os.path import dirname,splitext,basename,join
44
import state
55
from datetime import datetime
6+
import graph_utils as gutl
7+
from containers import run
68

79
class ArtifactError(Exception):
810
"""Custom exception for artifact management errors."""
@@ -34,12 +36,14 @@ def set_artifact(data,filepath,type="generic"):
3436
abs_filepath = join("cache",filepath)
3537
if(ext == ".json"):
3638
utl.save_json(data,abs_filepath)
39+
gutl.add_edge(state.job,id)
3740
return
3841

3942
def get_artifact(id):
4043
if(id not in state.artifacts):
4144
raise ArtifactError(f"Artifact with ID '{id}' does not exist")
4245
artifact = state.artifacts[id]
46+
gutl.add_edge(id,state.job)
4347
if(artifact["ext"] == ".json"):
4448
return utl.load_json(join("cache",artifact["filepath"]))
4549
return None
@@ -64,7 +68,9 @@ def run_pipeline(pipeline):
6468
run_stage(stage, jobs)
6569
utl.save_json(state.artifacts,"cache/artifacts.json")
6670
utl.save_json(state.pipe,"cache/pipeline.json")
67-
71+
utl.save_json(gutl.get_graph(),"cache/dependencies.json")
72+
utl.save_text(gutl.get_dot_graph(),"cache/dependencies.dot")
73+
run.graphviz("dependencies.dot")
6874

6975
if __name__ == '__main__':
7076
manifest = utl.load_yaml("manifest.yaml")

test-graph.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import graph_utils as gutl
2+
import utils as utl
3+
from containers import run
4+
5+
graph = {
6+
"nodes": [
7+
{"id": "A", "label": "Node A"},
8+
{"id": "B", "label": "Node B"},
9+
{"id": "C", "label": "Node C"}
10+
],
11+
"edges": [
12+
{"source": "A", "target": "B", "label": "A to B"},
13+
{"source": "B", "target": "C", "label": "B to C"}
14+
]
15+
}
16+
17+
dot = gutl.graph_to_dot(graph)
18+
19+
utl.save_text(dot,"cache/test/gen.dot")
20+
21+
run.graphviz("test/gen.dot")

test-manifest.yaml

-8
This file was deleted.

utils.py

+8
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ def save_json(data,fileName):
2323
jfile.close()
2424
return
2525

26+
def save_text(data,fileName):
27+
path = dirname(fileName)
28+
makedirs(path, exist_ok=True)
29+
jfile = open(fileName, "w")
30+
jfile.write(data)
31+
jfile.close()
32+
return
33+
2634
def duration_text(duration: timedelta):
2735
# Ensure that the duration is non-negative
2836
if duration < timedelta(0):

view/dependencies.dot

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
digraph G {
2+
A [label="fetch-data-1"];
3+
B [label="content" class="artifact"];
4+
C [label="calculate-functions"];
5+
D [label="stats" class="artifact"];
6+
E [label="compute-statistics"];
7+
F [label="result" class="artifact"];
8+
G [label="generate-website"];
9+
H [label="website" class="artifact"];
10+
A -> B;
11+
B -> C;
12+
C -> D;
13+
B -> E;
14+
E -> F;
15+
D -> G;
16+
F -> G;
17+
G -> H;
18+
}

view/dependencies.dot.svg

+109
Loading

0 commit comments

Comments
 (0)