Skip to content

Commit ca50b93

Browse files
author
Mark Robinson
committed
Support for xdot format download for workflows
1 parent 2a63b2f commit ca50b93

File tree

6 files changed

+123
-44
lines changed

6 files changed

+123
-44
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.commonwl.viewer.services;
21+
22+
import com.github.jabbalaci.graphviz.GraphViz;
23+
import org.springframework.beans.factory.annotation.Autowired;
24+
import org.springframework.beans.factory.annotation.Value;
25+
import org.springframework.stereotype.Service;
26+
27+
import java.io.File;
28+
29+
/**
30+
* Handles Graphviz rendering from DOT generated by DotWriter
31+
*/
32+
@Service
33+
public class GraphVizService {
34+
35+
private final String graphvizStorage;
36+
37+
@Autowired
38+
public GraphVizService(@Value("${graphvizStorage}") String graphvizStorage) {
39+
this.graphvizStorage = graphvizStorage;
40+
}
41+
42+
/**
43+
* Generate a graph in a specified format using GraphViz
44+
* @param fileName The name of the file to be generated in the graphvizStorage directory
45+
* @param dot The DOT source
46+
* @param format The format for the graph to be generated in
47+
* @return The file containing the graph
48+
*/
49+
public File getGraph(String fileName, String dot, String format) {
50+
// Generate graphviz image if it does not already exist
51+
File out = new File(graphvizStorage + "/" + fileName);
52+
if (!out.exists()) {
53+
GraphViz gv = new GraphViz();
54+
55+
// Different DPI and transparency for svg files
56+
if (format.equals("svg")) {
57+
gv.decreaseDpi();
58+
gv.decreaseDpi();
59+
gv.decreaseDpi();
60+
dot = dot.replace("bgcolor = \"#eeeeee\"", "bgcolor = \"transparent\"");
61+
}
62+
63+
gv.writeGraphToFile(gv.getGraph(dot, format, "dot"), out.getAbsolutePath());
64+
}
65+
return out;
66+
}
67+
68+
/**
69+
* Delete the cache of workflow images for a workflow
70+
* TODO: Make this more dynamic in some way, either store in workflow object or clear folder
71+
* @param workflowID The ID of the workflow used for assuming file locations
72+
*/
73+
public void deleteCache(String workflowID) {
74+
File graphvizSvg = new File(graphvizStorage + "/" + workflowID + ".svg");
75+
graphvizSvg.delete();
76+
File graphvizPng = new File(graphvizStorage + "/" + workflowID + ".png");
77+
graphvizPng.delete();
78+
File graphvizXdot = new File(graphvizStorage + "/" + workflowID + ".dot");
79+
graphvizXdot.delete();
80+
}
81+
}

src/main/java/org/commonwl/viewer/services/WorkflowService.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,24 +40,24 @@ public class WorkflowService {
4040
private final GitHubService githubService;
4141
private final WorkflowRepository workflowRepository;
4242
private final ROBundleFactory ROBundleFactory;
43+
private final GraphVizService graphVizService;
4344
private final int cacheDays;
44-
private final String graphvizStorage;
4545
private final int singleFileSizeLimit;
4646
private final int totalFileSizeLimit;
4747

4848
@Autowired
4949
public WorkflowService(GitHubService githubService,
5050
WorkflowRepository workflowRepository,
5151
ROBundleFactory ROBundleFactory,
52+
GraphVizService graphVizService,
5253
@Value("${cacheDays}") int cacheDays,
53-
@Value("${graphvizStorage}") String graphvizStorage,
5454
@Value("${singleFileSizeLimit}") int singleFileSizeLimit,
5555
@Value("${totalFileSizeLimit}") int totalFileSizeLimit) {
5656
this.githubService = githubService;
5757
this.workflowRepository = workflowRepository;
5858
this.ROBundleFactory = ROBundleFactory;
59+
this.graphVizService = graphVizService;
5960
this.cacheDays = cacheDays;
60-
this.graphvizStorage = graphvizStorage;
6161
this.singleFileSizeLimit = singleFileSizeLimit;
6262
this.totalFileSizeLimit = totalFileSizeLimit;
6363
}
@@ -128,10 +128,7 @@ public void removeWorkflow(Workflow workflow) {
128128
}
129129

130130
// Delete cached graphviz images if they exist
131-
File graphvizSvg = new File(graphvizStorage + "/" + workflow.getID() + ".svg");
132-
graphvizSvg.delete();
133-
File graphvizPng = new File(graphvizStorage + "/" + workflow.getID() + ".png");
134-
graphvizPng.delete();
131+
graphVizService.deleteCache(workflow.getID());
135132

136133
// Remove the workflow from the database
137134
workflowRepository.delete(workflow);

src/main/java/org/commonwl/viewer/web/WorkflowController.java

Lines changed: 24 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919

2020
package org.commonwl.viewer.web;
2121

22-
import com.github.jabbalaci.graphviz.GraphViz;
2322
import org.apache.commons.lang.StringUtils;
2423
import org.commonwl.viewer.domain.GithubDetails;
2524
import org.commonwl.viewer.domain.Workflow;
2625
import org.commonwl.viewer.domain.WorkflowForm;
26+
import org.commonwl.viewer.services.GraphVizService;
2727
import org.commonwl.viewer.services.WorkflowFormValidator;
2828
import org.commonwl.viewer.services.WorkflowRepository;
2929
import org.commonwl.viewer.services.WorkflowService;
@@ -55,6 +55,7 @@ public class WorkflowController {
5555
private final WorkflowFormValidator workflowFormValidator;
5656
private final WorkflowService workflowService;
5757
private final WorkflowRepository workflowRepository;
58+
private final GraphVizService graphVizService;
5859

5960
/**
6061
* Autowired constructor to initialise objects used by the controller
@@ -64,10 +65,12 @@ public class WorkflowController {
6465
@Autowired
6566
public WorkflowController(WorkflowFormValidator workflowFormValidator,
6667
WorkflowService workflowService,
67-
WorkflowRepository workflowRepository) {
68+
WorkflowRepository workflowRepository,
69+
GraphVizService graphVizService) {
6870
this.workflowFormValidator = workflowFormValidator;
6971
this.workflowService = workflowService;
7072
this.workflowRepository = workflowRepository;
73+
this.graphVizService = graphVizService;
7174
}
7275

7376
/**
@@ -239,29 +242,12 @@ public FileSystemResource downloadROBundle(@PathVariable("workflowID") String wo
239242
method = RequestMethod.GET,
240243
produces = "image/svg+xml")
241244
@ResponseBody
242-
public FileSystemResource getGraphAsSvg(@Value("${graphvizStorage}") String graphvizStorage,
243-
@PathVariable("workflowID") String workflowID) throws IOException {
244-
245-
// Get workflow from database
245+
public FileSystemResource getGraphAsSvg(@PathVariable("workflowID") String workflowID) throws IOException {
246246
Workflow workflowModel = workflowRepository.findOne(workflowID);
247-
248-
// 404 error if workflow does not exist
249247
if (workflowModel == null) {
250248
throw new WorkflowNotFoundException();
251249
}
252-
253-
// Generate graphviz image if it does not already exist
254-
File out = new File(graphvizStorage + "/" + workflowID + ".svg");
255-
if (!out.exists()) {
256-
GraphViz gv = new GraphViz();
257-
gv.decreaseDpi();
258-
gv.decreaseDpi();
259-
gv.decreaseDpi();
260-
gv.writeGraphToFile(gv.getGraph(workflowModel.getDotGraph()
261-
.replace("bgcolor = \"#eeeeee\"", "bgcolor = \"transparent\""), "svg", "dot"), out.getAbsolutePath());
262-
}
263-
264-
// Output the graph image
250+
File out = graphVizService.getGraph(workflowID + ".svg", workflowModel.getDotGraph(), "svg");
265251
return new FileSystemResource(out);
266252
}
267253

@@ -273,25 +259,29 @@ public FileSystemResource getGraphAsSvg(@Value("${graphvizStorage}") String grap
273259
method = RequestMethod.GET,
274260
produces = "image/png")
275261
@ResponseBody
276-
public FileSystemResource getGraphAsPng(@Value("${graphvizStorage}") String graphvizStorage,
277-
@PathVariable("workflowID") String workflowID) throws IOException {
278-
279-
// Get workflow from database
262+
public FileSystemResource getGraphAsPng(@PathVariable("workflowID") String workflowID) throws IOException {
280263
Workflow workflowModel = workflowRepository.findOne(workflowID);
281-
282-
// 404 error if workflow does not exist
283264
if (workflowModel == null) {
284265
throw new WorkflowNotFoundException();
285266
}
267+
File out = graphVizService.getGraph(workflowID + ".png", workflowModel.getDotGraph(), "png");
268+
return new FileSystemResource(out);
269+
}
286270

287-
// Generate graphviz image if it does not already exist
288-
File out = new File(graphvizStorage + "/" + workflowID + ".png");
289-
if (!out.exists()) {
290-
GraphViz gv = new GraphViz();
291-
gv.writeGraphToFile(gv.getGraph(workflowModel.getDotGraph(), "png", "dot"), out.getAbsolutePath());
271+
/**
272+
* Download a generated DOT graph for a workflow as xdot
273+
* @param workflowID The ID of the workflow to download the graph for
274+
*/
275+
@RequestMapping(value = "/workflows/{workflowID}/graph/xdot",
276+
method = RequestMethod.GET,
277+
produces = "text/vnd.graphviz")
278+
@ResponseBody
279+
public FileSystemResource getGraphAsXdot(@PathVariable("workflowID") String workflowID) throws IOException {
280+
Workflow workflowModel = workflowRepository.findOne(workflowID);
281+
if (workflowModel == null) {
282+
throw new WorkflowNotFoundException();
292283
}
293-
294-
// Output the graph image
284+
File out = graphVizService.getGraph(workflowID + ".dot", workflowModel.getDotGraph(), "xdot");
295285
return new FileSystemResource(out);
296286
}
297287
}

src/main/resources/static/css/main.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,15 @@ body {
156156
margin-bottom: 5px;
157157
}
158158

159+
#dot {
160+
margin-top: 5px;
161+
margin-bottom: 10px;
162+
}
163+
164+
#modalTitle {
165+
margin-top: 0;
166+
}
167+
159168
tr.selected, .table-hover tbody tr.selected:hover td, .table-hover tbody tr.selected:hover th {
160169
background-color: #b9ffbb !important;
161170
}

src/main/resources/static/js/workflow.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ require(['jquery', 'bootstrap.modal'],
191191
$('#download-dot').click(function (event) {
192192
// Generate download link src
193193
var dotGraph = $("#dot").val();
194-
var src = "data:text/plain;charset=utf-8," + encodeURIComponent(dotGraph);
194+
var src = "data:text/vnd.graphviz;charset=utf-8," + encodeURIComponent(dotGraph);
195195

196196
// Set hidden download link href to contents and click it
197197
var downloadLink = $("#download-link-dot");

src/main/resources/templates/workflow.html

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@
4444
<h4 class="modal-title" id="dotGraphLabel">Workflow DOT Graph</h4>
4545
</div>
4646
<div class="modal-body">
47-
<a id="download-link-dot" href="" download="workflow.dot"></a>
48-
<button id="download-dot" class="btn btn-primary" type="button">Download as .dot File</button>
47+
<h4 id="modalTitle">Dot File Source:</h4>
4948
<textarea id="dot" class="form-control" rows="15" th:field="${workflow.dotGraph}">
5049
digraph G {
5150
graph [
@@ -96,6 +95,9 @@ <h4 class="modal-title" id="dotGraphLabel">Workflow DOT Graph</h4>
9695
input3->step3->output3;
9796
}
9897
</textarea>
98+
<a id="download-link-dot" href="" download="workflow.dot"></a>
99+
<button id="download-dot" class="btn btn-primary" type="button">Download dot File</button>
100+
<a th:href="@{'/workflows/' + ${workflow.id} + '/graph/xdot'}" href="#" download="graph.dot" class="btn btn-primary" type="button">Download xdot File</a>
99101
</div>
100102
</div>
101103
</div>

0 commit comments

Comments
 (0)