diff --git a/ennstatus/cli/commands/stats.py b/ennstatus/cli/commands/stats.py new file mode 100644 --- /dev/null +++ b/ennstatus/cli/commands/stats.py @@ -0,0 +1,224 @@ +import json + +from collections import defaultdict + +import click + +from ennstatus import create_app +from ...status.functions import split_all_servers_to_types + + +@click.group(short_help='Get statistics') +def stats(): + pass + + +@stats.command('count') +@click.option('--by-type', 'by_type', is_flag=True, default=False) +@click.pass_obj +def count(obj, by_type): + + def calculate_host_number(config, type='all', servers=None): + + hosts = set() + if type == 'all': + + for values in config['ENNSTATUS_SERVERS'].values(): + ips = frozenset(values['IPS']) + hosts.add(ips) + + return len(hosts) + else: + for server in servers[servertype]: + ips = frozenset( + config['ENNSTATUS_SERVERS'][server.name.lower()]['IPS'] + ) + hosts.add(ips) + return len(hosts) + + app = create_app() + + with app.app_context(): + app.logger.disabled = True + servers = split_all_servers_to_types() + + if not by_type: + + click.echo( + 'We have %s servers in total!' % ( + click.style( + str(sum(len(x) for x in servers.values())), + fg='blue' + ) + ) + ) + + click.echo( + 'We have %s different hosts!' % ( + click.style( + str(calculate_host_number(app.config)), + fg='blue' + ) + ) + ) + else: + for servertype, server in servers.items(): + click.echo( + 'We have %s %s servers!' % ( + click.style( + str(len(server)), + fg='blue' + ), + click.style( + servertype, + fg='red' + ) + ) + ) + click.echo( + 'We have %s different %s hosts!' % ( + click.style( + str( + calculate_host_number( + app.config, + type=servertype, + servers=servers + ) + ), + fg='blue' + ), + click.style( + servertype, + fg='red' + ) + ) + ) + + +@stats.command('countries') +@click.option('--by-type', 'by_type', is_flag=True, default=False) +@click.pass_obj +def countries(obj, by_type): + app = create_app() + + with app.app_context(): + app.logger.disabled = True + servers = split_all_servers_to_types() + + if not by_type: + countries = defaultdict(int) + + for key, value in servers.items(): + for server in value: + countries[server.country] += 1 + + for key, value in sorted(countries.items(), key=lambda x: x[1]): + click.echo( + '%s: %s' % ( + click.style(key, fg='green'), + click.style(str(value), fg='blue') + ) + ) + + click.echo( + 'We are hosted in %s different countries' % click.style( + str(len(countries.keys())), + fg='blue' + ) + ) + else: + type_countries = { + 'exit': defaultdict(int), + 'relay': defaultdict(int), + 'bridge': defaultdict(int) + } + + for key, value in servers.items(): + for server in value: + type_countries[key][server.country] += 1 + + type_countries = dict((k, v) for k, v in type_countries.items() if v) + + for key, value in sorted(type_countries.items(), key=lambda x: x[0]): + click.echo( + click.style( + key.capitalize(), + fg='red', + bold=True, + underline=True + ) + ) + + for country, count in sorted(type_countries[key].items(), key=lambda x: x[1]): + click.echo( + '%s: %s' % ( + click.style(country, fg='green'), + click.style(str(count), fg='blue') + ) + ) + + click.echo( + '%s are hosted in %s different countries' % ( + key, + click.style( + str(len(type_countries[key].keys())), + fg='blue' + ) + ) + ) + + +@stats.command('exit_probability') +@click.option('--by-server', 'by_server', is_flag=True, default=False) +@click.pass_obj +def exit_probability(obj, by_server): + + app = create_app() + with app.app_context(): + app.logger.disabled = True + servers = split_all_servers_to_types() + + if not by_server: + exit_probability = defaultdict(int) + for server in servers['exit']: + for subkey in ('1_week', '1_month', '3_months', '1_year', '5_years'): + if server.mean_exit_probability[subkey] is not None: + exit_probability[subkey] += server.mean_exit_probability[subkey] + + for subkey in ('1_week', '1_month', '3_months', '1_year', '5_years'): + click.echo( + 'Mean exit probability over %s: %s' % ( + click.style( + subkey, + fg='blue' + ), + click.style( + str(round(exit_probability[subkey], 2)) + '%', + fg='red' + ) + ) + ) + else: + for server in servers['exit']: + click.echo( + click.style( + server.name.capitalize(), + fg='red', + bold=True, + underline=True + ) + ) + for subkey in ('1_week', '1_month', '3_months', '1_year', '5_years'): + if server.mean_exit_probability[subkey] is not None: + click.echo( + 'Mean exit probabilty over %s: %s' % ( + click.style( + subkey, + fg='blue' + ), + click.style( + str(round(server.mean_exit_probability[subkey], 2)) + "%", + fg='red' + ) + ) + )