Commits

Chris Mutel committed 59867b3

0.3; working Hinton matrix, interpolated histogram, better web styling, LCA setup page

  • Participants
  • Parent commits 9b00eef

Comments (0)

Files changed (17)

 include *.txt
 recursive-include bw2ui/*.py
 recursive-include bw2ui/web *.css
-recursive-include bw2ui/web *.png
+recursive-include bw2ui/web *.png
+recursive-include bw2ui/web *.jpg
+recursive-include bw2ui/web *.js

bw2ui/bin/bw2-web.py

     clean_jobs_directory()
 
     args = docopt(__doc__, version='Brightway2 Web UI 0.1')
-    port = int(args.get("--port", False) or 5000 + random.randint(0, 999))
+    port = int(args.get("--port", False) or 5000)  # + random.randint(0, 999))
     host = "0.0.0.0" if args.get("--insecure", False) else "localhost"
 
     if not args["--nobrowser"]:
         "use_debugger": args["--debug"]
     }
 
-    run_simple(host, port, bw2webapp, use_evalex=True, **kwargs)
+    # run_simple(host, port, bw2webapp, use_evalex=True, **kwargs)
+    bw2webapp.run(debug=True)

bw2ui/controller.py

 # -*- coding: utf-8 -*-
 from brightway2 import databases, methods, Database, Method, config, reset_meta
-from brightway2.io import EcospoldImporter, EcospoldImpactAssessmentImporter
+from bw2data.io import EcospoldImporter, EcospoldImpactAssessmentImporter
 from errors import UnknownAction
 
 
 # -*- coding: utf-8 -*-
 from brightway2 import config, databases, methods, Database, Method, \
     JsonWrapper
-from bw2analyzer import ContributionAnalysis
+from bw2analyzer import ContributionAnalysis, DatabaseExplorer
 from bw2calc import LCA
-from flask import Flask, url_for, render_template, request, redirect, abort, \
-    Response
+from flask import Flask, url_for, render_template, request, redirect, abort
+from fuzzywuzzy import process
 from jobs import JobDispatch, InvalidJob
-from utils import get_job_id, get_job, set_job_status
-import base64
+from utils import get_job_id, get_job, set_job_status, json_response
+import itertools
 
 app = Flask(__name__)
 
 @app.route("/status/<job>")
 def job_status(job):
     try:
-        return Response(JsonWrapper.dumps(get_job(job)),
-            mimetype='application/json')
+        return json_response(get_job(job))
     except:
         abort(404)
 
     return render_template("index.html", **context)
 
 
-@app.route('/calculate/lca')
-@app.route('/calculate/lca/<process>/<method>')
-def lca(process=None, method=None):
-    context = {}
-    if process:
-        method = eval(base64.urlsafe_b64decode(str(method)), None, None)
-        process = eval(base64.urlsafe_b64decode(str(process)), None, None)
-        lca = LCA(process, method)
+@app.route('/hinton')
+def hinton():
+    return render_template("hinton.html")
+
+
+@app.route('/lca', methods=["GET", "POST"])
+def lca():
+    if request.method == "GET":
+        return render_template("lca-select.html")
+    else:
+        fu = eval(request.form["activity"])
+        method = eval(request.form["method"])
+        context = {}
+        lca = LCA(fu, method)
         lca.lci()
         lca.lcia()
         rt, rb = lca.reverse_dict()
         context["ia_unit"] = methods[method]["unit"]
         context["ia_method"] = ": ".join(method)
         context["fu"] = [(ca.get_name(k), "%.2g" % v, ca.db_names[k[0]][k][
-            "unit"]) for k, v in process.iteritems()]
+            "unit"]) for k, v in fu.iteritems()]
         return render_template("lca.html", **context)
-    else:
-        return "No parameters"
 
 
 @app.route('/progress')
     return render_template("hist.html", **{"job": job_id, 'status': status_id})
 
 
+def filter_sort_process_database(data, filter=None, order=None):
+    if order:
+        data = list(itertools.chain(
+            *[[(k, v) for k, v in data.iteritems() if v["name"] == x
+            ] for x in order]))
+    else:
+        data = data.iteritems()
+    pass
+
+
+@app.route("/database/<name>")
+def database_explorer(name):
+    if name not in databases:
+        return abort(404)
+    db = Database(name)
+    data = db.load()
+    if request.args.get("q", None):
+        names = [x["name"] for x in data.values()]
+        names = process.extract(request.args.get("q"), names, limit=20)
+        data = filter_sort_process_database(data, filter=names, order=names)
+    else:
+        data = filter_sort_process_database(data)
+    return render_template("database.html",
+        name=name, data=JsonWrapper.dumps(data))
+
+
+@app.route("/method-json/<name1>/<name2>/<name3>")
+def method_json(name1, name2, name3):
+    name = (name1, name2, name3)
+    print name, name in methods
+    if name not in methods:
+        abort(404)
+    biosphere = Database("biosphere").load()
+    print "Biosphere loaded", len(biosphere)
+    cfs = [{"n": biosphere[x[0]]["name"],
+        "c": str(biosphere[x[0]]["categories"]),
+        "a": x[1]
+        } for x in Method(name).load().iteritems()]
+    cfs.sort()
+    return json_response(cfs)
+
+
+@app.route("/method/<name1>")
+@app.route("/method/<name1>/<name2>")
+@app.route("/method/<name1>/<name2>/<name3>")
+def method_explorer(name1, name2=None, name3=None):
+    return
+
+
+def short_name(name):
+    return " ".join(name.split(" ")[:3])[:25]
+
+
+@app.route("/database/tree/<name>/<code>")
+@app.route("/database/tree/<name>/<code>/<direction>")
+def database_tree(name, code, direction="backwards"):
+    def format_d(d):
+        return [{"name": short_name(data[k]["name"]),
+            "children": format_d(v) if isinstance(v, dict) \
+                else [{"name": short_name(data[x]["name"])} for x in v]
+            } for k, v in d.iteritems()]
+
+    if name not in databases:
+        abort(404)
+    explorer = DatabaseExplorer(name)
+    data = Database(name).load()
+    if (name, code) not in data:
+        try:
+            code = int(code)
+            assert (name, code) in data
+        except:
+            return abort(404)
+    if direction == "forwards":
+        nodes = explorer.uses_this_process((name, code), 1)
+    else:
+        nodes = explorer.provides_this_process((name, code), 1)
+    for db in databases[name]["depends"]:
+        data.update(Database(db).load())
+    formatted = {
+        "name": short_name(data[(name, code)]["name"]),
+        "children": format_d(nodes)
+    }
+    import pprint
+    pprint.pprint(formatted)
+    return render_template("database_tree.html",
+        f=formatted,
+        activity=data[(name, code)]["name"],
+        direction=direction.title())
+
+
 @app.route('/start', methods=["GET", "POST"])
 def start_bw():
     """Start Brightway"""

bw2ui/web/static/css/base.css

+body {
+    background-image: url('/static/img/linedpaper.png');
+    background-repeat: repeat;
+}
+
+.container {
+    background: white;
+    padding-left: 10px;
+    padding-right: 10px;
+    border-left: 2px solid dimgray;
+    border-right: 2px solid dimgray;
+    padding-bottom: 10px;
+    border-bottom: 1px solid dimgray;
+}
+
+h1, h2, h3, h4 {
+    font-family: 'Source Sans Pro', sans-serif;
+}
+
+brightway {
+    color: dimgray;
+    font-weight: normal;
+}
+
+two {
+    color: cornflowerblue;
+    font-weight: normal;
+}
+
+.hinton-label {
+    overflow: hidden;
+    background: white;
+}
+
+.ihline {
+    stroke: steelblue;
+    stroke-width: 2;
+    fill: none;
+}
+
+.indicator {
+    stroke: red;
+    stroke-width: 1;
+    shape-rendering: crispEdges;
+    fill: none;
+}
+
+.grid-line {
+    stroke: darkgray;
+    stroke-width: 1;
+    shape-rendering: crispEdges;
+    fill: none;
+}
+
+.axis path, .axis line {
+    fill: none;
+    stroke: black;
+    stroke-width: 1;
+    shape-rendering: crispEdges;
+}

bw2ui/web/static/img/linedpaper.png

Added
New image

bw2ui/web/static/js/hinton.js

+var hinton_matrix = function (data, total, xlabels, ylabels, id, w, h, padding) {
+  // Transform data for areal display
+  t_data = [];
+  for (var i = data.length - 1; i >= 0; i--) {
+    t_data.push([data[i][0], data[i][1], Math.sqrt(data[i][2])]);
+  }
+  data = t_data;
+
+  var rows = xlabels.length + 1,
+    cols = ylabels.length + 1;
+    delta_x = (w - 2 * padding) / cols,
+    delta_y = (h - 2 * padding) / rows,
+    total = Math.sqrt(total);
+    // max_size = d3.max(data, function (d) { return d[2]; });
+
+  if (xlabels.length < d3.max(data, function (d) { return d[0]; }) + 1) {
+    throw "Not enough xlabels";
+  }
+
+  if (ylabels.length < d3.max(data, function (d) { return d[1]; }) + 1) {
+    throw "Not enough ylabels";
+  }
+
+  var x_lines = [];
+  for (var i = 1; i <= rows; i++) {
+    x_lines.push([[1, i], [cols, i]]);
+  }
+
+  var y_lines = [];
+  for (var i = 1; i <= cols; i++) {
+    y_lines.push([[i, 1], [i, rows]]);
+  }
+
+  var color_scale = d3.scale.linear()
+    .range(["cornflowerblue", "crimson"])
+    .domain([0, total]);
+
+  var x_size_scale = d3.scale.linear()
+    .range([1, delta_x - 1])
+    .domain([0, total]);
+
+  var y_size_scale = d3.scale.linear()
+    .range([1, delta_y - 1])
+    .domain([0, total]);
+
+  var y = d3.scale.linear()
+    .range([padding, h - padding])
+    .domain([0, cols]);
+
+  var x = d3.scale.linear()
+    .range([padding, w - padding])
+    .domain([0, rows]);
+
+  var grid_line = d3.svg.line()
+    .x(function(d) { return x(d[0]); })
+    .y(function(d) { return y(d[1]); });
+
+  var svg = d3.select(id)
+    .append("svg")
+    .attr("width", w)
+    .attr("height", h);
+
+  var rect = svg.selectAll("rect")
+    .data(data)
+    .enter().append("rect")
+      .attr("x", function (d) { return x(d[0] + 1.5) - (x_size_scale(d[2]) * 0.5); })
+      .attr("y", function (d) { return y(d[1] + 1.5) - (y_size_scale(d[2]) * 0.5); })
+        // delta_x / 2 + x(d[1]) - x_size_scale(Math.sqrt(d[2]) / 2); })
+      // .attr("y", function (d) { return delta_y / 2 + y(d[0]) - y_size_scale(Math.sqrt(d[2])) / 2; })
+      .attr("width", function (d) { return x_size_scale(d[2]); })
+      .attr("height", function (d) { return y_size_scale(d[2]); })
+      .attr("fill", function (d) { return color_scale(d[2]); });
+
+  for (var i = ylabels.length - 1; i >= 0; i--) {
+    svg.append("svg:foreignObject")
+      .attr('x', x(i + 1) + 1)
+      .attr('y', 1)
+      .attr("width", delta_x - 2)
+      .attr("height", delta_y - 2)
+      .append("xhtml:body")
+      .html("<div class=\"hinton-label\" title=\"" + ylabels[i] + "\">" + ylabels[i] + "</div>");
+  }
+
+  for (var i = xlabels.length - 1; i >= 0; i--) {
+    svg.append("svg:foreignObject")
+      .attr('x', 1)
+      .attr('y', y(i + 1) + 1)
+      .attr("width", delta_x - 2)
+      .attr("height", delta_y - 2)
+      .append("xhtml:body")
+      .html("<div class=\"hinton-label\" title=\"" + xlabels[i] + "\">" + xlabels[i] + "</div>");
+  }
+
+  svg.selectAll(".x-lines")
+    .data(x_lines)
+    .enter().append("path")
+    .attr("class", "grid-line")
+    .attr("d", grid_line);
+
+  svg.selectAll(".y-lines")
+    .data(y_lines)
+    .enter().append("path")
+    .attr("class", "grid-line")
+    .attr("d", grid_line);
+};

bw2ui/web/static/js/interp-histogram.js

+var interpolated_histogram = function (data, xlabel, median, upper, lower, id, w, h, padding) {
+  var y_max = d3.max(data, function (d) { return d[0]; }),
+    x_max = d3.max(data, function (d) { return d[1]; }),
+    x_min = d3.min(data, function (d) { return d[1]; });
+
+  var y = d3.scale.linear()
+    .range([h - padding, padding])
+    .domain([0, y_max * 1.1])
+    .nice();
+
+  var y_axis = d3.svg.axis()
+    .scale(y)
+    .ticks(4)
+    .orient("left");
+
+  var x = d3.scale.linear()
+    .range([padding, w - padding])
+    .domain([x_min, x_max])
+    .nice();
+
+  var x_axis = d3.svg.axis()
+    .scale(x)
+    .ticks(6)
+    .orient("bottom");
+
+  var svg = d3.select(id)
+    .append("svg")
+    .attr("width",w)
+    .attr("height",h);
+
+  var line = d3.svg.line()
+    .x(function(d) { return x(d[1]); })
+    .y(function(d) { return y(d[0]); })
+    .interpolate("linear");
+
+  var g = svg.append("svg:g")
+    .append("svg:path")
+    .attr("class", "ihline")
+    .attr("d", line(data));
+
+  var indicator_line = d3.svg.line()
+    .x(function(d) { return x(d[0]); })
+    .y(function(d) { return y(d[1]); });
+  
+  svg.append("svg:g")
+    .append("svg:path")
+    .attr("class", "indicator")
+    .attr("d", indicator_line([[median, 0], [median, y_max * 1.05]]));
+
+  svg.append("text")
+    .attr("class", "label")
+    .attr("text-anchor", "middle")
+    .attr("x", x(median))
+    .attr("y", y(y_max * 1.05) - 5)
+    .text("Median");
+
+  svg.append("svg:g")
+      .append("svg:path")
+      .attr("class", "indicator")
+      .attr("d", indicator_line([[lower, 0], [lower, y_max * 0.2]]));
+
+  svg.append("text")
+    .attr("class", "label")
+    .attr("text-anchor", "middle")
+    .attr("x", x(lower))
+    .attr("y", y(y_max * 0.2) - 5 )
+    .text("95% lower");
+
+  svg.append("svg:g")
+      .append("svg:path")
+      .attr("class", "indicator")
+      .attr("d", indicator_line([[upper, 0], [upper, y_max * 0.4]]));
+
+  svg.append("text")
+    .attr("class", "x label")
+    .attr("text-anchor", "middle")
+    .attr("x", x(upper))
+    .attr("y", y(y_max * 0.4) - 5 )
+    .text("95% upper");
+
+  svg.append("svg:g")
+    .attr("class", "x axis")
+    .attr("transform", "translate(0," + (h - padding + 2) + ")")
+    .call(x_axis);
+
+  svg.append("svg:g")
+   .attr("class", "y axis")
+   .call(y_axis);
+
+  svg.append("text")
+    .attr("class", "y label")
+    .attr("text-anchor", "end")
+    .attr("transform", "rotate(-90)")
+    .attr("y", padding)
+    .attr("x", padding)
+    .attr("dy", ".5em")
+    .text("Count");
+
+  svg.append("text")
+    .attr("class", "x label")
+    .attr("text-anchor", "end")
+    .attr("x", w)
+    .attr("y", h - padding - 5)
+    .text(xlabel);
+}

bw2ui/web/templates/base.html

 	<link rel="stylesheet" href="{{ url_for('static', filename="blueprint/plugins/buttons/screen.css") }}" type="text/css" media="screen, projection">
 	<link rel="stylesheet" href="{{ url_for('static', filename="blueprint/print.css") }}" type="text/css" media="print">
 	<!--[if lt IE 8]><link rel="stylesheet" href="{{ url_for('static', filename="blueprint/ie.css") }}" type="text/css" media="screen, projection"><![endif]-->
+	<link rel="stylesheet" href="{{ url_for('static', filename="css/base.css") }}" type="text/css" media="screen, projection">
+	<link href='http://fonts.googleapis.com/css?family=Source+Sans+Pro' rel='stylesheet' type='text/css'>
 	<script src="http://yui.yahooapis.com/3.7.3/build/yui/yui-min.js"></script>
 	<script src="http://d3js.org/d3.v2.min.js"></script>
 	{% block extrahead %}{% endblock %}
 </head>
 <body class="yui3-skin-sam">
-	<div class="container">{% block body %}{% endblock %}</div>
+	<div class="container">{% block header %}<h1><span id="brightway">Brightway</span><span id="two">2</span> web</h1><hr>{% endblock %}
+		{% block body %}{% endblock %}</div>
 </body>
 </html>

bw2ui/web/templates/database_tree.html

+{% extends "base.html" %}
+
+{% block extrahead %}
+<style type="text/css">
+.node circle {
+  cursor: pointer;
+  fill: #fff;
+  stroke: steelblue;
+  stroke-width: 1.5px;
+}
+
+.node text {
+  font-size: 11px;
+}
+
+path.link {
+  fill: none;
+  stroke: #ccc;
+  stroke-width: 1.5px;
+}</style>
+{% endblock %}
+
+
+{% block body %}
+<h2>{{ direction }} supply chain for {{ activity }}</h2>
+<hr>
+<div id="cluster"></div>
+
+<script type="text/javascript">
+var root = {{f|tojson|safe}},
+  root,
+  w = 900,
+  h = 700;
+
+var cluster = d3.layout.cluster().size([h, w-160]);
+
+var diagonal = d3.svg.diagonal()
+    .projection(function(d) { return [d.y, d.x]; });
+
+var svg = d3.select("#cluster").append("svg")
+    .attr("width", w)
+    .attr("height", h)
+  .append("g")
+    .attr("transform", "translate(40,0)");
+
+var nodes = cluster.nodes(root),
+    links = cluster.links(nodes);
+
+var link = svg.selectAll(".link")
+    .data(links)
+  .enter().append("path")
+    .attr("class", "link")
+    .attr("d", diagonal);
+
+var node = svg.selectAll(".node")
+    .data(nodes)
+  .enter().append("g")
+    .attr("class", "node")
+    .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
+
+node.append("circle")
+    .attr("r", 4.5);
+
+node.append("text")
+    .attr("dx", function(d) { return d.children ? -8 : 8; })
+    .attr("dy", 3)
+    .style("text-anchor", function(d) { return d.children ? "end" : "start"; })
+    .text(function(d) { return d.name; });
+</script>
+{% endblock %}

bw2ui/web/templates/hinton.html

+{% extends "base.html" %}
+
+{% block extrahead %}
+<script src="{{ url_for('static', filename="js/hinton.js") }}"></script>
+<script src="{{ url_for('static', filename="js/interp-histogram.js") }}"></script>
+<style type="text/css">
+svg { 
+  padding-top: 10px;
+  padding-bottom: 10px;
+  padding-left: 80px;
+}
+</style>
+{% endblock %}
+
+
+{% block body %}
+<h1>Testing Hinton matrix</h1>
+<div id="hinton"></div>
+<hr>
+<div id="interp"></div>
+<script type="text/javascript">
+var data = [[0, 0, 2.13e-01],
+  [0, 4, 3.88e-04],
+  [1, 4, 3.46e-02],
+  [2, 2, 4.64e-02],
+  [2, 3, 2.91e-2],
+  [3, 1, 1.46e-1],
+  [3, 2, 1.41e-4],
+  [3, 4, 7.5e-4],
+  [4, 3, 5e-3],
+  [4, 4, 1.4e-4]],
+  ylabels = ["wheat grains conventional, Castilla-y-Leon, at farm",
+  "nitric acid, 50% in H2O, at plant",
+  "ammonia, steam reforming, liquid, at plant",
+  "natural gas, burned in cogen 1MWe lean burn",
+  "combine harvesting"],
+  w = 400,
+  h = 400,
+  padding = 10;
+
+hinton_matrix(data, 0.773, ["foo", "foo", "foo", "foo", "foo"], ylabels, "#hinton", w, h, padding);
+</script>
+<script type="text/javascript">
+var data = [[1, 0.60372056294234133], [0, 0.60948078161411423], [0, 0.61524100028588713], [2, 0.62100121895765992], [0, 0.62676143762943282], [0, 0.63252165630120571], [0, 0.63828187497297861], [1, 0.6440420936447514], [2, 0.6498023123165243], [9, 0.6555625309882972], [8, 0.6613227496600701], [11, 0.66708296833184288], [20, 0.67284318700361578], [17, 0.67860340567538868], [31, 0.68436362434716158], [36, 0.69012384301893448], [40, 0.69588406169070727], [53, 0.70164428036248017], [77, 0.70740449903425306], [100, 0.71316471770602596], [121, 0.71892493637779875], [147, 0.72468515504957165], [167, 0.73044537372134455], [182, 0.73620559239311745], [205, 0.74196581106489035], [188, 0.74772602973666313], [246, 0.75348624840843603], [266, 0.75924646708020893], [323, 0.76500668575198183], [305, 0.77076690442375462], [343, 0.77652712309552752], [333, 0.78228734176730041], [375, 0.78804756043907331], [355, 0.79380777911084621], [354, 0.799567997782619], [353, 0.8053282164543919], [348, 0.8110884351261648], [354, 0.81684865379793758], [337, 0.82260887246971048], [286, 0.82836909114148338], [317, 0.83412930981325628], [323, 0.83988952848502918], [270, 0.84564974715680208], [300, 0.85140996582857487], [301, 0.85717018450034776], [272, 0.86293040317212066], [229, 0.86869062184389345], [196, 0.87445084051566635], [175, 0.88021105918743925], [174, 0.88597127785921215], [166, 0.89173149653098505], [150, 0.89749171520275794], [132, 0.90325193387453073], [124, 0.90901215254630363], [114, 0.91477237121807653], [97, 0.92053258988984932], [101, 0.92629280856162222], [79, 0.93205302723339511], [55, 0.93781324590516801], [69, 0.94357346457694091], [45, 0.94933368324871381], [37, 0.9550939019204866], [37, 0.9608541205922595], [31, 0.96661433926403229], [29, 0.97237455793580518], [28, 0.97813477660757808], [22, 0.98389499527935098], [20, 0.98965521395112388], [11, 0.99541543262289678], [14, 1.0011756512946697], [10, 1.0069358699664424], [9, 1.0126960886382155], [16, 1.0184563073099882], [6, 1.0242165259817611], [5, 1.0299767446535339], [10, 1.0357369633253068], [3, 1.0414971819970797], [5, 1.0472574006688526], [2, 1.0530176193406255], [6, 1.0587778380123982], [5, 1.0645380566841713], [2, 1.070298275355944], [1, 1.0760584940277169], [1, 1.0818187126994898], [2, 1.0875789313712627], [0, 1.0933391500430356], [1, 1.0990993687148085], [0, 1.1048595873865814], [0, 1.1106198060583541], [0, 1.1163800247301272], [0, 1.1221402434018999], [0, 1.1279004620736728], [0, 1.1336606807454457], [0, 1.1394208994172186], [0, 1.1451811180889915], [0, 1.1509413367607642], [1, 1.1567015554325373], [0, 1.16246177410431], [0, 1.1682219927760831], [1, 1.1739822114478558]],
+  id = "#interp",
+  w = 500,
+  h = 300,
+  xlabel = "kg CO2-eq.",
+  padding = 10;
+
+interpolated_histogram(data, xlabel, 0.816, 0.677, 1.03, id, w, h, padding);
+</script>
+{% endblock %}

bw2ui/web/templates/index.html

 {% extends "base.html" %}
 
 {% block body %}
-<h1>Brightway2 web</h1>
-<hr>
 <h3>Data directory is <i>{{ config.dir }}</i></h3>
 <div class="span-10">
     <h2>Databases</h2>
 <a href="/progress">
     <button>Dynamic progress update</button>
 </a>
+<a href="/hinton">
+    <button>Hinton demo</button>
+</a>
 
 <script type="text/javascript">
 YUI().use('datatable-sort', 'datatable-scroll', function (Y) {

bw2ui/web/templates/lca-select.html

+{% extends "base.html" %}
+
+{% block body %}
+<form action="/lca" method="POST">
+  <fieldset>
+    <legend>LCA report setup</legend>
+        <p>Activity: <input type="text" name="activity"></p>
+        <p>Method: <input type="text" name="method"></p>
+        <p><input type="submit" name="submit"></p>
+  </fieldset>
+</form>
+{% endblock %}

bw2ui/web/templates/lca.html

 {% endblock %}
 
 {% block body %}
-<h1>Brightway2 life cycle assessment report</h1>
-<hr>
 <div class="span-15 colborder">
 	<h2 style="margin-bottom: 0">Functional unit:</h2>
 	<ul style="margin-bottom: 0">
 <hr>
 <div id="chart"></div>
 <script type="text/javascript">
-	var width = 960,
-	    height = 400,
+	var width = 950,
+	    height = 200,
 	    color = d3.scale.category20();
 
 	var treemap = d3.layout.treemap()
 	.enter().append("div")
 	  .attr("class", "cell")
 	  .attr("title", function(d) { return d.children ? null : d.name; })
+	  .style("font-size", "14px")
 	  .style("background", function(d) { return d.children ? color(d.name) : null; })
 	  .call(cell)
 	  .text(function(d) { return d.children ? null : d.name; });

bw2ui/web/templates/method.html

Empty file added.

bw2ui/web/utils.py

 from brightway2 import config, JsonWrapper
+from flask import Response
 import os
 import uuid
 
 
 def get_job_id():
     return uuid.uuid4().hex
+
+
+def json_response(data):
+    return Response(JsonWrapper.dumps(data), mimetype='application/json')
 
 setup(
   name='bw2ui',
-  version="0.2",
+  version="0.3",
   packages=["bw2ui", "bw2ui.web"],
   package_data={'bw2ui.web': [
     "static/blueprint/*.css",
     "static/blueprint/plugins/buttons/*.css",
     "static/blueprint/plugins/fancy-type/*.css",
+    "static/img/*.png",
+    "static/img/*.jpg",
+    "static/js/*.js",
+    "static/css/*.css",
     "templates/*.html",
     ]},
   author="Chris Mutel",
   author_email="cmutel@gmail.com",
   license=open('LICENSE.txt').read(),
-  requires=["brightway2", "docopt", "flask"],
+  install_requires=["brightway2", "docopt", "flask", "requests", "bw-stats-toolkit", \
+    "fuzzywuzzy"],
   scripts=["bw2ui/bin/bw2-web.py", "bw2ui/bin/bw2-controller.py"],
   url="https://bitbucket.org/cmutel/brightway2-ui",
   long_description=open('README.txt').read(),