diff --git a/ennstatus/stats/forms.py b/ennstatus/stats/forms.py new file mode 100644 --- /dev/null +++ b/ennstatus/stats/forms.py @@ -0,0 +1,36 @@ +from flask_wtf import Form +from wtforms import SelectField +from wtforms.validators import DataRequired + +STYLES = [ + ('default', 'Default'), + ('light', 'Light'), + ('neon', 'Neon'), + ('light_red_blue', 'Red Blue'), + ('dark_solarized', 'Dark Solarized'), + ('light_solarized', 'Light Solarized'), + ('dark_colorized', 'Dark Colorized'), + ('light_colorized', 'Light Colorized'), + ('turquoise', 'Turquoise'), + ('green', 'Light green'), + ('dark_green', 'Dark green'), + ('dark_green_blue', 'Dark green blue'), + ('blue', 'Blue'), + ('solid_color', 'Solid Color'), +] + +SERVER_TYPES = [ + ('all', 'All'), + ('exit', 'Exit'), + ('relay', 'Relay'), + ('bridge', 'Bridge'), +] + + +class WorldmapStyleForm(Form): + style = SelectField('Style', + validators=[DataRequired()], + choices=STYLES) + server_type = SelectField('Server type', + validators=[DataRequired()], + choices=SERVER_TYPES) diff --git a/ennstatus/stats/functions.py b/ennstatus/stats/functions.py new file mode 100644 --- /dev/null +++ b/ennstatus/stats/functions.py @@ -0,0 +1,54 @@ +from collections import Counter, defaultdict + +from flask import current_app + +import pygal + + +from ennstatus.status.functions import split_all_servers_to_types + +COUNTRIES_TO_ISO = dict( + zip(pygal.i18n.COUNTRIES.values(), pygal.i18n.COUNTRIES.keys())) +COUNTRIES_TO_ISO['Isle of Man'] = 'gb' + +def _make_country_list(): + + servers = split_all_servers_to_types() + countries = defaultdict(list) + + for key, value in servers.items(): + for server in value: + country = COUNTRIES_TO_ISO[server['country']] + countries[key].append(country) + + return countries + + +def make_worldmap(server_type, style): + + arguments = { + 'title': '%s nodes' % server_type.capitalize(), + 'style': pygal.style.styles[style], + 'legend_at_bottom': True, + 'disable_xml_declaration': True, + 'pretty_print': True, + } + + countries = _make_country_list() + + exits_count = Counter(countries['Exit']) + relays_count = Counter(countries['Relay']) + bridges_count = Counter(countries['Bridge']) + + plot = pygal.Worldmap(**arguments) + + if server_type in ('all', 'exit'): + plot.add('Exits', exits_count) + + if server_type in ('all', 'relay'): + plot.add('Relays', relays_count) + + if server_type in ('all', 'bridge'): + plot.add('Bridges', bridges_count) + + return plot diff --git a/ennstatus/stats/views.py b/ennstatus/stats/views.py new file mode 100644 --- /dev/null +++ b/ennstatus/stats/views.py @@ -0,0 +1,62 @@ +from flask import (Blueprint, render_template, request, current_app, + redirect, url_for) + +from ennstatus.stats.functions import (make_worldmap, make_total_pie, + make_type_pie) +from ennstatus.stats.forms import (WorldmapStyleForm) + +stats_page = Blueprint('stats', __name__) + + +@stats_page.route('/') +def index(): + return render_template('stats/index.html') + + +@stats_page.route('/worldmap', methods=('GET', 'POST')) +def worldmap(): + + current_app.logger.info('Handling worldmap') + form = WorldmapStyleForm() + style_choices = [choice[0] for choice in form.style.choices] + server_choices = [choice[0] for choice in form.server_type.choices] + + if request.method == 'POST': + current_app.logger.debug('Validating form') + if form.validate_on_submit(): + style = form.style.data + server_type = form.server_type.data + return redirect(url_for('stats.worldmap', server_type=server_type, + style=style)) + else: + if 'style' in request.args: + style = request.args['style'] + if style in style_choices: + current_app.logger.info('Using style %s' % style) + else: + current_app.logger.warn('Style %s not found' % style) + style = 'default' + else: + current_app.logger.info('Using default style') + style = 'default' + + if 'server_type' in request.args: + server_type = request.args['server_type'] + if server_type in server_choices: + current_app.logger.info('Showing %s nodes' % server_type) + else: + current_app.logger.warn('Server type %s not found' % style) + server_type = 'all' + else: + current_app.logger.info('Showing all servers') + server_type = 'all' + + + form.style.data = style + form.server_type.data = server_type + + plot = make_worldmap(server_type, style) + + return render_template('stats/worldmap.html', plot=plot, form=form) + + diff --git a/ennstatus/templates/stats/base.html b/ennstatus/templates/stats/base.html new file mode 100644 --- /dev/null +++ b/ennstatus/templates/stats/base.html @@ -0,0 +1,7 @@ +{% extends "base.html" %} + +{% block scripts %} + {{ super() }} + + +{% endblock %} diff --git a/ennstatus/templates/stats/worldmap.html b/ennstatus/templates/stats/worldmap.html new file mode 100644 --- /dev/null +++ b/ennstatus/templates/stats/worldmap.html @@ -0,0 +1,26 @@ +{% extends "stats/base.html" %} + +{% set title = "Worldmap" %} + +{% block content %} +
+
+
+ {{ form.hidden_tag() }} +
+ {{ form.style.label}} + {{ form.style(_class="form-control") }} + {{ form.server_type.label }} + {{ form.server_type(_class="form-control") }} +
+ +
+
+
+
+
+
+ {{ plot.render()|safe }} +
+
+{% endblock %} diff --git a/requirements.in b/requirements.in --- a/requirements.in +++ b/requirements.in @@ -5,5 +5,5 @@ Flask-Mail==0.9.0 #Flask-SSLify==0.1.4 Flask==0.10.1 pygeoip==0.3.0 -#pygal==1.2.1 +pygal==1.2.1 #humanize==0.5