aboutsummaryrefslogtreecommitdiff
path: root/wqflask
diff options
context:
space:
mode:
Diffstat (limited to 'wqflask')
-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 %}