new: Add support for multi-country graphs
parent
0398d2f2b9
commit
b8ad28b1f4
|
@ -179,9 +179,9 @@ class Querying():
|
||||||
daily_sum = sum(ranks)
|
daily_sum = sum(ranks)
|
||||||
return daily_sum, to_return
|
return daily_sum, to_return
|
||||||
|
|
||||||
def country_history(self, country: str, period: int=30, source: Union[list, str]='',
|
def country_history(self, country: Union[list, str], period: int=30, source: Union[list, str]='',
|
||||||
ipversion: str='v4', date: Dates=datetime.date.today()):
|
ipversion: str='v4', date: Dates=datetime.date.today()):
|
||||||
to_return = []
|
to_return = {}
|
||||||
|
|
||||||
if isinstance(date, str):
|
if isinstance(date, str):
|
||||||
date = parse(date).date()
|
date = parse(date).date()
|
||||||
|
@ -189,10 +189,14 @@ class Querying():
|
||||||
# the period to display will be around the date passed at least 2/3 before the date, at most 1/3 after
|
# the period to display will be around the date passed at least 2/3 before the date, at most 1/3 after
|
||||||
date = datetime.date.today()
|
date = datetime.date.today()
|
||||||
|
|
||||||
for i in range(period):
|
if isinstance(country, str):
|
||||||
d = date - timedelta(days=i)
|
country = [country]
|
||||||
rank, details = self.country_rank(country, d, source, ipversion)
|
for c in country:
|
||||||
if rank is None:
|
to_return[c] = []
|
||||||
rank = 0
|
for i in range(period):
|
||||||
to_return.insert(0, (d.isoformat(), rank, list(details)))
|
d = date - timedelta(days=i)
|
||||||
|
rank, details = self.country_rank(c, d, source, ipversion)
|
||||||
|
if rank is None:
|
||||||
|
rank = 0
|
||||||
|
to_return[c].insert(0, (d.isoformat(), rank, list(details)))
|
||||||
return to_return
|
return to_return
|
||||||
|
|
|
@ -47,7 +47,10 @@ def load_session():
|
||||||
session['asn'] = d['asn']
|
session['asn'] = d['asn']
|
||||||
session.pop('country', None)
|
session.pop('country', None)
|
||||||
elif 'country' in d:
|
elif 'country' in d:
|
||||||
session['country'] = d.getlist('country')
|
if '_all' in d.getlist('country'):
|
||||||
|
session.pop('country', None)
|
||||||
|
else:
|
||||||
|
session['country'] = d.getlist('country')
|
||||||
session.pop('asn', None)
|
session.pop('asn', None)
|
||||||
set_default_date_session()
|
set_default_date_session()
|
||||||
|
|
||||||
|
@ -106,22 +109,24 @@ def country_history_callback():
|
||||||
mapping = defaultdict(dict)
|
mapping = defaultdict(dict)
|
||||||
dates = []
|
dates = []
|
||||||
all_asns = set([])
|
all_asns = set([])
|
||||||
for d, r_sum, details in history_data:
|
for country, data in history_data.items():
|
||||||
dates.append(d)
|
for d, r_sum, details in data:
|
||||||
for detail in details:
|
dates.append(d)
|
||||||
asn, r = detail
|
for detail in details:
|
||||||
all_asns.add(asn)
|
asn, r = detail
|
||||||
mapping[asn][d] = r
|
all_asns.add(asn)
|
||||||
|
mapping[asn][d] = r
|
||||||
|
|
||||||
to_display = [[''] + dates]
|
to_display_temp = [[country] + dates]
|
||||||
for a in sorted(list(all_asns), key=int):
|
for a in sorted(list(all_asns), key=int):
|
||||||
line = [a]
|
line = [a]
|
||||||
for d in dates:
|
for d in dates:
|
||||||
if mapping[a].get(d) is not None:
|
if mapping[a].get(d) is not None:
|
||||||
line.append(round(mapping[a].get(d), 3))
|
line.append(round(mapping[a].get(d), 3))
|
||||||
else:
|
else:
|
||||||
line.append('N/A')
|
line.append('N/A')
|
||||||
to_display.append(line)
|
to_display_temp.append(line)
|
||||||
|
to_display.append(to_display_temp)
|
||||||
return json.dumps(render_template('country_asn_map.html', to_display=to_display))
|
return json.dumps(render_template('country_asn_map.html', to_display=to_display))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -36,14 +36,6 @@ function linegraph(call_path) {
|
||||||
context.lineWidth = 1.5;
|
context.lineWidth = 1.5;
|
||||||
context.strokeStyle = "steelblue";
|
context.strokeStyle = "steelblue";
|
||||||
context.stroke();
|
context.stroke();
|
||||||
d3.json(call_path + '_callback',
|
|
||||||
{credentials: 'same-origin',
|
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
// headers: {'Content-Type': 'application/json'}
|
|
||||||
}).then(function(data) {
|
|
||||||
d3.select('#asn_details').html(data);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function xAxis() {
|
function xAxis() {
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
function linegraph(call_path) {
|
||||||
|
var canvas = document.querySelector("canvas"),
|
||||||
|
context = canvas.getContext("2d");
|
||||||
|
|
||||||
|
// set the dimensions and margins of the graph
|
||||||
|
var margin = {top: 20, right: 20, bottom: 30, left: 50},
|
||||||
|
width = canvas.width - margin.left - margin.right,
|
||||||
|
height = canvas.height - margin.top - margin.bottom;
|
||||||
|
|
||||||
|
// parse the date / time
|
||||||
|
var parseTime = d3.timeParse("%Y-%m-%d");
|
||||||
|
|
||||||
|
// set the ranges
|
||||||
|
var x = d3.scaleTime().range([0, width]);
|
||||||
|
var y = d3.scaleLinear().range([height, 0]);
|
||||||
|
var z = d3.scaleOrdinal(d3.schemeCategory10);
|
||||||
|
|
||||||
|
// define the line
|
||||||
|
var line = d3.line()
|
||||||
|
.x(function(d) { return x(parseTime(d[0])); })
|
||||||
|
.y(function(d) { return y(d[1]); })
|
||||||
|
.curve(d3.curveStep)
|
||||||
|
.context(context);
|
||||||
|
|
||||||
|
context.translate(margin.left, margin.top);
|
||||||
|
|
||||||
|
// Get the data
|
||||||
|
d3.json(call_path, {credentials: 'same-origin'}).then(function(data) {
|
||||||
|
var color = d3.scaleOrdinal(d3.schemeCategory10);
|
||||||
|
var i = 0;
|
||||||
|
for (country in data) {
|
||||||
|
var country_data = data[country]
|
||||||
|
x.domain(d3.extent(country_data, function(d) { return parseTime(d[0]); }));
|
||||||
|
y.domain(d3.extent(country_data, function(d) { return d[1]; }));
|
||||||
|
|
||||||
|
context.beginPath();
|
||||||
|
line(country_data);
|
||||||
|
context.lineWidth = 1.5;
|
||||||
|
context.strokeStyle = color(i);
|
||||||
|
context.stroke();
|
||||||
|
i += 1;
|
||||||
|
};
|
||||||
|
xAxis();
|
||||||
|
yAxis();
|
||||||
|
d3.json(call_path + '_callback',
|
||||||
|
{credentials: 'same-origin',
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
// headers: {'Content-Type': 'application/json'}
|
||||||
|
}).then(function(data) {
|
||||||
|
d3.select('#asn_details').html(data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function xAxis() {
|
||||||
|
var tickCount = 10,
|
||||||
|
tickSize = .1,
|
||||||
|
ticks = x.ticks(tickCount),
|
||||||
|
tickFormat = x.tickFormat();
|
||||||
|
|
||||||
|
context.beginPath();
|
||||||
|
ticks.forEach(function(d) {
|
||||||
|
context.moveTo(x(d), height);
|
||||||
|
context.lineTo(x(d), height + tickSize);
|
||||||
|
});
|
||||||
|
context.strokeStyle = "black";
|
||||||
|
context.stroke();
|
||||||
|
|
||||||
|
context.textAlign = "center";
|
||||||
|
context.textBaseline = "top";
|
||||||
|
ticks.forEach(function(d) {
|
||||||
|
context.fillText(tickFormat(d), x(d), height + tickSize);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function yAxis() {
|
||||||
|
var tickCount = 20,
|
||||||
|
tickSize = 1,
|
||||||
|
tickPadding = 1,
|
||||||
|
ticks = y.ticks(tickCount),
|
||||||
|
tickFormat = y.tickFormat(tickCount);
|
||||||
|
|
||||||
|
context.beginPath();
|
||||||
|
ticks.forEach(function(d) {
|
||||||
|
context.moveTo(0, y(d));
|
||||||
|
context.lineTo(-6, y(d));
|
||||||
|
});
|
||||||
|
context.strokeStyle = "black";
|
||||||
|
context.stroke();
|
||||||
|
|
||||||
|
context.beginPath();
|
||||||
|
context.moveTo(-tickSize, 0);
|
||||||
|
context.lineTo(0.5, 0);
|
||||||
|
context.lineTo(0.5, height);
|
||||||
|
context.lineTo(-tickSize, height);
|
||||||
|
context.strokeStyle = "black";
|
||||||
|
context.stroke();
|
||||||
|
|
||||||
|
context.textAlign = "right";
|
||||||
|
context.textBaseline = "middle";
|
||||||
|
ticks.forEach(function(d) {
|
||||||
|
context.fillText(tickFormat(d), -tickSize - tickPadding, y(d));
|
||||||
|
});
|
||||||
|
|
||||||
|
context.save();
|
||||||
|
context.rotate(-Math.PI / 2);
|
||||||
|
context.textAlign = "right";
|
||||||
|
context.textBaseline = "top";
|
||||||
|
context.font = "bold 10px sans-serif";
|
||||||
|
context.fillText("Rank", -10, 10);
|
||||||
|
context.restore();
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,17 +5,17 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block title %}Ranking - {{ country }}{% endblock %}
|
{% block title %}Ranking - {{ ' '.join(country) }}{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
<script src='{{ url_for('static', filename='linegraph.js') }}'></script>
|
<script src='{{ url_for('static', filename='linegraph_country.js') }}'></script>
|
||||||
<script>linegraph('/country_history');</script>
|
<script>linegraph('/country_history');</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<center>
|
<center>
|
||||||
<h1>Ranking - {{country}}</h1></br></br>
|
<h1>Ranking - {{ ' '.join(country) }}</h1></br></br>
|
||||||
</center>
|
</center>
|
||||||
{% include ['top_forms.html'] %}
|
{% include ['top_forms.html'] %}
|
||||||
<canvas width="1024" height="800"></canvas>
|
<canvas width="1024" height="800"></canvas>
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
{% for to_display_country in to_display%}
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<tr>
|
<tr>
|
||||||
{% for date in to_display[0] %}
|
{% for date in to_display_country[0] %}
|
||||||
<td>{{ date }}</td>
|
<td>{{ date }}</td>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
{% for line in to_display[1:] %}
|
{% for line in to_display_country[1:] %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{{ url_for('asn_details', asn=line[0]) }}">{{ line[0] }}</a></td>
|
<td><a href="{{ url_for('asn_details', asn=line[0]) }}">{{ line[0] }}</a></td>
|
||||||
{% for rank in line[1:] %}
|
{% for rank in line[1:] %}
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,10 @@
|
||||||
<button type="submit" class="btn btn-primary my-1">IP version</button>
|
<button type="submit" class="btn btn-primary my-1">IP version</button>
|
||||||
</form>
|
</form>
|
||||||
<form class="form-inline" style="width:400px; display:inline-block;" action="" method=post>
|
<form class="form-inline" style="width:400px; display:inline-block;" action="" method=post>
|
||||||
<select name="source" class="selectpicker" multiple>
|
<select name="source" class="selectpicker" data-live-search="true" multiple>
|
||||||
<option value="_all" {% if not source %} selected="selected"{% endif %}>all</option>
|
<option value="_all" data-tokens="all" {% if not source %} selected="selected"{% endif %}>all</option>
|
||||||
{% for s in sources %}
|
{% for s in sources %}
|
||||||
<option value="{{ s }}" {% if s in source %} selected="selected"{% endif %}>{{ s }}</option>
|
<option value="{{ s }}" data-tokens="{{ s }}" {% if s in source %} selected="selected"{% endif %}>{{ s }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<input type="submit" class="btn btn-primary my-1"value="Set source">
|
<input type="submit" class="btn btn-primary my-1"value="Set source">
|
||||||
|
@ -25,9 +25,9 @@
|
||||||
</form>
|
</form>
|
||||||
<form class="form-inline"style="width:500px; display:inline-block;" action="{{ url_for('country') }}" method=post>
|
<form class="form-inline"style="width:500px; display:inline-block;" action="{{ url_for('country') }}" method=post>
|
||||||
<select name="country" class="selectpicker" multiple>
|
<select name="country" class="selectpicker" multiple>
|
||||||
<option value="" {% if not country %} selected="selected"{% endif %}>all</option>
|
<option value="_all" data-tokens="all" {% if not country %} selected="selected"{% endif %}>all</option>
|
||||||
{% for cc, name in countries %}
|
{% for cc, name in countries %}
|
||||||
<option value="{{ cc }}" {% if country == cc %} selected="selected"{% endif %}>{{ name }}</option>
|
<option value="{{ cc }}" data-tokens="{{ cc }} {{ name }}" {% if cc in country %} selected="selected"{% endif %}>{{ name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<input type="submit" class="btn btn-primary my-1"value="Set country">
|
<input type="submit" class="btn btn-primary my-1"value="Set country">
|
||||||
|
|
Loading…
Reference in New Issue