]> nos-oignons.net Git - website.git/commitdiff
Proprification et mise en place des graphs
authorLunar <lunar@anargeek.net>
Sat, 23 Nov 2013 16:41:51 +0000 (17:41 +0100)
committerLunar <lunar@anargeek.net>
Sat, 23 Nov 2013 18:43:03 +0000 (19:43 +0100)
README
Services.en.po
Services.mdwn
Services/Participation.html [deleted file]
assets/bw_graphs.css [new file with mode: 0644]
assets/bw_graphs.js [new file with mode: 0644]
assets/l10n.en.js [new file with mode: 0644]
assets/l10n.fr.js [new file with mode: 0644]
assets/pie_graphs.js [new file with mode: 0644]

diff --git a/README b/README
index 44cdc402f5386e74b32ec62073c06c7d96c0bb71..089fb0391ff6628d84dfba5126ad5a27066ad4e7 100644 (file)
--- a/README
+++ b/README
@@ -49,3 +49,10 @@ Images
 
 Les sources des images utilisés se trouve dans le répertoire `Site_web` du
 dépôt « comm ».
+
+Modification de la liste des relais
+-----------------------------------
+
+La liste des relais gérés par Nos oignons se trouve à deux endroits :
+`Services.mdwn` et `assets/relays.js`. Il faut bien penser à tout mettre à
+jour.
index ba68054eeed61a4e5a8c2f0f29a5b651af067194..3d0d6552f07929b2234629a4b24ba98ef4989782 100644 (file)
@@ -1,8 +1,8 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: Nos oignons website\n"
-"POT-Creation-Date: 2013-11-17 15:40+0100\n"
-"PO-Revision-Date: 2013-11-17 16:00+0100\n"
+"POT-Creation-Date: 2013-11-23 19:39+0100\n"
+"PO-Revision-Date: 2013-11-23 19:50+0100\n"
 "Last-Translator: Nos oignons <webmaster@nos-oignons.net>\n"
 "Language-Team: English <webmaster@nos-oignons.net>\n"
 "Language: en\n"
@@ -10,11 +10,137 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
+#. type: Plain text
+#, no-wrap
+msgid "[[!meta script=\"assets/l10n.fr\"]]\n"
+msgstr "[[!meta script=\"assets/l10n.en\"]]\n"
+
+#. type: Plain text
+#, no-wrap
+msgid "[[!meta script=\"assets/relays\"]]\n"
+msgstr "[[!meta script=\"assets/relays\"]]\n"
+
+#. type: Plain text
+#, no-wrap
+msgid "[[!meta script=\"assets/d3/d3.v3.min\"]]\n"
+msgstr "[[!meta script=\"assets/d3/d3.v3.min\"]]\n"
+
+#. type: Plain text
+#, no-wrap
+msgid "[[!meta script=\"assets/bw_graphs\"]]\n"
+msgstr "[[!meta script=\"assets/bw_graphs\"]]\n"
+
+#. type: Plain text
+#, no-wrap
+msgid "[[!meta stylesheet=\"assets/bw_graphs\" rel=\"stylesheet\"]]\n"
+msgstr "[[!meta stylesheet=\"assets/bw_graphs\" rel=\"stylesheet\"]]\n"
+
+#. type: Plain text
+#, no-wrap
+msgid "[[!meta script=\"assets/pie_graphs\"]]\n"
+msgstr "[[!meta script=\"assets/pie_graphs\"]]\n"
+
 #. type: Plain text
 #, no-wrap
 msgid "[[!meta title=\"Services\"]]\n"
 msgstr "[[!meta title=\"Services\"]]\n"
 
+#. type: Plain text
+#, no-wrap
+msgid "<script>\n"
+msgstr "<script>\n"
+
+#. type: Plain text
+#, no-wrap
+msgid ""
+"var content = d3.select(\"#content\");\n"
+"var consensus_div = content.append(\"div\")\n"
+"consensus_div.append(\"div\")\n"
+"  .attr(\"id\", \"consensus-pie\")\n"
+"  .style(\"width\", PieDrawer.width + \"px\")\n"
+"  .style(\"height\", PieDrawer.height + \"px\")\n"
+"  .style(\"float\", \"left\");\n"
+"consensus_div.append(\"div\")\n"
+"  .style(\"width\", \"400px\")\n"
+"  .style(\"margin-left\", \"5px\")\n"
+"  .style(\"line-height\", PieDrawer.height + \"px\")\n"
+"  .style(\"font-weight\", \"bold\")\n"
+"  .style(\"float\", \"left\")\n"
+"  .text(L10n.consensus_weight);\n"
+msgstr ""
+"var content = d3.select(\"#content\");\n"
+"var consensus_div = content.append(\"div\")\n"
+"consensus_div.append(\"div\")\n"
+"  .attr(\"id\", \"consensus-pie\")\n"
+"  .style(\"width\", PieDrawer.width + \"px\")\n"
+"  .style(\"height\", PieDrawer.height + \"px\")\n"
+"  .style(\"float\", \"left\");\n"
+"consensus_div.append(\"div\")\n"
+"  .style(\"width\", \"400px\")\n"
+"  .style(\"margin-left\", \"5px\")\n"
+"  .style(\"line-height\", PieDrawer.height + \"px\")\n"
+"  .style(\"font-weight\", \"bold\")\n"
+"  .style(\"float\", \"left\")\n"
+"  .text(L10n.consensus_weight);\n"
+
+#. type: Plain text
+#, no-wrap
+msgid ""
+"var exit_div = content.append(\"div\")\n"
+"exit_div.append(\"div\")\n"
+"  .attr(\"id\", \"exit-pie\")\n"
+"  .style(\"width\", PieDrawer.width)\n"
+"  .style(\"height\", PieDrawer.height)\n"
+"  .style(\"clear\", \"left\")\n"
+"  .style(\"float\", \"left\");\n"
+"exit_div.append(\"div\")\n"
+"  .style(\"width\", \"400px\")\n"
+"  .style(\"margin-left\", \"5px\")\n"
+"  .style(\"line-height\", PieDrawer.height + \"px\")\n"
+"  .style(\"font-weight\", \"bold\")\n"
+"  .style(\"float\", \"left\")\n"
+"  .text(L10n.exit_probability);\n"
+msgstr ""
+"var exit_div = content.append(\"div\")\n"
+"exit_div.append(\"div\")\n"
+"  .attr(\"id\", \"exit-pie\")\n"
+"  .style(\"width\", PieDrawer.width)\n"
+"  .style(\"height\", PieDrawer.height)\n"
+"  .style(\"clear\", \"left\")\n"
+"  .style(\"float\", \"left\");\n"
+"exit_div.append(\"div\")\n"
+"  .style(\"width\", \"400px\")\n"
+"  .style(\"margin-left\", \"5px\")\n"
+"  .style(\"line-height\", PieDrawer.height + \"px\")\n"
+"  .style(\"font-weight\", \"bold\")\n"
+"  .style(\"float\", \"left\")\n"
+"  .text(L10n.exit_probability);\n"
+
+#. type: Plain text
+#, no-wrap
+msgid ""
+"content.append(\"div\")\n"
+"  .style(\"clear\", \"left\");\n"
+msgstr ""
+"content.append(\"div\")\n"
+"  .style(\"clear\", \"left\");\n"
+
+#. type: Plain text
+#, no-wrap
+msgid ""
+"new ConsensusPieDrawer(\"#consensus-pie\").draw();\n"
+"new ExitPieDrawer(\"#exit-pie\").draw();\n"
+"</script>\n"
+msgstr ""
+"new ConsensusPieDrawer(\"#consensus-pie\").draw();\n"
+"new ExitPieDrawer(\"#exit-pie\").draw();\n"
+"</script>\n"
+
+#. type: Title =
+#, no-wrap
+msgid "Relais\n"
+msgstr "Relays\n"
+
 #. type: Plain text
 msgid "Nos oignons fait actuellement fonctionner les relais Tor suivants :"
 msgstr "Nos Oignons currently runs the following Tor relays:"
@@ -114,3 +240,29 @@ msgstr ""
 "    </tr>\n"
 "  </tfoot>\n"
 "</table>\n"
+
+#. type: Plain text
+#, no-wrap
+msgid ""
+"<script>\n"
+"d3.select(\"#content\").append(\"h1\")\n"
+"  .text(L10n.bandwidth);\n"
+"d3.select(\"#content\").append(\"div\")\n"
+"  .attr(\"id\", \"bandwidth\")\n"
+"  .text(L10n.loading);\n"
+msgstr ""
+"<script>\n"
+"d3.select(\"#content\").append(\"h1\")\n"
+"  .text(L10n.bandwidth);\n"
+"d3.select(\"#content\").append(\"div\")\n"
+"  .attr(\"id\", \"bandwidth\")\n"
+"  .text(L10n.loading);\n"
+
+#. type: Plain text
+#, no-wrap
+msgid ""
+"new BwDrawer(\"#bandwidth\").draw();\n"
+"</script>\n"
+msgstr ""
+"new BwDrawer(\"#bandwidth\").draw();\n"
+"</script>\n"
index 084f7203da839353613af0bf655c7c7e7255f4ed..88bb60c0e2fa9c24f8fee41b3095aba9853634d9 100644 (file)
@@ -1,5 +1,53 @@
+[[!meta script="assets/l10n.fr"]]
+[[!meta script="assets/relays"]]
+[[!meta script="assets/d3/d3.v3.min"]]
+[[!meta script="assets/bw_graphs"]]
+[[!meta stylesheet="assets/bw_graphs" rel="stylesheet"]]
+[[!meta script="assets/pie_graphs"]]
 [[!meta title="Services"]]
 
+<script>
+
+var content = d3.select("#content");
+var consensus_div = content.append("div")
+consensus_div.append("div")
+  .attr("id", "consensus-pie")
+  .style("width", PieDrawer.width + "px")
+  .style("height", PieDrawer.height + "px")
+  .style("float", "left");
+consensus_div.append("div")
+  .style("width", "400px")
+  .style("margin-left", "5px")
+  .style("line-height", PieDrawer.height + "px")
+  .style("font-weight", "bold")
+  .style("float", "left")
+  .text(L10n.consensus_weight);
+
+var exit_div = content.append("div")
+exit_div.append("div")
+  .attr("id", "exit-pie")
+  .style("width", PieDrawer.width)
+  .style("height", PieDrawer.height)
+  .style("clear", "left")
+  .style("float", "left");
+exit_div.append("div")
+  .style("width", "400px")
+  .style("margin-left", "5px")
+  .style("line-height", PieDrawer.height + "px")
+  .style("font-weight", "bold")
+  .style("float", "left")
+  .text(L10n.exit_probability);
+
+content.append("div")
+  .style("clear", "left");
+
+new ConsensusPieDrawer("#consensus-pie").draw();
+new ExitPieDrawer("#exit-pie").draw();
+</script>
+
+Relais
+======
+
 Nos oignons fait actuellement fonctionner les relais Tor suivants :
 
 <table>
@@ -48,3 +96,13 @@ Nos oignons fait actuellement fonctionner les relais Tor suivants :
     </tr>
   </tfoot>
 </table>
+
+<script>
+d3.select("#content").append("h1")
+  .text(L10n.bandwidth);
+d3.select("#content").append("div")
+  .attr("id", "bandwidth")
+  .text(L10n.loading);
+
+new BwDrawer("#bandwidth").draw();
+</script>
diff --git a/Services/Participation.html b/Services/Participation.html
deleted file mode 100644 (file)
index e3edb65..0000000
+++ /dev/null
@@ -1,328 +0,0 @@
-[[!meta script="assets/relays"]]
-[[!meta script="assets/d3/d3.v3.min"]]
-
-<style type="text/css">
-
-.arc path {
-  stroke: #fff;
-}
-
-</style>
-
-<div style="float: left; width: 45%">
-  <h2 style="text-align: center;">Consensus weight</h2>
-  <div id="consensus-pie"></div>
-</div>
-<div style="float: left; width: 45%; margin-left: 3%">
-  <h2 style="text-align: center;">Exit probability</h2>
-  <div id="exit-pie"></div>
-</div>
-<div style="clear: left;"></div>
-
-<script type="text/javascript">
-
-var width = 250,
-    height = 250,
-    radius = Math.min(width, height) / 2,
-    formatPercent = d3.format(".2%");
-
-var color_nos_oignons = "#ffa430";
-var color_others = "#57075f";
-
-var arc = d3.svg.arc()
-    .outerRadius(radius - 30)
-    .innerRadius(radius - 80);
-
-var labelRadius = radius - 15;
-
-var pie = d3.layout.pie()
-    .sort(null)
-    .value(function(d) { return d.frac; });
-
-var onionoo_url = "https://onionoo.torproject.org/details?type=relay&contact=adminsys@nos-oignons.net";
-
-var search_terms = "";
-nos_oignons_relays.forEach(function(r) {
-  search_terms += " $" + r.fingerprint
-});
-
-function PieDrawer() { };
-PieDrawer.prototype.getSelector = undefined;
-PieDrawer.prototype.getFieldName = undefined;
-PieDrawer.prototype.getField = undefined;
-PieDrawer.prototype.draw = function() {
-  var svg = d3.select(this.getSelector()).append("svg")
-      .style("margin", "auto")
-      .attr("width", width)
-      .attr("height", height)
-    .append("g")
-      .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
-
-  var text = svg.append("text")
-      .attr("text-anchor", "middle")
-      .attr("dy", ".35em")
-      .text("Loading…");
-
-  var field_getter = this.getField;
-  d3.json(onionoo_url + "&fields=fingerprint," + this.getFieldName(), function(error, raw_data) {
-    var frac_nos_oignons = 0;
-    raw_data['relays'].forEach(function(d) {
-      nos_oignons_relays.forEach(function(r) {
-        if (r.fingerprint == d.fingerprint) {
-          var value = field_getter(d);
-          if (value) {
-            frac_nos_oignons += value;
-          }
-        }
-      });
-    });
-    var frac_others = 1 - frac_nos_oignons;
-
-    data = [ { name: 'Nos oignons', frac: frac_nos_oignons, color: color_nos_oignons, },
-             { name: 'Others',      frac: frac_others,      color: color_others }, ];
-
-    text.text(formatPercent(data[0].frac));
-
-    var g = svg.selectAll(".arc")
-        .data(pie(data))
-      .enter().append("g")
-        .attr("class", "arc");
-
-    g.append("path")
-        .attr("d", arc)
-        .style("fill", function(d) { return d.data.color; });
-
-  });
-}
-
-function ConsensusPieDrawer() { };
-ConsensusPieDrawer.prototype = new PieDrawer();
-ConsensusPieDrawer.prototype.getSelector = function() {
-  return "#consensus-pie";
-};
-ConsensusPieDrawer.prototype.getFieldName = function() {
-  return "consensus_weight_fraction";
-};
-ConsensusPieDrawer.prototype.getField = function(r) {
-  return r.consensus_weight_fraction;
-};
-
-function ExitPieDrawer() { };
-ExitPieDrawer.prototype = new PieDrawer();
-ExitPieDrawer.prototype.getSelector = function() {
-  return "#exit-pie";
-};
-ExitPieDrawer.prototype.getFieldName = function() {
-  return "exit_probability";
-};
-ExitPieDrawer.prototype.getField = function(r) {
-  return r.exit_probability;
-};
-/*
-new ConsensusPieDrawer().draw();
-new ExitPieDrawer().draw();
-*/
-</script>
-
-<h2>Bandwidth</h2>
-
-<h3>3 days / read</h3>
-<div id="bandwidth-3d-read"></div>
-<h3>3 days / write</h3>
-<div id="bandwidth-3d-write"></div>
-<h3>1 week / read</h3>
-<div id="bandwidth-1w-read"></div>
-<h3>1 week / write</h3>
-<div id="bandwidth-1w-write"></div>
-<h3>1 month / read</h3>
-<div id="bandwidth-1m-read"></div>
-<h3>1 month / write</h3>
-<div id="bandwidth-1m-write"></div>
-<h3>3 months / read</h3>
-<div id="bandwidth-3m-read"></div>
-<h3>3 months / write</h3>
-<div id="bandwidth-3m-write"></div>
-<h3>1 year / read</h3>
-<div id="bandwidth-1y-read"></div>
-<h3>1 year / write</h3>
-<div id="bandwidth-1y-write"></div>
-
-<style type="text/css">
-
-.axis path,
-.axis line {
-  fill: none;
-  stroke: #000;
-  shape-rendering: crispEdges;
-}
-
-</style>
-
-<script type="text/javascript">
-
-var margin = {top: 50, right: 90, bottom: 90, left: 130},
-    width = 700 - margin.left - margin.right,
-    height = 400 - margin.top - margin.bottom;
-
-var parseTime = d3.time.format("%Y-%m-%d %H:%M:%S").parse,
-    bwFormatter = d3.format(".f");
-
-var x = d3.time.scale()
-    .range([0, width]);
-
-var y = d3.scale.linear()
-    .range([height, 0]);
-
-var color = d3.scale.category20();
-
-var xAxis = d3.svg.axis()
-    .scale(x)
-    .orient("bottom");
-
-var yAxis = d3.svg.axis()
-    .scale(y)
-    .orient("left")
-    .tickFormat(function(d) { return (d == 0) ? "" : bwFormatter(Math.abs(d)) + " Mbit/s " + ((d > 0) ? "in" : "out"); });
-
-var area = d3.svg.area()
-    .x(function(d) { return x(d.date); })
-    .y0(function(d) { return y(d.y0); })
-    .y1(function(d) { return y(d.y0 + d.y); });
-
-var read_stack = d3.layout.stack()
-    .values(function(d) { console.log(JSON.stringify(d)); return d.read_values; });
-var write_stack = d3.layout.stack()
-    .values(function(d) { console.log(JSON.stringify(d)); return d.write_values; })
-    .y(function(d) { return -d.y; });
-
-var onionoo_url = "https://onionoo.torproject.org/bandwidth?type=relay&contact=adminsys@nos-oignons.net";
-
-function extract_values(history, minTime, maxTime) {
-  var values = [];
-  var first = parseTime(history.first);
-  var last = parseTime(history.last);
-  var i = 0;
-  for (var current = minTime; current <= maxTime; current = d3.time.second.offset(current, history.interval)) {
-    if (first <= current && current <= last) {
-      values.push({ date: current, y: history.factor * history.values[i++] * 8 / 1000000 });
-    }
-  }
-  return values;
-}
-
-function draw_bandwidth_graph(raw_data, selector, period) {
-  var svg = d3.select(selector).append("svg")
-      .attr("width", width + margin.left + margin.right)
-      .attr("height", height + margin.top + margin.bottom)
-    .append("g")
-      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
-
-  var valid_fingerprints = [];
-  nos_oignons_relays.forEach(function(r) {
-    var relay_data = raw_data["relays"].filter(function(d) { return d.fingerprint == r.fingerprint; })[0];
-    if (relay_data && relay_data['read_history'][period] && relay_data['write_history'][period]) {
-      valid_fingerprints.push(r.fingerprint);
-    }
-  });
-  color.domain(valid_fingerprints);
-
-  var minTime = d3.max(raw_data.relays.map(function(d) {
-        return d['read_history'][period] && parseTime(d['read_history'][period].first) &&
-               d['write_history'][period] && parseTime(d['write_history'][period].first);
-      }));
-  var maxTime = d3.min(raw_data.relays.map(function(d) {
-        return d['read_history'][period] && parseTime(d['read_history'][period].last) &&
-               d['write_history'][period] && parseTime(d['write_history'][period].last);
-      }));
-  var maxReadBandwidth = 0;
-  var maxWriteBandwidth = 0;
-
-  var data = color.domain().map(function(fingerprint) {
-        var relay_data = raw_data["relays"].filter(function(d) { return d.fingerprint == fingerprint; })[0];
-        var read_history = relay_data['read_history'][period];
-        var write_history = relay_data['write_history'][period];
-        var read_values = extract_values(read_history, minTime, maxTime);
-        var write_values = extract_values(write_history, minTime, maxTime);
-
-        maxReadBandwidth = maxReadBandwidth + d3.max(read_values.map(function(d) { return d.y; }));
-        maxWriteBandwidth = maxWriteBandwidth + d3.max(write_values.map(function(d) { return d.y; }));
-
-        return {
-          fingerprint: fingerprint,
-          read_values: read_values,
-          write_values: write_values,
-        };
-      });
-
-  x.domain([minTime, maxTime]);
-  y.domain([-maxWriteBandwidth, maxReadBandwidth]);
-
-  var read_graph = svg.selectAll(".read_graph")
-      .data(read_stack(data))
-    .enter().append("g")
-      .attr("class", "read_graph");
-  read_graph.append("path")
-      .attr("class", "area")
-      .attr("d", function(d) { return area(d.read_values); })
-      .style("fill", function(d) { return color(d.fingerprint); });
-
-  var write_graph = svg.selectAll(".write_graph")
-      .data(write_stack(data))
-    .enter().append("g")
-      .attr("class", "write_graph");
-  write_graph.append("path")
-      .attr("class", "area")
-      .attr("d", function(d) { return area(d.write_values); })
-      .style("fill", function(d) { return color(d.fingerprint); });
-
-  svg.append("g")
-      .attr("class", "x axis")
-      .attr("transform", "translate(0," + height + ")")
-      .call(xAxis)
-        .selectAll("text")
-        .style("text-anchor", "end")
-        .attr("transform", "rotate(-90) translate(-10, 0)");
-
-  svg.append("g")
-      .attr("class", "y axis")
-      .call(yAxis);
-
-  var legend = svg.selectAll(".legend")
-      .data(color.domain().slice().reverse())
-    .enter().append("g")
-      .attr("class", "legend")
-      .attr("transform", function(d, i) { return "translate(0," + ((i * 20) - margin.top) + ")"; });
-
-  legend.append("rect")
-      .attr("x", width - 18)
-      .attr("width", 18)
-      .attr("height", 18)
-      .style("fill", color);
-
-  legend.append("text")
-      .attr("x", width - 24)
-      .attr("y", 9)
-      .attr("dy", ".35em")
-      .style("text-anchor", "end")
-      .text(function(d) {
-        return nos_oignons_relays.filter(function(r) { return r.fingerprint == d; })[0].name;
-      });
-};
-
-d3.json(onionoo_url, function(error, raw_data) {
-  draw_bandwidth_graph(raw_data, "#bandwidth-3d-read", "3_days");
-  draw_bandwidth_graph(raw_data, "#bandwidth-3d-read", "3_months");
-/*
-  draw_bandwidth_graph(raw_data, "#bandwidth-3d-write", "write_history", "3_days");
-  draw_bandwidth_graph(raw_data, "#bandwidth-1w-read", "read_history", "1_week");
-  draw_bandwidth_graph(raw_data, "#bandwidth-1w-write", "write_history", "1_week");
-  draw_bandwidth_graph(raw_data, "#bandwidth-1m-read", "read_history", "1_month");
-  draw_bandwidth_graph(raw_data, "#bandwidth-1m-write", "write_history", "1_month");
-  draw_bandwidth_graph(raw_data, "#bandwidth-3m-read", "read_history", "3_months");
-  draw_bandwidth_graph(raw_data, "#bandwidth-3m-write", "write_history", "3_months");
-  draw_bandwidth_graph(raw_data, "#bandwidth-1y-read", "read_history", "1_year");
-  draw_bandwidth_graph(raw_data, "#bandwidth-1y-write", "write_history", "1_year");
-*/
-});
-
-</script>
diff --git a/assets/bw_graphs.css b/assets/bw_graphs.css
new file mode 100644 (file)
index 0000000..a455147
--- /dev/null
@@ -0,0 +1,19 @@
+.axis path,
+.axis line {
+  fill: none;
+  stroke: #000;
+  shape-rendering: crispEdges;
+}
+
+#bandwidth form {
+  margin: auto;
+  width: 35em;
+}
+
+#bandwidth div {
+  float: left;
+  width: 7em;
+}
+#bandwidth svg {
+  clear: left;
+}
diff --git a/assets/bw_graphs.js b/assets/bw_graphs.js
new file mode 100644 (file)
index 0000000..bcd0216
--- /dev/null
@@ -0,0 +1,224 @@
+function BwDrawer(selector) {
+  this.selector = selector;
+};
+BwDrawer.prototype = new BwDrawer();
+
+BwDrawer.margin = {top: 50, right: 10, bottom: 90, left: 130};
+BwDrawer.width = 600 - BwDrawer.margin.left - BwDrawer.margin.right;
+BwDrawer.height = 400 - BwDrawer.margin.top - BwDrawer.margin.bottom;
+
+BwDrawer.parseTime = d3.time.format("%Y-%m-%d %H:%M:%S").parse;
+BwDrawer.bwFormatter = d3.format(".f");
+
+BwDrawer.x = d3.time.scale()
+    .range([0, BwDrawer.width]);
+
+BwDrawer.y = d3.scale.linear()
+    .range([BwDrawer.height, 0]);
+
+BwDrawer.color = d3.scale.category20();
+
+BwDrawer.xAxis = d3.svg.axis()
+    .scale(BwDrawer.x)
+    .orient("bottom");
+
+BwDrawer.yAxis = d3.svg.axis()
+    .scale(BwDrawer.y)
+    .orient("left")
+    .tickFormat(function(d) { return (d == 0) ? "" : BwDrawer.bwFormatter(Math.abs(d)) + " Mbit/s " + ((d > 0) ? "in" : "out"); });
+
+BwDrawer.area = d3.svg.area()
+    .x(function(d) { return BwDrawer.x(d.date); })
+    .y0(function(d) { return BwDrawer.y(d.y0); })
+    .y1(function(d) { return BwDrawer.y(d.y0 + d.y); });
+
+BwDrawer.read_stack = d3.layout.stack()
+    .values(function(d) { return d.read_values; });
+BwDrawer.write_stack = d3.layout.stack()
+    .values(function(d) { return d.write_values; });
+
+BwDrawer.onionoo_url = "https://onionoo.torproject.org/bandwidth?type=relay&contact=adminsys@nos-oignons.net";
+
+BwDrawer.periods = [
+    { id: "3_days", label: L10n.t_3_days },
+    { id: "1_week", label: L10n.t_1_week },
+    { id: "1_month", label: L10n.t_1_month },
+    { id: "3_months", label: L10n.t_3_months },
+    { id: "1_year", label: L10n.t_1_year },
+  ];
+
+BwDrawer.extract_values = function(history, interval, minTime, maxTime) {
+  var values = [];
+  var first = history ? BwDrawer.parseTime(history.first) : maxTime;
+  var last = history ? BwDrawer.parseTime(history.last) : minTime;
+  var i = 0;
+  for (var current = minTime; current <= maxTime; current = d3.time.second.offset(current, interval)) {
+    values.push({ date: current,
+                  y: (first <= current && current <= last) ? history.factor * history.values[i++] * 8 / 1000000 : 0
+                });
+  }
+  return values;
+}
+
+BwDrawer.draw_bandwidth_graph = function(raw_data, selector, period) {
+  var update_period;
+
+  var svg = d3.select(selector).append("svg")
+      .attr("width", BwDrawer.width + BwDrawer.margin.left + BwDrawer.margin.right)
+      .attr("height", BwDrawer.height + BwDrawer.margin.top + BwDrawer.margin.bottom)
+    .append("g")
+      .attr("transform", "translate(" + BwDrawer.margin.left + "," + BwDrawer.margin.top + ")");
+
+  var form = d3.select(selector).append("form")
+      .attr("action", "#");
+  BwDrawer.periods.forEach(function(p) {
+    var div = form.append("div");
+    var radio = div.append("input")
+      .attr("type", "radio")
+      .attr("name", "period")
+      .attr("id", "period_" + p.id)
+      .on("click", function() { update_period(p.id); });
+    div.append("label")
+      .attr("for", "period_" + p.id)
+      .text(p.label);
+    if (p.id == BwDrawer.periods[0].id) {
+      radio.attr("checked", true);
+    }
+  });
+
+  var valid_fingerprints = [];
+  nos_oignons_relays.forEach(function(r) {
+    var relay_data = raw_data["relays"].filter(function(d) { return d.fingerprint == r.fingerprint; })[0];
+    valid_fingerprints.push(r.fingerprint);
+  });
+  BwDrawer.color.domain(valid_fingerprints);
+
+  var bw_data = {};
+  BwDrawer.periods.map(function(p) { return p.id; }).forEach(function(period) {
+    var interval = d3.max(raw_data.relays, function(d) {
+      return d['read_history'][period] && d['read_history'][period].interval;
+    });
+    raw_data.relays.forEach(function(d) {
+      if ((d['read_history'][period] && d['read_history'][period].interval != interval) ||
+          (d['write_history'][period] && d['write_history'][period].interval != interval)) {
+        throw "PANIC: Different interval for different relays in the same time period.";
+      }
+    });
+    var minTime = d3.max(raw_data.relays, function(d) {
+      return d['read_history'][period] && BwDrawer.parseTime(d['read_history'][period].first) &&
+             d['write_history'][period] && BwDrawer.parseTime(d['write_history'][period].first);
+    });
+    var maxTime = d3.min(raw_data.relays, function(d) {
+      return d['read_history'][period] && BwDrawer.parseTime(d['read_history'][period].last) &&
+             d['write_history'][period] && BwDrawer.parseTime(d['write_history'][period].last);
+    });
+
+    var maxReadBandwidth = 0;
+    var maxWriteBandwidth = 0;
+
+    var values = BwDrawer.color.domain().map(function(fingerprint) {
+      var relay_data = raw_data["relays"].filter(function(d) { return d.fingerprint == fingerprint; })[0];
+      var read_history = relay_data['read_history'][period];
+      var write_history = relay_data['write_history'][period];
+      var read_values = BwDrawer.extract_values(read_history, interval, minTime, maxTime);
+      var write_values = BwDrawer.extract_values(write_history, interval, minTime, maxTime);
+
+      maxReadBandwidth = maxReadBandwidth + d3.max(read_values, function(d) { return d.y; });
+      maxWriteBandwidth = maxWriteBandwidth + d3.max(write_values, function(d) { return d.y; });
+
+      return {
+        fingerprint: fingerprint,
+        read_values: read_values,
+        write_values: write_values.map(function(d) { d.y = -d.y; return d })
+      };
+    });
+    bw_data[period] = { minTime: minTime,
+                        maxTime: maxTime,
+                        maxReadBandwidth: maxReadBandwidth,
+                        maxWriteBandwidth: maxWriteBandwidth,
+                        values: values };
+  });
+
+  BwDrawer.y.domain([-d3.max(d3.values(bw_data), function(d) { return d.maxWriteBandwidth; }),
+            d3.max(d3.values(bw_data), function(d) { return d.maxReadBandwidth; })]);
+
+  var initial_period = BwDrawer.periods[0].id;
+
+  BwDrawer.x.domain([bw_data[initial_period].minTime, bw_data[initial_period].maxTime]);
+
+  var read_graph = svg.selectAll(".read_graph")
+      .data(BwDrawer.read_stack(bw_data[initial_period].values))
+    .enter().append("path")
+      .attr("class", "read_graph area")
+      .attr("d", function(d) { return BwDrawer.area(d.read_values); })
+      .style("fill", function(d) { return BwDrawer.color(d.fingerprint); });
+
+  var write_graph = svg.selectAll(".write_graph")
+      .data(BwDrawer.write_stack(bw_data[initial_period].values))
+    .enter().append("path")
+      .attr("class", "write_graph area")
+      .attr("d", function(d) { return BwDrawer.area(d.write_values); })
+      .style("fill", function(d) { return BwDrawer.color(d.fingerprint); });
+
+  update_period = function(period) {
+    BwDrawer.x.domain([bw_data[period].minTime, bw_data[period].maxTime]);
+    var t = svg.transition().duration(300);
+    t.select(".x.axis").call(BwDrawer.xAxis);
+    t.selectAll(".read_graph").style("fill-opacity", 0);
+    t.selectAll(".write_graph").style("fill-opacity", 0);
+    t.each("end", function() {
+      d3.selectAll(".read_graph").data(BwDrawer.read_stack(bw_data[period].values));
+      d3.selectAll(".write_graph").data(BwDrawer.write_stack(bw_data[period].values));
+      d3.selectAll(".read_graph").attr("d", function(d) { return BwDrawer.area(d.read_values); })
+      d3.selectAll(".write_graph")
+        .attr("d", function(d) { return BwDrawer.area(d.write_values); })
+      var t2 = svg.transition().duration(100);
+      t2.selectAll(".read_graph").style("fill-opacity", 1);
+      t2.selectAll(".write_graph").style("fill-opacity", 1);
+    });
+    d3.selectAll(".x.axis text")
+      .style("text-anchor", "end")
+      .attr("transform", "rotate(-90) translate(-10, 0)");
+  }
+
+  svg.append("g")
+      .attr("class", "x axis")
+      .attr("transform", "translate(0," + BwDrawer.height + ")")
+      .call(BwDrawer.xAxis)
+        .selectAll("text")
+        .style("text-anchor", "end")
+        .attr("transform", "rotate(-90) translate(-10, 0)");
+
+  svg.append("g")
+      .attr("class", "y axis")
+      .call(BwDrawer.yAxis);
+
+  var legend = svg.selectAll(".legend")
+      .data(BwDrawer.color.domain().slice().reverse())
+    .enter().append("g")
+      .attr("class", "legend")
+      .attr("transform", function(d, i) { return "translate(0," + ((i * 20) - BwDrawer.margin.top) + ")"; });
+
+  legend.append("rect")
+      .attr("x", BwDrawer.width - 18)
+      .attr("width", 18)
+      .attr("height", 18)
+      .style("fill", BwDrawer.color);
+
+  legend.append("text")
+      .attr("x", BwDrawer.width - 24)
+      .attr("y", 9)
+      .attr("dy", ".35em")
+      .style("text-anchor", "end")
+      .text(function(d) {
+        return nos_oignons_relays.filter(function(r) { return r.fingerprint == d; })[0].name;
+      });
+};
+
+BwDrawer.prototype.draw = function() {
+  var selector = this.selector;
+  d3.json(BwDrawer.onionoo_url, function(error, raw_data) {
+    d3.select(selector).text("");
+    BwDrawer.draw_bandwidth_graph(raw_data, selector);
+  });
+};
diff --git a/assets/l10n.en.js b/assets/l10n.en.js
new file mode 100644 (file)
index 0000000..f89d7a5
--- /dev/null
@@ -0,0 +1,12 @@
+function L10n() { };
+L10n.loading = "Loading…";
+L10n.consensus_weight = "Weight in the whole network";
+L10n.exit_probability = "Probability to be used as exit node";
+L10n.bandwidth = "Bandwidth";
+L10n.nos_oignons = "Nos oignons";
+L10n.others = "Others";
+L10n.t_3_days = "3 days";
+L10n.t_1_week = "1 week";
+L10n.t_1_month = "1 month";
+L10n.t_3_months = "3 months";
+L10n.t_1_year = "1 year";
diff --git a/assets/l10n.fr.js b/assets/l10n.fr.js
new file mode 100644 (file)
index 0000000..0e948d5
--- /dev/null
@@ -0,0 +1,12 @@
+function L10n() { };
+L10n.loading = "Chargement…";
+L10n.consensus_weight = "Poid dans l'ensemble du réseau";
+L10n.exit_probability = "Probabilité d'être utilisé comme nœud de sortie";
+L10n.bandwidth = "Bande passante";
+L10n.nos_oignons = "Nos oignons";
+L10n.others = "Autres";
+L10n.t_3_days = "3 jours";
+L10n.t_1_week = "1 semaine";
+L10n.t_1_month = "1 mois";
+L10n.t_3_months = "3 mois";
+L10n.t_1_year = "1 an";
diff --git a/assets/pie_graphs.js b/assets/pie_graphs.js
new file mode 100644 (file)
index 0000000..e607fa5
--- /dev/null
@@ -0,0 +1,94 @@
+function PieDrawer(selector) {
+  this.selector = selector;
+};
+PieDrawer.width = 150;
+PieDrawer.height = 150;
+PieDrawer.radius = Math.min(PieDrawer.width, PieDrawer.height) / 2;
+PieDrawer.formatPercent = d3.format(".2%");
+
+PieDrawer.color_nos_oignons = "#ffa430";
+PieDrawer.color_others = "#57075f";
+
+PieDrawer.arc = d3.svg.arc()
+    .outerRadius(PieDrawer.radius - 10)
+    .innerRadius(PieDrawer.radius - 40);
+
+PieDrawer.labelRadius = PieDrawer.radius - 15;
+
+PieDrawer.pie = d3.layout.pie()
+    .sort(null)
+    .value(function(d) { return d.frac; });
+
+PieDrawer.onionoo_url = "https://onionoo.torproject.org/details?type=relay&contact=adminsys@nos-oignons.net";
+
+PieDrawer.prototype.getFieldName = undefined;
+PieDrawer.prototype.getField = undefined;
+PieDrawer.prototype.draw = function() {
+  var svg = d3.select(this.selector).append("svg")
+      .style("margin", "auto")
+      .attr("width", PieDrawer.width)
+      .attr("height", PieDrawer.height)
+    .append("g")
+      .attr("transform", "translate(" + PieDrawer.width / 2 + "," + PieDrawer.height / 2 + ")");
+
+  var text = svg.append("text")
+      .attr("text-anchor", "middle")
+      .attr("dy", ".35em")
+      .text(L10n.loading);
+
+  var field_getter = this.getField;
+  d3.json(PieDrawer.onionoo_url + "&fields=fingerprint," + this.getFieldName(), function(error, raw_data) {
+    var frac_nos_oignons = 0;
+    raw_data['relays'].forEach(function(d) {
+      nos_oignons_relays.forEach(function(r) {
+        if (r.fingerprint == d.fingerprint) {
+          var value = field_getter(d);
+          if (value) {
+            frac_nos_oignons += value;
+          }
+        }
+      });
+    });
+    var frac_others = 1 - frac_nos_oignons;
+
+    data = [ { name: L10n.nos_oignons, frac: frac_nos_oignons, color: PieDrawer.color_nos_oignons, },
+             { name: L10n.others,      frac: frac_others,      color: PieDrawer.color_others }, ];
+
+    text.text(PieDrawer.formatPercent(data[0].frac));
+
+    var g = svg.selectAll(".arc")
+        .data(PieDrawer.pie(data))
+      .enter().append("g")
+        .attr("class", "arc");
+
+    g.append("path")
+        .attr("d", PieDrawer.arc)
+        .style("fill", function(d) { return d.data.color; });
+
+  });
+}
+
+function ConsensusPieDrawer(selector) {
+  this.selector = selector;
+};
+ConsensusPieDrawer.prototype = new PieDrawer();
+ConsensusPieDrawer.prototype.getSelector = function() {
+  return "#consensus-pie";
+};
+ConsensusPieDrawer.prototype.getFieldName = function() {
+  return "consensus_weight_fraction";
+};
+ConsensusPieDrawer.prototype.getField = function(r) {
+  return r.consensus_weight_fraction;
+};
+
+function ExitPieDrawer(selector) {
+  this.selector = selector;
+};
+ExitPieDrawer.prototype = new PieDrawer();
+ExitPieDrawer.prototype.getFieldName = function() {
+  return "exit_probability";
+};
+ExitPieDrawer.prototype.getField = function(r) {
+  return r.exit_probability;
+};