From 999c5032941ec108ba36cd293bdb334ad0db05a3 Mon Sep 17 00:00:00 2001 From: Andy Bunce Date: Wed, 21 May 2025 16:12:37 +0100 Subject: [PATCH] [add] graphviz --- graphviz/Dockerfile | 10 +++++++ graphviz/compose.yml | 7 +++++ graphviz/readme.md | 7 +++++ graphviz/requirements.txt | 3 ++ graphviz/server.py | 60 +++++++++++++++++++++++++++++++++++++++ graphviz/test.dot | 11 +++++++ 6 files changed, 98 insertions(+) create mode 100644 graphviz/Dockerfile create mode 100644 graphviz/compose.yml create mode 100644 graphviz/readme.md create mode 100644 graphviz/requirements.txt create mode 100644 graphviz/server.py create mode 100644 graphviz/test.dot diff --git a/graphviz/Dockerfile b/graphviz/Dockerfile new file mode 100644 index 0000000..a8030f2 --- /dev/null +++ b/graphviz/Dockerfile @@ -0,0 +1,10 @@ +FROM alpine:3 + +RUN apk add --no-cache \ + graphviz python3 py-pip + +ADD requirements.txt ./ +RUN pip3 install -r requirements.txt --break-system-packages + +ADD server.py ./ +CMD hug -f server.py diff --git a/graphviz/compose.yml b/graphviz/compose.yml new file mode 100644 index 0000000..c1ee058 --- /dev/null +++ b/graphviz/compose.yml @@ -0,0 +1,7 @@ +name: graphviz +services: + graphviz: + ports: + - 8000:8000 + image: graphviz + restart: unless-stopped \ No newline at end of file diff --git a/graphviz/readme.md b/graphviz/readme.md new file mode 100644 index 0000000..408485d --- /dev/null +++ b/graphviz/readme.md @@ -0,0 +1,7 @@ + + +``` +docker build -t graphviz . +docker run -p8000:8000 graphviz +``` +Based on https://github.com/sseemayer/docker-graphviz diff --git a/graphviz/requirements.txt b/graphviz/requirements.txt new file mode 100644 index 0000000..846c5ad --- /dev/null +++ b/graphviz/requirements.txt @@ -0,0 +1,3 @@ +hug==2.6.1 +hug-middleware-cors==1.0.0 +six==1.16.0 \ No newline at end of file diff --git a/graphviz/server.py b/graphviz/server.py new file mode 100644 index 0000000..62b1033 --- /dev/null +++ b/graphviz/server.py @@ -0,0 +1,60 @@ +import hug +from hug.middleware import CORSMiddleware +from six import BytesIO +import subprocess +import logging +from tempfile import NamedTemporaryFile as ntf + +logging.basicConfig() +api = hug.API(__name__) +api.http.add_middleware(CORSMiddleware(api, allow_credentials=True)) + +@hug.not_found() +def not_found_handler(): + return "Not Found" + +@hug.get_post('/viz.svg', output=hug.output_format.image("svg+xml")) +@hug.get_post('/viz.png', output=hug.output_format.image("png")) +@hug.get_post('/viz.dot', output=hug.output_format.text) +@hug.get_post('/viz.xdot', output=hug.output_format.text) +def demo( + dot: 'A Graphviz dot document', + request, + response, + algorithm: hug.types.one_of(['dot', 'neato', 'twopi', 'circo', 'fdp', 'sfdp', 'patchwork', 'osage'])='dot', +): + + suffix = request.path.split(".")[-1] + + # Enforce unicode strings + try: + dot = dot.decode("utf-8") + except AttributeError: + pass + + with ntf(suffix=".dot", mode="w") as f_dot, ntf(mode="r+b") as f_out, ntf(mode="r") as f_err: + f_dot.write(dot) + f_dot.flush() + + cmd = [ + algorithm, + '-T', + suffix, + f_dot.name, + '-o', + f_out.name + ] + + proc = subprocess.Popen(cmd, stdout=f_err, stderr=subprocess.STDOUT) + + ret = proc.wait() + + if ret != 0: + response.status = hug.HTTP_500 + f_err.seek(0) + return {"status_code": ret, "message": f_err.read()} + + f_out.seek(0) + out_data = f_out.read() + + return BytesIO(out_data) diff --git a/graphviz/test.dot b/graphviz/test.dot new file mode 100644 index 0000000..024f822 --- /dev/null +++ b/graphviz/test.dot @@ -0,0 +1,11 @@ + + +digraph D { + + node [fontname="Arial"]; + + node_A [shape=record label="shape=record|{above|middle|below}|right"]; + node_B [shape=plaintext label="shape=plaintext|{curly|braces and|bars without}|effect"]; + + +}