about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--wqflask/wqflask/markdown_routes.py14
-rw-r--r--wqflask/wqflask/static/new/css/markdown.css14
-rw-r--r--wqflask/wqflask/templates/environment.html134
3 files changed, 162 insertions, 0 deletions
diff --git a/wqflask/wqflask/markdown_routes.py b/wqflask/wqflask/markdown_routes.py
index 59a465e7..183f4caa 100644
--- a/wqflask/wqflask/markdown_routes.py
+++ b/wqflask/wqflask/markdown_routes.py
@@ -7,6 +7,8 @@ import markdown
 import os
 import sys
 
+from bs4 import BeautifulSoup
+
 from flask import Blueprint
 from flask import render_template
 
@@ -68,10 +70,21 @@ def references():
 
 @environments_blueprint.route("/")
 def environments():
+
     md_file = get_file_from_python_search_path("wqflask/DEPENDENCIES.md")
+    svg_file = get_file_from_python_search_path(
+        "wqflask/dependency-graph.html")
+    svg_data = None
+    if svg_file:
+        with open(svg_file, 'r') as f:
+            svg_data = "".join(
+                BeautifulSoup(f.read(),
+                              'lxml').body.script.contents)
+
     if md_file is not None:
         return (
             render_template("environment.html",
+                            svg_data=svg_data,
                             rendered_markdown=render_markdown(
                                 md_file,
                                 is_remote_file=False)),
@@ -80,6 +93,7 @@ def environments():
     # Fallback: Fetch file from server
     return (render_template(
         "environment.html",
+        svg_data=None,
         rendered_markdown=render_markdown(
             "general/environments/environments.md")),
             200)
diff --git a/wqflask/wqflask/static/new/css/markdown.css b/wqflask/wqflask/static/new/css/markdown.css
index 0c0309fb..38d664e2 100644
--- a/wqflask/wqflask/static/new/css/markdown.css
+++ b/wqflask/wqflask/static/new/css/markdown.css
@@ -57,6 +57,20 @@
     word-spacing: 0.2em;
 }
 
+.graph-legend h1 {
+    text-align: center;
+}
+
+.graph-legend,
+#guix-graph {
+    width: 90%;
+    margin: 10px auto;
+}
+
+#guix-graph {
+    border: solid 2px black;
+}
+
 #markdown table {
     width: 100%;
 }
diff --git a/wqflask/wqflask/templates/environment.html b/wqflask/wqflask/templates/environment.html
index cd30e768..5fe01dad 100644
--- a/wqflask/wqflask/templates/environment.html
+++ b/wqflask/wqflask/templates/environment.html
@@ -13,4 +13,138 @@
    <div  class="cls-table-style">{{ rendered_markdown|safe }} </div>
 </div>
 
+{% if svg_data %}
+
+<div class="graph-legend">
+    <h1>Chord dependency Graph of Genenetwork2</h1>
+    Graph generated from <a href="http://git.genenetwork.org/guix-bioinformatics/guix-bioinformatics/src/branch/master/gn/packages/genenetwork.scm">genenetwork.scm</a>. You can zoom in and out within the bounding box.
+</div>
+
+<div id="guix-graph"></div>
+{% endif %}
+
+{% endblock %}
+
+{% block js %}
+
+{% if svg_data %}
+<script language="javascript" type="text/javascript" src="{{ url_for('js', filename='d3js/d3.min.js') }}"></script>
+<script type="text/javascript">
+ {{ svg_data|safe }}
+ // based on http://bl.ocks.org/mbostock/1046712 under GPLv3
+ // Adapted from: https://elephly.net/graph.html
+ var outerRadius = (nodeArray.length * 10) / 2,
+     innerRadius = outerRadius - 100,
+     width = outerRadius * 2,
+     height = outerRadius * 2,
+     colors = d3.scale.category20c(),
+     matrix = [];
+
+ function neighborsOf (node) {
+     return links.filter(function (e) {
+         return e.source === node;
+     }).map(function (e) {
+         return e.target;
+     });
+ }
+
+ function zoomed () {
+     zoomer.attr("transform",
+                 "translate(" + d3.event.translate + ")" +
+                 "scale(" + d3.event.scale + ")");
+ }
+
+ function fade (opacity, root) {
+     return function (g, i) {
+         root.selectAll("g path.chord")
+             .filter(function (d) {
+                 return d.source.index != i && d.target.index != i;
+             })
+             .transition()
+             .style("opacity", opacity);
+     };
+ }
+
+ // Now that we have all nodes in an object we can replace each reference
+ // with the actual node object.
+ links.forEach(function (link) {
+     link.target = nodes[link.target];
+     link.source = nodes[link.source];
+ });
+
+ // Construct a square matrix for package dependencies
+ nodeArray.forEach(function (d, index, arr) {
+     var source = index,
+         row = matrix[source];
+     if (!row) {
+         row = matrix[source] = [];
+         for (var i = -1; ++i < arr.length;) row[i] = 0;
+     }
+     neighborsOf(d).forEach(function (d) { row[d.index]++; });
+ });
+
+ // chord layout
+ var chord = d3.layout.chord()
+               .padding(0.01)
+               .sortSubgroups(d3.descending)
+               .sortChords(d3.descending)
+               .matrix(matrix);
+
+ var arc = d3.svg.arc()
+             .innerRadius(innerRadius)
+             .outerRadius(innerRadius + 20);
+
+ var zoom = d3.behavior.zoom()
+              .scaleExtent([0.1, 10])
+              .on("zoom", zoomed);
+
+ var svg = d3.select("#guix-graph").append("svg")
+             .attr("width", "100%")
+             .attr("height", "100%")
+             .attr('viewBox','0 0 '+Math.min(width,height)+' '+Math.min(width,height))
+             .attr('preserveAspectRatio','xMinYMin')
+             .call(zoom);
+
+ var zoomer = svg.append("g");
+
+ var container = zoomer.append("g")
+                       .attr("transform", "translate(" + outerRadius + "," + outerRadius + ")");
+
+ // Group for arcs and labels
+ var g = container.selectAll(".group")
+                  .data(chord.groups)
+                  .enter().append("g")
+                  .attr("class", "group")
+                  .on("mouseout", fade(1, container))
+                  .on("mouseover", fade(0.1, container));
+
+ // Draw one segment per package
+ g.append("path")
+  .style("fill",   function (d) { return colors(d.index); })
+  .style("stroke", function (d) { return colors(d.index); })
+  .attr("d", arc);
+
+ // Add circular labels
+ g.append("text")
+  .each(function (d) { d.angle = (d.startAngle + d.endAngle) / 2; })
+  .attr("dy", ".35em")
+  .attr("transform", function (d) {
+      return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
+           + "translate(" + (innerRadius + 26) + ")"
+           + (d.angle > Math.PI ? "rotate(180)" : "");
+  })
+  .style("text-anchor", function (d) { return d.angle > Math.PI ? "end" : null; })
+  .text(function (d) { return nodeArray[d.index].label; });
+
+ // Draw chords from source to target; color by source.
+ container.selectAll(".chord")
+          .data(chord.chords)
+          .enter().append("path")
+          .attr("class", "chord")
+          .style("stroke", function (d) { return d3.rgb(colors(d.source.index)).darker(); })
+          .style("fill", function (d) { return colors(d.source.index); })
+          .attr("d", d3.svg.chord().radius(innerRadius));
+</script>
+{% endif %}
+
 {% endblock %}