diff --git a/README.rst b/README.rst
--- a/README.rst
+++ b/README.rst
@@ -54,7 +54,7 @@ branching model, but with some modificat
Description of the branches:
- **default**: This is the stable branch.
-- **stable**: This is the main development branch.
+- **dev**: This is the main development branch.
- **hotfix-***: These branches branch off from the default branch and are used to
fix bugs which are in the stable version. They will be merged back into
*default* and *dev*. After the bug is fixed, the *PATCH* number must be
diff --git a/ennstatus/__init__.py b/ennstatus/__init__.py
--- a/ennstatus/__init__.py
+++ b/ennstatus/__init__.py
@@ -82,7 +82,10 @@ def create_app():
app.register_blueprint(status_page, url_prefix='/status')
from .statistics.views import statistics_page
- app.register_blueprint(statistics_page, url_prefix='/stats')
+ app.register_blueprint(statistics_page, url_prefix='/statistics')
+
+ from .data.views import data_page
+ app.register_blueprint(data_page, url_prefix='/data')
from .log import init_logging
init_logging(app)
diff --git a/ennstatus/data/__init__.py b/ennstatus/data/__init__.py
new file mode 100644
--- /dev/null
+++ b/ennstatus/data/__init__.py
@@ -0,0 +1,16 @@
+# Ënnstatus
+# Copyright (C) 2015 Dennis Fink
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
diff --git a/ennstatus/data/views.py b/ennstatus/data/views.py
new file mode 100644
--- /dev/null
+++ b/ennstatus/data/views.py
@@ -0,0 +1,42 @@
+# Ënnstatus
+# Copyright (C) 2015 Dennis Fink
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+from collections import defaultdict
+
+from flask import Blueprint, jsonify
+
+from ennstatus.status.functions import split_all_servers_to_types
+
+data_page = Blueprint('data', __name__)
+
+
+@data_page.route('/worldmap')
+def worldmap():
+ servers = split_all_servers_to_types()
+ countries = {}
+
+ for key, value in servers.items():
+ for server in value:
+ if server.country not in countries:
+ countries[server.country] = defaultdict(int)
+ countries[server.country][server.type] += 1
+ countries[server.country]['total'] += 1
+
+ maximum = max(i['total'] for i in countries.values())
+
+ countries['max'] = maximum
+
+ return jsonify(countries)
diff --git a/ennstatus/static/css/map.css b/ennstatus/static/css/map.css
--- a/ennstatus/static/css/map.css
+++ b/ennstatus/static/css/map.css
@@ -20,3 +20,14 @@
stroke-linejoin: round;
stroke-width: 0.1px;
}
+
+.tooltip {
+ color: #222;
+ background: #fff;
+ padding: .5em;
+ text-shadow: #f5f5f5 0 1px 0;
+ border-radius: 2px;
+ box-shadow: 0px 0px 2px 0px #a6a6a6;
+ opacity: 0.9;
+ position: absolute;
+}
diff --git a/ennstatus/static/js/worldmap.js b/ennstatus/static/js/worldmap.js
--- a/ennstatus/static/js/worldmap.js
+++ b/ennstatus/static/js/worldmap.js
@@ -35,9 +35,9 @@ setup(width, height);
function setup(width, height) {
projection = d3.geo.mercator()
- .translate([(width / 2), (height / 2)])
- .scale(width / 2 / Math.PI)
-
+ .translate([(width / 2), (height / 2)])
+ .scale(width / 2 / Math.PI)
+
path = d3.geo.path().projection(projection);
svg = d3.select("#chart").append("svg")
@@ -70,44 +70,60 @@ function draw(topo) {
.attr("d", path);
var country = g.selectAll(".country").data(topo);
- d3.json("/stats/data/worldmap", function(err, data) {
+ d3.json("/data/worldmap", function(err, data) {
- var colorscale = d3.scale.threshold().domain([2, 4, 8, 16, 32, 48]).range(["#f2f0f7", "#dadaeb", "#bcbddc", "#9e9ac8", "#756bb1", "#54278f"])
+ var colorscale = d3.scale.threshold().domain(d3.range(1, (data.max+1)/3).map(function(n) { var a=1, b=1, f=1; for(var i = 2; i <= n; i++) { f = a+b; a=b; b=f } return f;})).range([
+ "#aeffb9",
+ "#87ff97",
+ "#60ff76",
+ "#38ff54",
+ "#11ff32",
+ "#00e920",
+ "#00c21b",
+ "#009a15",
+ "#008713",
+ "#007310",
+ "#00600d",
+ "#004c0a"
+ ])
country.enter().insert("path")
.attr("class", "country")
.attr("d", path)
.attr("id", function(d, i) { return d.id; })
- .attr("title", function(d, i) {
- if (d.properties.name in data) {
- return "
" + d.properties.name + "
" + data[d.properties.name];
- } else {
- return d.properties.name;
- }
- })
.style("fill", function(d, i) {
if (d.properties.name in data) {
- return colorscale(data[d.properties.name]);
+ return colorscale(data[d.properties.name]['total']);
} else {
return "#fdf6e3";
}
});
- var offsetL = document.getElementById('chart').offsetLet+20;
- var offsetT = document.getElementById('chart').offsetTop+10;
+ var tooltip = d3.select('#chart').append('div')
+ .attr('class', 'tooltip')
country.on("mousemove", function(d, i) {
var mouse = d3.mouse(svg.node()).map(function(d) { return parseInt(d); });
tooltip.classed("hidden", false)
- .attr("style", "left:"+(mouse[0]+offsetL)+"px;top:"+(mouse[1]+offsetT)+"px")
- .html(function(d, i) {
+ .attr("style", "left:"+(mouse[0]+40)+"px;top:"+mouse[1]+"px")
+ .html(function() {
if (d.properties.name in data) {
- return d.properties.name + " " + data[d.properties.name];
+ var text = "" + d.properties.name + "" + "
Total servers: " + data[d.properties.name]['total']
+ if ('bridge' in data[d.properties.name]) {
+ text = text + "
Bridge servers: " + data[d.properties.name]['bridge']
+ }
+ if ('exit' in data[d.properties.name]) {
+ text = text + "
Exit servers: " + data[d.properties.name]['exit']
+ }
+ if ('relay' in data[d.properties.name]) {
+ text = text + "
Relay server: " + data[d.properties.name]['relay']
+ }
+ return text
} else {
- return d.properties.name;
+ return "" + d.properties.name + ""
}
})
})
diff --git a/ennstatus/statistics/views.py b/ennstatus/statistics/views.py
--- a/ennstatus/statistics/views.py
+++ b/ennstatus/statistics/views.py
@@ -14,11 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-from collections import defaultdict
-
-from flask import Blueprint, render_template, current_app, jsonify
-
-from ennstatus.status.functions import split_all_servers_to_types
+from flask import Blueprint, render_template
statistics_page = Blueprint('statistics', __name__)
@@ -26,19 +22,3 @@ statistics_page = Blueprint('statistics'
@statistics_page.route('/worldmap')
def worldmap():
return render_template('statistics/worldmap.html')
-
-
-@statistics_page.route('/data/worldmap')
-def data_worldmap():
- servers = split_all_servers_to_types()
- countries = defaultdict(int)
-
- for key, value in servers.items():
- for server in value:
- countries[server['country']] += 1
-
- maximum = max(countries.values())
-
- countries['max'] = maximum
-
- return jsonify(countries)
diff --git a/ennstatus/templates/base.html b/ennstatus/templates/base.html
--- a/ennstatus/templates/base.html
+++ b/ennstatus/templates/base.html
@@ -108,6 +108,12 @@
Mailing List
+
+ Statistics
+
+
diff --git a/ennstatus/templates/donate/index.html b/ennstatus/templates/donate/index.html
--- a/ennstatus/templates/donate/index.html
+++ b/ennstatus/templates/donate/index.html
@@ -111,63 +111,63 @@
Bitcoin Address: 1EYZCq2ZL6chWXYYkJoDo7fz39UC7do5cC
-
-