From 4c2924543ea0f426c3342fe8430ca606a6dbc85c Mon Sep 17 00:00:00 2001 From: Lunar Date: Fri, 3 Jun 2016 16:14:25 +0200 Subject: [PATCH] =?utf8?q?Services=C2=A0:=20Ajout=20d'un=20graphe=20avec?= =?utf8?q?=20l'historique=20du=20poid=20dasn=20le=20consensus=20et=20de=20?= =?utf8?q?la=20probabilit=C3=A9=20d'=C3=AAtre=20n=C5=93ud=20de=20sortie?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- Services.mdwn | 10 ++ assets/bw_graphs.css | 4 +- assets/l10n.en.js | 1 + assets/l10n.fr.js | 1 + assets/weights_graphs.css | 19 +++ assets/weights_graphs.js | 247 ++++++++++++++++++++++++++++++++++++++ local.css | 4 +- 7 files changed, 282 insertions(+), 4 deletions(-) create mode 100644 assets/weights_graphs.css create mode 100644 assets/weights_graphs.js diff --git a/Services.mdwn b/Services.mdwn index f80c4f4..75ea52a 100644 --- a/Services.mdwn +++ b/Services.mdwn @@ -3,6 +3,8 @@ [[!meta script="assets/d3/d3.v3.min"]] [[!meta script="assets/bw_graphs"]] [[!meta stylesheet="assets/bw_graphs" rel="stylesheet"]] +[[!meta script="assets/weights_graphs"]] +[[!meta stylesheet="assets/weights_graphs" rel="stylesheet"]] [[!meta script="assets/pie_graphs"]] [[!meta title="Services"]] @@ -111,4 +113,12 @@ d3.select("#content").append("div") .text(L10n.loading); new BwDrawer("#bandwidth").draw(); + +d3.select("#content").append("h1") + .text(L10n.weights); +d3.select("#content").append("div") + .attr("id", "weights") + .text(L10n.loading); + +new WeightsDrawer("#weights").draw(); diff --git a/assets/bw_graphs.css b/assets/bw_graphs.css index a455147..7fd6abe 100644 --- a/assets/bw_graphs.css +++ b/assets/bw_graphs.css @@ -1,5 +1,5 @@ -.axis path, -.axis line { +#bandwidth .axis path, +#bandwidth .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; diff --git a/assets/l10n.en.js b/assets/l10n.en.js index 6360646..a03d9be 100644 --- a/assets/l10n.en.js +++ b/assets/l10n.en.js @@ -3,6 +3,7 @@ L10n.loading = "Loading…"; L10n.consensus_weight = "Nos oignons' weight in the Tor network"; L10n.exit_probability = "Probability to use one of our exit nodes"; L10n.bandwidth = "Bandwidth"; +L10n.weights = "Weights and probabilites"; L10n.nos_oignons = "Nos oignons"; L10n.others = "Others"; L10n.t_1_month = "1 month"; diff --git a/assets/l10n.fr.js b/assets/l10n.fr.js index 67c1056..d85a82c 100644 --- a/assets/l10n.fr.js +++ b/assets/l10n.fr.js @@ -3,6 +3,7 @@ L10n.loading = "Chargement…"; L10n.consensus_weight = "Poids de Nos oignons dans le réseau Tor"; L10n.exit_probability = "Probabilité d'utiliser un de nos nœuds de sortie"; L10n.bandwidth = "Bande passante"; +L10n.weights = "Poids et probabilités"; L10n.nos_oignons = "Nos oignons"; L10n.others = "Autres"; L10n.t_1_month = "1 mois"; diff --git a/assets/weights_graphs.css b/assets/weights_graphs.css new file mode 100644 index 0000000..ca1207c --- /dev/null +++ b/assets/weights_graphs.css @@ -0,0 +1,19 @@ +#weights .axis path, +#weights .axis line { + fill: none; + stroke: #000; + shape-rendering: crispEdges; +} + +#weights form { + margin: auto; + width: 35em; +} + +#weights div { + float: left; + width: 7em; +} +#weights svg { + clear: left; +} diff --git a/assets/weights_graphs.js b/assets/weights_graphs.js new file mode 100644 index 0000000..915a8b8 --- /dev/null +++ b/assets/weights_graphs.js @@ -0,0 +1,247 @@ +function WeightsDrawer(selector) { + this.selector = selector; + this.current_source = "consensus_weight_fraction"; + this.current_period = "1_month"; + this.weights_data = {}; + this.svg = null; +}; +WeightsDrawer.prototype = new WeightsDrawer(); + +WeightsDrawer.margin = {top: 50, right: 10, bottom: 90, left: 130}; +WeightsDrawer.width = 600 - WeightsDrawer.margin.left - WeightsDrawer.margin.right; +WeightsDrawer.height = 400 - WeightsDrawer.margin.top - WeightsDrawer.margin.bottom; + +WeightsDrawer.parseTime = d3.time.format("%Y-%m-%d %H:%M:%S").parse; +WeightsDrawer.percentFormatter = d3.format(".2%"); + +WeightsDrawer.x = d3.time.scale() + .range([0, WeightsDrawer.width]); + +WeightsDrawer.y = d3.scale.linear() + .range([WeightsDrawer.height, 0]); + +WeightsDrawer.xAxis = d3.svg.axis() + .scale(WeightsDrawer.x) + .orient("bottom"); + +WeightsDrawer.yAxis = d3.svg.axis() + .scale(WeightsDrawer.y) + .orient("left") + .tickFormat(function(d) { return (d == 0) ? "" : WeightsDrawer.percentFormatter(d); }); + +WeightsDrawer.area = d3.svg.area() + .x(function(d) { return WeightsDrawer.x(d.date); }) + .y0(function(d) { return WeightsDrawer.y(d.y0); }) + .y1(function(d) { return WeightsDrawer.y(d.y0 + d.y); }); + +WeightsDrawer.stack = d3.layout.stack() + .values(function(d) { return d.values; }); + +WeightsDrawer.onionoo_url = "https://onionoo.torproject.org/weights?type=relay&contact=adminsys@nos-oignons.net"; + +WeightsDrawer.periods = [ + { id: "1_month", label: L10n.t_1_month }, + { id: "3_months", label: L10n.t_3_months }, + { id: "1_year", label: L10n.t_1_year }, + { id: "5_years", label: L10n.t_5_years }, + ]; + +WeightsDrawer.current_period = WeightsDrawer.periods[0].id; + +WeightsDrawer.sources = [ + { id: "consensus_weight_fraction", label: L10n.consensus_weight }, + { id: "exit_probability", label: L10n.exit_probability }, + ]; + +WeightsDrawer.current_source = WeightsDrawer.sources[0].id; + +WeightsDrawer.extract_values = function(history, interval, minTime, maxTime) { + var values = []; + var first = history ? WeightsDrawer.parseTime(history.first) : maxTime; + var last = history ? WeightsDrawer.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++] : 0 + }); + } + return values; +} + +WeightsDrawer.color = d3.scale.ordinal(); +WeightsDrawer.color.domain(nos_oignons_relays.map(function(r) {return r.fingerprint})); +WeightsDrawer.color.range(nos_oignons_relays.map(function(r) {return r.color})); + +WeightsDrawer.prototype.draw_weights_graph = function(raw_data) { + var drawer = this; + + // Purge non running relays + raw_data.relays.forEach(function(r, i) { + if (typeof r.consensus_weight_fraction === 'undefined') { + raw_data.relays.splice(i, 1); + } + }); + + var form_source = d3.select(drawer.selector).append("form") + .attr("action", "#"); + WeightsDrawer.sources.forEach(function(s) { + var div = form_source.append("div"); + var radio = div.append("input") + .attr("type", "radio") + .attr("name", "source") + .attr("id", "source_" + s.id) + .on("click", function() { drawer.update_source(s.id); }); + div.append("label") + .attr("for", "source_" + s.id) + .text(s.label); + if (s.id == WeightsDrawer.sources[0].id) { + radio.attr("checked", true); + } + }); + + drawer.svg = d3.select(drawer.selector).append("svg") + .attr("width", WeightsDrawer.width + WeightsDrawer.margin.left + WeightsDrawer.margin.right) + .attr("height", WeightsDrawer.height + WeightsDrawer.margin.top + WeightsDrawer.margin.bottom) + .append("g") + .attr("transform", "translate(" + WeightsDrawer.margin.left + "," + WeightsDrawer.margin.top + ")"); + + var form_period = d3.select(drawer.selector).append("form") + .attr("action", "#"); + WeightsDrawer.periods.forEach(function(p) { + var div = form_period.append("div"); + var radio = div.append("input") + .attr("type", "radio") + .attr("name", "period") + .attr("id", "period_" + p.id) + .on("click", function() { drawer.update_period(p.id); }); + div.append("label") + .attr("for", "period_" + p.id) + .text(p.label); + if (p.id == WeightsDrawer.periods[0].id) { + radio.attr("checked", true); + } + }); + + WeightsDrawer.sources.map(function(s) { return s.id; }).forEach(function(source) { + drawer.weights_data[source] = {}; + WeightsDrawer.periods.map(function(p) { return p.id; }).forEach(function(period) { + var interval = d3.max(raw_data.relays, function(d) { + return d[source][period] && d[source][period].interval; + }); + raw_data.relays.forEach(function(d) { + if ((d[source][period] && d[source][period].interval != interval)) { + throw "PANIC: Different interval for different relays in the same time period."; + } + }); + var minTime = d3.min(raw_data.relays, function(d) { + return d[source][period] && WeightsDrawer.parseTime(d[source][period].first); + }); + var maxTime = d3.min(raw_data.relays, function(d) { + return d[source][period] && WeightsDrawer.parseTime(d[source][period].last); + }); + + var maxValue = 0; + + var relays = WeightsDrawer.color.domain().map(function(fingerprint) { + var relay_data = raw_data["relays"].filter(function(d) { return d.fingerprint == fingerprint; })[0]; + + var history = relay_data[source][period]; + var values = WeightsDrawer.extract_values(history, interval, minTime, maxTime); + maxValue = maxValue + d3.max(values, function(d) { return d.y; }); + + return { + fingerprint: fingerprint, + values: values, + }; + }); + drawer.weights_data[source][period] = { + minTime: minTime, + maxTime: maxTime, + maxValue: maxValue, + relays: relays + }; + }); + }); + + WeightsDrawer.y.domain([0, drawer.weights_data[WeightsDrawer.current_source][WeightsDrawer.current_period].maxValue]); + + WeightsDrawer.x.domain([drawer.weights_data[WeightsDrawer.current_source][WeightsDrawer.current_period].minTime, drawer.weights_data[WeightsDrawer.current_source][WeightsDrawer.current_period].maxTime]); + + var weight_graph = drawer.svg.selectAll(".weight_graph") + .data(WeightsDrawer.stack(drawer.weights_data[WeightsDrawer.current_source][WeightsDrawer.current_period].relays)) + .enter().append("path") + .attr("class", "weight_graph area") + .attr("d", function(d) { return WeightsDrawer.area(d.values); }) + .style("fill", function(d) { return WeightsDrawer.color(d.fingerprint); }); + + drawer.svg.append("g") + .attr("class", "x axis") + .attr("transform", "translate(0," + WeightsDrawer.height + ")") + .call(WeightsDrawer.xAxis) + .selectAll("text") + .style("text-anchor", "end") + .attr("transform", "rotate(-90) translate(-10, 0)"); + + drawer.svg.append("g") + .attr("class", "y axis") + .call(WeightsDrawer.yAxis); + + var legend = drawer.svg.selectAll(".legend") + .data(WeightsDrawer.color.domain().slice().reverse()) + .enter().append("g") + .attr("class", "legend") + .attr("transform", function(d, i) { return "translate(0," + ((i * 20) - WeightsDrawer.margin.top) + ")"; }); + + legend.append("rect") + .attr("x", WeightsDrawer.width - 18) + .attr("width", 18) + .attr("height", 18) + .style("fill", WeightsDrawer.color); + + legend.append("text") + .attr("x", WeightsDrawer.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; + }); +}; + +WeightsDrawer.prototype.refresh_graph = function() { + var drawer = this; + + WeightsDrawer.x.domain([drawer.weights_data[drawer.current_source][drawer.current_period].minTime, drawer.weights_data[drawer.current_source][drawer.current_period].maxTime]); + WeightsDrawer.y.domain([0, drawer.weights_data[drawer.current_source][drawer.current_period].maxValue]); + var t = drawer.svg.transition().duration(300); + t.select(".x.axis").call(WeightsDrawer.xAxis); + t.select(".y.axis").call(WeightsDrawer.yAxis); + t.selectAll(".weight_graph").style("fill-opacity", 0); + t.each("end", function() { + d3.selectAll(".weight_graph").data(WeightsDrawer.stack(drawer.weights_data[drawer.current_source][drawer.current_period].relays)); + d3.selectAll(".weight_graph").attr("d", function(d) { return WeightsDrawer.area(d.values); }) + var t2 = drawer.svg.transition().duration(100); + t2.selectAll(".weight_graph").style("fill-opacity", 1); + }); + d3.selectAll(".x.axis text") + .style("text-anchor", "end") + .attr("transform", "rotate(-90) translate(-10, 0)"); +} + +WeightsDrawer.prototype.update_source = function(source) { + this.current_source = source; + this.refresh_graph(); +} + +WeightsDrawer.prototype.update_period = function(period) { + this.current_period = period; + this.refresh_graph(); +} + +WeightsDrawer.prototype.draw = function() { + var drawer = this; + d3.json(WeightsDrawer.onionoo_url, function(error, raw_data) { + d3.select(drawer.selector).text(""); + drawer.draw_weights_graph(raw_data); + }); +}; diff --git a/local.css b/local.css index 53b5b54..ba0db98 100644 --- a/local.css +++ b/local.css @@ -415,8 +415,8 @@ main .footnotes { text-align: center; } -#bandwidth {text-align: center;} -#bandwidth svg {text-align: left;} +#bandwidth, #weights {text-align: center;} +#bandwidth svg, #weights svg {text-align: left;} /* Tor nodes table */ .tor-nodes {} -- 2.39.5