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 @@ -70,44 +70,54 @@ 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)).range([ + "#f2f0f7", + "#dadaeb", + "#bcbddc", + "#9e9ac8", + "#756bb1", + "#54278f" + ]) 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
  • + diff --git a/ennstatus/templates/statistics/worldmap.html b/ennstatus/templates/statistics/worldmap.html --- a/ennstatus/templates/statistics/worldmap.html +++ b/ennstatus/templates/statistics/worldmap.html @@ -26,6 +26,7 @@ {% block content %}
    +

    Worldmap