Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
299 lines (256 sloc) 12.3 KB
<!DOCTYPE html>
<title>FlowCraft DAG tool</title>
<meta charset="utf-8"/>
<style>
.node circle {
stroke: steelblue;
stroke-width: 3px;
}
.node text {
font: 14px sans-serif;
font-weight: bold;
}
.link {
fill: none;
stroke: #acacac;
stroke-width: 2px;
}
div.tooltip {
position: absolute;
text-align: center;
padding: 10px 15px 10px 15px;
font: 14px sans-serif;
background: lightsteelblue;
box-shadow: 1px 2px 8px #626262;
border-radius: 8px;
pointer-events: none;
}
</style>
<body>
</body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
// fetchs data using jinja
const inputData = {'name': 'root', 'children': [{'name': 'integrity_coverage_1_1', 'process': {'pid': '1_1', 'input': 'fastq', 'output': 'fastq', 'lane': 1, 'directives': 'integrity_coverage'}, 'children': [{'name': 'fastqc_trimmomatic_1_2', 'process': {'pid': '1_2', 'input': 'fastq', 'output': 'fastq', 'lane': 1, 'directives': 'fastqccpus: 2memory: 4GBcontainer: flowcraft/fastqcversion: 0.11.7-1trimmomaticcpus: 2memory: { 4.GB * task.attempt }container: flowcraft/trimmomaticversion: 0.36-1'}, 'children': [{'name': 'filter_poly_1_3', 'process': {'pid': '1_3', 'input': 'fastq', 'output': 'fastq', 'lane': 1, 'directives': 'filter_polycpus: 1memory: { 4.GB * task.attempt }container: flowcraft/prinseqversion: 0.20.4-1'}, 'children': [{'name': 'bowtie_1_4', 'process': {'pid': '1_4', 'input': 'fastq', 'output': 'bam', 'lane': 1, 'directives': 'bowtiecontainer: flowcraft/bowtie2_samtoolsversion: 1.0.0-1memory: {5.Gb*task.attempt}cpus: 4bowtie_buildcontainer: flowcraft/bowtie2_samtoolsversion: 1.0.0-1memory: {5.Gb*task.attempt}cpus: 1'}, 'children': [{'name': 'retrieve_mapped_1_5', 'process': {'pid': '1_5', 'input': 'bam', 'output': 'fastq', 'lane': 1, 'directives': 'retrieve_mappedcontainer: flowcraft/bowtie2_samtoolsversion: 1.0.0-1memory: {5.Gb*task.attempt}cpus: 2'}, 'children': [{'name': 'check_coverage_1_6', 'process': {'pid': '1_6', 'input': 'fastq', 'output': 'fastq', 'lane': 1, 'directives': 'check_coverage'}, 'children': [{'name': 'viral_assembly_1_7', 'process': {'pid': '1_7', 'input': 'fastq', 'output': 'fasta', 'lane': 1, 'directives': 'va_spadescpus: 4memory: { 5.GB * task.attempt }container: flowcraft/viral_assemblyversion: 0.1-1scratch: trueva_megahitcpus: 4memory: { 5.GB * task.attempt }container: flowcraft/viral_assemblyversion: 0.1-1scratch: true'}, 'children': [{'name': 'assembly_mapping_1_8', 'process': {'pid': '1_8', 'input': 'fasta', 'output': 'fasta', 'lane': 1, 'directives': 'assembly_mappingcpus: 4memory: { 5.GB * task.attempt }container: flowcraft/bowtie2_samtoolsversion: 1.0.0-1process_assembly_mappingcpus: 1memory: { 5.GB * task.attempt }container: flowcraft/bowtie2_samtoolsversion: 1.0.0-1'}, 'children': [{'name': 'pilon_1_9', 'process': {'pid': '1_9', 'input': 'fasta', 'output': 'fasta', 'lane': 1, 'directives': 'piloncpus: 4memory: { 7.GB * task.attempt }container: flowcraft/pilonversion: 1.22.0-1pilon_reportcpus: 1memory: { 7.GB * task.attempt }container: flowcraft/pilonversion: 1.22.0-1'}, 'children': [{'name': 'split_assembly_1_10', 'process': {'pid': '1_10', 'input': 'fasta', 'output': 'fasta', 'lane': 1, 'directives': 'split_assemblycpus: 1memory: { 1.GB * task.attempt }'}, 'children': [{'name': 'dengue_typing_1_11', 'process': {'pid': '1_11', 'input': 'fasta', 'output': 'fasta', 'lane': 1, 'directives': 'dengue_typing_assemblycpus: 4memory: 1GBcontainer: flowcraft/seq_typingversion: 2.0-1dengue_typing_readscpus: 4memory: { 5.GB * task.attempt }container: ummidock/seq_typingversion: 2.2-02'}, 'children': [{'name': 'mafft_1_12', 'process': {'pid': '1_12', 'input': 'fasta', 'output': 'align', 'lane': 1, 'directives': 'mafftcontainer: flowcraft/mafftversion: 7.402-1cpus: 4memory: { 4.GB * task.attempt }'}, 'children': [{'name': 'raxml_1_13', 'process': {'pid': '1_13', 'input': 'align', 'output': '.tree', 'lane': 1, 'directives': 'raxmlcontainer: flowcraft/raxmlversion: 8.2.11-2cpus: 4memory: { 4.GB * task.attempt }report_raxmlcontainer: flowcraft/raxmlversion: 8.2.11-2'}, 'children': []}]}]}]}]}]}]}]}]}]}]}]}]}]}
/**
* This function creates a tooltip with the node/process information
* on mouse over in the respective node
*
* @param {Object} d - stores information of the node data (containing
* name, input, output, etc) and parent info for this node
*/
const mouseover = (d) => {
div.transition()
.duration(200)
.style("opacity", .9)
div.html(`<b>pid:</b> ${d.data.process.pid},<br>
<b>lane:</b> ${d.data.process.lane},<br>
<b>input:</b> ${d.data.process.input},<br>
<b>output:</b> ${d.data.process.output},<br>
<b>directives:</b><br>
${d.data.process.directives}
`)
.style("left", (d3.event.pageX) + "px")
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px")
.style("text-align", "left")
}
/**
* Function that hides the tooltip
* @param {Object} d - stores information of the node data (containing
* name, input, output, etc) and parent info for this node
*/
const mouseout = (d) => {
div.transition()
.duration(500)
.style("opacity", 0)
}
/**
* Function that collapses nodes and all their childrens
* @param {Object} d - stores information of the node data (containing
* name, input, output, etc) and parent info for this node
*/
// const collapse = (d) => {
// if(d.children) {
// d._children = d.children
// d._children.forEach(collapse)
// d.children = null
// }
// }
// Set the dimensions and margins of the diagram
const margin = {top: 20, right: 20, bottom: 20, left: 20},
width = 1870,
height = 860
const div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0)
let i = 0,
duration = 750
let root
// Assigns parent, children, height, depth
root = d3.hierarchy(inputData, (d) => { return d.children })
root.x0 = height / 2
root.y0 = 0
// declares a tree layout and assigns the size
const treemap = d3.tree().size([height, width])
// Assigns the x and y position for the nodes
const treeData = treemap(root)
// append the svg object to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
const svg = d3.select("body")
.append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.call(d3.zoom().on("zoom", function () {
svg.attr("transform", d3.event.transform)
}))
.on("dblclick.zoom", null)
.append("g")
.attr("transform", "translate("
+ margin.left + "," + margin.top + ")"
)
/**
* Function that updates the graph on load and on node clicks
*
* @param {Object} source - Stores the full tree information, including
* the root node, which will be deleted by filter on nodes and links.
*/
const update = (source) => {
// Creates a curved (diagonal) path from parent to the child nodes
/**
* Creates a curved (diagonal) path from parent to the child nodes
*
* @param {Object} s
* @param {Object} d
* @returns {string}
*/
const diagonal = (s, d) => {
path = `M ${s.y} ${s.x}
C ${(s.y + d.y) / 2} ${s.x},
${(s.y + d.y) / 2} ${d.x},
${d.y} ${d.x}`
return path
}
/**
* Function that toggles childrens on click
*
* @param {Object} d - stores information of the node data (containing
* name, input, output, etc) and parent info for this node
*/
const click = (d) => {
if (d.children) {
d._children = d.children
d.children = null
} else {
d.children = d._children
d._children = null
}
update(d)
}
// Compute the new tree layout.
let nodes = treeData.descendants(),
links = treeData.descendants().slice(1)
// hide root node
nodes = nodes.filter( (d) => {
return d.depth
})
// hide links to root
links = links.filter( (d) => {
return d.depth !== 1
})
// ****************** Nodes section ***************************
// Update the nodes...
const node = svg.selectAll('g.node')
.data(nodes, (d) => { return d.id || (d.id = ++i) })
// Enter any new modes at the parent's previous position.
const nodeEnter = node.enter().append('g')
.attr('class', 'node')
.attr("transform", (d) => {
return "translate(" + source.y0 + "," + source.x0 + ")"
})
.on('click', click)
.on("mouseover", mouseover)
.on("mouseout", mouseout)
// Add Circle for the nodes
nodeEnter.append('circle')
.attr('class', 'node')
.attr('r', 1e-6)
// .style("fill", (d) => {
// return d._children ? "lightsteelblue" : "#fff"
// })
// Add labels for the nodes
nodeEnter.append('text')
.attr("y", "-20")
.attr("text-anchor", "middle")
.text( (d) => { return d.data.name } )
// gets labels variable
const labels = d3.selectAll("text")
// returns the label with max width value
const maxTextWidth = d3.max(labels.nodes(),
n => n.getComputedTextLength())
// Normalize for fixed-depth, according to max_width
nodes.forEach( (d) => { d.y = d.depth * maxTextWidth} )
// UPDATE
const nodeUpdate = nodeEnter.merge(node)
// Transition to the proper position for the node
nodeUpdate.transition()
.duration(duration)
.attr("transform", (d) => {
return "translate(" + d.y + "," + d.x + ")"
})
// Update the node attributes and style
nodeUpdate.select('circle.node')
.attr('r', 10)
.style("fill", (d) => {
return d._children ? "#ffad6b" : "lightsteelblue"
})
.attr('cursor', 'pointer')
// Remove any exiting nodes
const nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", (d) => {
return "translate(" + source.y + "," + source.x + ")"
})
.remove()
// On exit reduce the node circles size to 0
nodeExit.select('circle')
.attr('r', 1e-6)
// On exit reduce the opacity of text labels
nodeExit.select('text')
.style('fill-opacity', 1e-6)
// ****************** links section ***************************
// Update the links...
const link = svg.selectAll('path.link')
.data(links, (d) => { return d.id })
// Enter any new links at the parent's previous position.
const linkEnter = link.enter().insert('path', "g")
.attr("class", "link")
.attr('d', (d) => {
const o = {x: source.x0, y: source.y0}
return diagonal(o, o)
})
// merge links
const linkUpdate = linkEnter.merge(link)
// Transition back to the parent element position
linkUpdate.transition()
.duration(duration)
.attr('d', function(d){ return diagonal(d, d.parent) })
// Remove any existing links
const linkExit = link.exit().transition()
.duration(duration)
.attr('d', (d) => {
const o = {x: source.x, y: source.y}
return diagonal(o, o)
})
.remove()
// Store the old positions for transition.
nodes.forEach( (d) => {
d.x0 = d.x
d.y0 = d.y
})
}
// Collapse after the second level
// root.children.forEach(collapse);
update(root)
</script>
You can’t perform that action at this time.