about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBonfaceKilz2020-12-04 11:43:11 +0300
committerGitHub2020-12-04 11:43:11 +0300
commit8084309bdfa46e29d3cc941911fa25b23f8400d5 (patch)
tree492b5ab1a6157adeb20e69a77dceeff8cd0b268f
parent1587a2f047ea33b44b0e16d02d772176d19e0f17 (diff)
parent64bfeadac33e6d22297714544cd96ef16677fe16 (diff)
downloadgenenetwork2-8084309bdfa46e29d3cc941911fa25b23f8400d5.tar.gz
Merge pull request #497 from BonfaceKilz/feature/display-deps-from-guix
Feature/display deps from guix
-rw-r--r--wqflask/wqflask/markdown_routes.py55
-rw-r--r--wqflask/wqflask/static/new/css/markdown.css41
-rw-r--r--wqflask/wqflask/templates/environment.html152
3 files changed, 221 insertions, 27 deletions
diff --git a/wqflask/wqflask/markdown_routes.py b/wqflask/wqflask/markdown_routes.py
index 601845d7..183f4caa 100644
--- a/wqflask/wqflask/markdown_routes.py
+++ b/wqflask/wqflask/markdown_routes.py
@@ -4,6 +4,10 @@ Render pages from github, or if they are unavailable, look for it else where
 """
 import requests
 import markdown
+import os
+import sys
+
+from bs4 import BeautifulSoup
 
 from flask import Blueprint
 from flask import render_template
@@ -16,18 +20,25 @@ policies_blueprint = Blueprint("policies_blueprint", __name__)
 facilities_blueprint = Blueprint("facilities_blueprint", __name__)
 
 
-def render_markdown(file_name):
+def render_markdown(file_name, is_remote_file=True):
     """Try to fetch the file name from Github and if that fails, try to
 look for it inside the file system """
     github_url = ("https://raw.githubusercontent.com/"
                   "genenetwork/gn-docs/master/")
 
+    if not is_remote_file:
+        text = ""
+        with open(file_name, "r", encoding="utf-8") as input_file:
+            text = input_file.read()
+        return markdown.markdown(text,
+                                 extensions=['tables'])
+
     md_content = requests.get(f"{github_url}{file_name}")
+
     if md_content.status_code == 200:
+        return markdown.markdown(md_content.content.decode("utf-8"),
+                                 extensions=['tables'])
 
-        return markdown.markdown(md_content.content.decode("utf-8"), extensions=['tables'])
-    # TODO: Add fallback on our git server by checking the mirror.
-    # Content not available
     return (f"\nContent for {file_name} not available. "
             "Please check "
             "(here to see where content exists)"
@@ -35,6 +46,14 @@ look for it inside the file system """
             "Please reach out to the gn2 team to have a look at this")
 
 
+def get_file_from_python_search_path(pathname_suffix):
+    cands = [os.path.join(d, pathname_suffix) for d in sys.path]
+    try:
+        return list(filter(os.path.exists, cands))[0]
+    except IndexError:
+        return None
+
+
 @glossary_blueprint.route('/')
 def glossary():
     return render_template(
@@ -51,7 +70,33 @@ def references():
 
 @environments_blueprint.route("/")
 def environments():
-    return render_template("environment.html", rendered_markdown=render_markdown("general/environments/environments.md")), 200
+
+    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)),
+            200
+        )
+    # Fallback: Fetch file from server
+    return (render_template(
+        "environment.html",
+        svg_data=None,
+        rendered_markdown=render_markdown(
+            "general/environments/environments.md")),
+            200)
 
 
 @links_blueprint.route("/")
diff --git a/wqflask/wqflask/static/new/css/markdown.css b/wqflask/wqflask/static/new/css/markdown.css
index dca3e31d..38d664e2 100644
--- a/wqflask/wqflask/static/new/css/markdown.css
+++ b/wqflask/wqflask/static/new/css/markdown.css
@@ -57,8 +57,47 @@
     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%;
+}
+
+#markdown td {
+    padding: 1em;
+    text-align: left;
+}
+
+#markdown th {
+    text-align: center;
+}
+
+#markdown table,
+#markdown td,
+#markdown th {
+    border: solid 2px black;
+}
+
+#markdown td,
+#markdown th {
+    padding-top: 8px;
+    padding-bottom: 8px;
+}
+
 @media(max-width:650px) {
     .container {
         width: 100vw;
     }
-}
\ No newline at end of file
+}
diff --git a/wqflask/wqflask/templates/environment.html b/wqflask/wqflask/templates/environment.html
index 94b31464..5fe01dad 100644
--- a/wqflask/wqflask/templates/environment.html
+++ b/wqflask/wqflask/templates/environment.html
@@ -8,33 +8,143 @@
 
 {% block content %}
 
- <div class="github-btn-container">
-    <div class="github-btn ">
-        <a href="https://github.com/genenetwork/gn-docs">
-            Edit Text
-            <img src="/static/images/edit.png">
-        </a>
-    </div>
-</div>
 <div id="markdown" class="container">
 	
    <div  class="cls-table-style">{{ rendered_markdown|safe }} </div>
 </div>
 
-<style type="text/css">
-table {
-	width: 100%;
-}
-table, th, td {
-  border: 1px solid black;
+{% 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; });
 
-td,th{
-	padding-top:8px;
-	padding-bottom: 8px;
-	text-align: center;
-}
+ // 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 %}
 
-</style>
 {% endblock %}