filter/search router_list

This commit is contained in:
Dominik Heidler 2015-11-18 14:48:14 +01:00
parent 5b8609102a
commit b5b4ff60fe
10 changed files with 147 additions and 51 deletions

View File

@ -6,6 +6,7 @@ client = MongoClient()
db = client.freifunk
# create db indexes
db.routers.create_index("hostname")
db.routers.create_index("status")
db.routers.create_index("last_contact")
db.routers.create_index("netifs.mac")

View File

@ -8,6 +8,7 @@ from ffmap.web.api import api
from ffmap.web.filters import filters
from ffmap.dbtools import FreifunkDB
from ffmap import stattools
from ffmap.web.helpers import *
from flask import Flask, render_template, request, Response
import bson
@ -36,15 +37,8 @@ def router_map():
@app.route('/routers')
def router_list():
query = {}
for allowed_filter in ('hostname', 'status', 'hood', 'user.nickname', 'hardware.name', 'netifs.mac'):
if allowed_filter in request.args:
query[allowed_filter] = request.args[allowed_filter]
if allowed_filter == 'netifs.mac':
query[allowed_filter] = query[allowed_filter].lower()
if query[allowed_filter] == "EXISTS_NOT":
query[allowed_filter] = {"$exists": False}
return render_template("router_list.html", routers=db.routers.find(query, {
query, query_str = parse_router_list_search_query(request.args)
return render_template("router_list.html", query_str=query_str, routers=db.routers.find(query, {
"hostname": 1,
"status": 1,
"hood": 1,
@ -53,7 +47,7 @@ def router_list():
"created": 1,
"system.uptime": 1,
"system.clients": 1,
}))
}).sort("hostname", pymongo.ASCENDING))
@app.route('/routers/<dbid>')
def router_info(dbid):

View File

@ -35,6 +35,10 @@ def utc2local(dt):
def format_dt(dt):
return dt.strftime("%Y-%m-%d %H:%M:%S")
@filters.app_template_filter('format_dt_date')
def format_dt_date(dt):
return dt.strftime("%Y-%m-%d")
@filters.app_template_filter('dt2jstimestamp')
def dt2jstimestamp(dt):
return int(dt.timestamp())*1000

39
ffmap/web/helpers.py Normal file
View File

@ -0,0 +1,39 @@
#!/usr/bin/python3
import bson
def format_query(query_usr):
query_list = []
for key, value in query_usr.items():
if key == "hostname":
qtag = ""
else:
qtag = "%s:" % key
query_list.append("%s%s" % (qtag, value))
return " ".join(query_list)
allowed_filters = ('status', 'hood', 'user.nickname', 'hardware.name', 'software.firmware', 'netifs.mac', 'hostname')
def parse_router_list_search_query(args):
query_usr = bson.SON()
if "q" in args:
for word in args["q"].strip().split(" "):
if not ':' in word:
key = "hostname"
value = word
else:
key, value = word.split(':')
if key in allowed_filters:
query_usr[key] = query_usr.get(key, "") + value
query = {}
for key, value in query_usr.items():
if value == "EXISTS":
query[key] = {"$exists": True}
elif value == "EXISTS_NOT":
query[key] = {"$exists": False}
elif key == 'netifs.mac':
query[key] = value.lower()
elif key == 'hostname':
query[key] = {"$regex": value, "$options": 'i'}
else:
query[key] = value
return (query, format_query(query_usr))

View File

@ -308,10 +308,15 @@ function global_router_firmwares_graph() {
}
var plot = $.plot(placeholder, pdata, {
legend: {noColumns: 1, show: true, "labelFormatter": legendFormatter},
grid: {hoverable: true, clickable: false},
grid: {hoverable: true, clickable: true},
tooltip: {show: true, content: "<b>%s</b>: %p.0%", shifts: {x: 15, y: 5}, defaultTheme: true},
series: {pie: {show: true, radius: 99/100, label: {show: true, formatter: labelFormatter, radius: 0.5, threshold: 0.10}}}
});
placeholder.bind("plotclick", function(event, pos, obj) {
if (obj) {
window.location.href = routers_page_url + "?q=software.firmware:" + obj.series.label;
}
});
}
function global_router_models_graph() {
@ -324,9 +329,14 @@ function global_router_models_graph() {
});
}
var plot = $.plot(placeholder, pdata, {
legend: {noColumns: 2, show: true, "labelFormatter": legendFormatter},
grid: {hoverable: true, clickable: false},
legend: {noColumns: 1, show: true, "labelFormatter": legendFormatter},
grid: {hoverable: true, clickable: true},
tooltip: {show: true, content: "<b>%s</b>: %p.0%", shifts: {x: 15, y: 5}, defaultTheme: true},
series: {pie: {show: true, radius: 99/100, label: {show: true, formatter: labelFormatter, radius: 0.5, threshold: 0.2}}}
});
placeholder.bind("plotclick", function(event, pos, obj) {
if (obj) {
window.location.href = routers_page_url + "?q=hardware.name:" + obj.series.label;
}
});
}

View File

@ -4,7 +4,7 @@
{% block head %}
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="{% block viewport %}width=device-width, initial-scale=1{% endblock %}">
<meta name="viewport" content="{% block viewport %}width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no{% endblock %}">
<title>{% block title %}FF-Map{% endblock %}</title>
@ -40,6 +40,8 @@
<li class="{{ "active" if request.endpoint in fkt }}"><a href="{{ url_for(fkt[0]) }}">{{ text }}</a></li>
{%- endfor %}
</ul>
{%- block search %}
{%- endblock %}
</div><!--/.nav-collapse -->
</div>
</nav>

View File

@ -1,5 +1,4 @@
{% extends "bootstrap.html" %}
{% block viewport %}{{super()}}, maximum-scale=1.0, user-scalable=no{% endblock %}
{% block title %}{{super()}} Map{% endblock %}
{% block head %}{{super()}}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.3/leaflet.css" />

View File

@ -1,5 +1,4 @@
{% extends "bootstrap.html" %}
{% block viewport %}{{super()}}, maximum-scale=1.0, user-scalable=no{% endblock %}
{% block title %}{{super()}} :: {{ router.hostname }}{% endblock %}
{% block head %}{{super()}}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.3/leaflet.css" />
@ -98,7 +97,7 @@
<tr><th>Hood</th><td>{{ router.hood }}</td></tr>
{%- endif %}
{%- if router.user %}
<tr><th>User</th><td><a href="{{ url_for('router_list') }}?user.nickname={{ router.user.nickname }}">{{ router.user.nickname }}</a></td></tr>
<tr><th>User</th><td><a href="{{ url_for('router_list') }}?q=user.nickname:{{ router.user.nickname }}">{{ router.user.nickname }}</a></td></tr>
{%- endif %}
<tr><th>Hardware</th><td>{{ router.hardware.name }} ({{ router.hardware.chipset }})</td></tr>
<tr><th>WAN Uplink</th><td><span class="{{ "glyphicon glyphicon-ok" if router.system.has_wan_uplink else "glyphicon glyphicon-remove" }}"></span></td></tr>
@ -271,7 +270,7 @@
</div>
<script type="text/javascript">
var router_stats = {{ router.stats|statbson2json|safe }};
$(function() {
$(document).ready(function() {
memory_graph();
process_graph();
client_graph();

View File

@ -1,31 +1,79 @@
{% extends "bootstrap.html" %}
{% block title %}{{super()}} Routers{% endblock %}
{% block head %}{{super()}}
<script src="https://cdn.datatables.net/1.10.10/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.10.10/js/dataTables.bootstrap.min.js"></script>
<!--<script src="https://cdn.datatables.net/responsive/2.0.0/js/dataTables.responsive.min.js"></script>-->
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.10/css/dataTables.bootstrap.min.css">
<!--<link rel="stylesheet" href="https://cdn.datatables.net/responsive/2.0.0/css/responsive.dataTables.min.css">-->
<style type="text/css">
@media(min-width:991px) {
.text-nowrap-responsive {
white-space: nowrap;
}
}
</style>
{% endblock %}
{%- block search %}
<form class="navbar-form navbar-right" role="search">
<div class="input-group">
<input type="text" class="form-control" placeholder="Search routers" name="q" value="{{ query_str }}">
<span class="input-group-btn">
<button type="submit" class="btn btn-default">
<span class="glyphicon glyphicon-search"></span>
</button>
</span>
</div>
</form>
{%- endblock %}
{% block content %}
<table class="table table-condensed table-striped table-bordered" style="margin-bottom: 8px;">
<tr>
<th style="width: 1%;">Hostname</th>
<th style="width: 45px;">Status</th>
<th>Hood</th>
<th>User</th>
<th>Hardware</th>
<th>Created</th>
<th>Uptime</th>
<th>Clients</th>
</tr>
{%- for router in routers %}
<tr>
<td class="text-nowrap"><a href="{{ url_for("router_info", dbid=router._id) }}">{{ router.hostname }}</a></td>
<td class="text-center"><span class="{{ router.status|status2css }}">{{ router.status }}</span></td>
<td>{{ router.hood }}</td>
<td>{{ router.user.nickname if router.user }}</td>
<td>{{ router.get("hardware", {}).get("name", "") }}</td>
<td>{{ router.created|utc2local|format_dt }}</td>
<td>{{ router.system.uptime|format_ts_diff }}</td>
<td>{{ router.system.clients }}</td>
</tr>
{%- endfor %}
</table>
<div class="table-responsive">
<table id="routerlist" class="table table-condensed table-striped table-bordered" style="margin-bottom: 8px;">
<thead>
<tr>
<th style="width: 1%; padding-right: 5px; min-width: 90px;">Hostname</th>
<th style="width: 45px; padding-right: 5px;">Status</th>
<th style="padding-right: 5px;">Hood</th>
<th style="padding-right: 5px;">User</th>
<th style="padding-right: 5px;">Hardware</th>
<th style="padding-right: 5px;">Created</th>
<th style="padding-right: 5px;">Uptime</th>
<th>Clients</th>
</tr>
</thead>
<tbody>
{%- for router in routers %}
<tr>
<td class="text-nowrap-responsive"><a href="{{ url_for("router_info", dbid=router._id) }}">{{ router.hostname }}</a></td>
<td class="text-center"><span class="{{ router.status|status2css }}">{{ router.status }}</span></td>
<td>{{ router.hood }}</td>
<td>{{ router.user.nickname if router.user }}</td>
<td class="text-nowrap">{{ router.get("hardware", {}).get("name", "") }}</td>
<td class="text-nowrap">{{ router.created|utc2local|format_dt_date }}</td>
<td class="text-nowrap">{{ router.system.uptime|format_ts_diff }}</td>
<td>{{ router.system.clients }}</td>
</tr>
{%- endfor %}
</tbody>
</table>
</div>
<div style="margin-bottom: 20px;">
{{ routers.count() }} Router{{ "s" if (routers.count() > 1) else "" }} found.
</div>
<script type="text/javascript">
$(document).ready(function() {
$("#routerlist").DataTable({
"paging": false,
"info": false,
"searching": false,
/*"responsive": {
"details": false
},*/
"columnDefs": [
{"orderable": false, "targets": 1},
{"orderable": false, "targets": -2},
]
});
});
</script>
{% endblock %}

View File

@ -1,5 +1,4 @@
{% extends "bootstrap.html" %}
{% block viewport %}{{super()}}, maximum-scale=1.0, user-scalable=no{% endblock %}
{% block title %}{{super()}} Statistics{% endblock %}
{% block head %}{{super()}}
<script src="{{ url_for('static', filename='js/graph/date.js') }}"></script>
@ -52,13 +51,13 @@
<th class="active" title="Total Routers">Total</th>
<th class="info">Clients</th>
</tr>
{%- for hood in hoods %}
{%- for hood, value in hoods|dictsort %}
<tr>
<td>{{ hood }}</td>
<td class="success">{{ hoods[hood]["online"] or 0 }}</td>
<td class="danger">{{ hoods[hood]["offline"] or 0 }}</td>
<td class="warning">{{ hoods[hood]["unknown"] or 0 }}</td>
<td class="active">{{ hoods_sum[hood]["routers"] or 0 }}</td>
<td class="success">{{ value["online"] or 0 }}</td>
<td class="danger">{{ value["offline"] or 0 }}</td>
<td class="warning">{{ value["unknown"] or 0 }}</td>
<td class="active">{{ value["routers"] or 0 }}</td>
<td class="info">{{ hoods_sum[hood]["clients"] or 0 }}</td>
</tr>
{%- endfor %}
@ -134,9 +133,10 @@
</div>
<script type="text/javascript">
var global_stats = {{ stats|statbson2json|safe }};
var router_firmwares = {{ router_firmwares|tojson }}
var router_models = {{ router_models|tojson }}
$(function() {
var router_firmwares = {{ router_firmwares|tojson }};
var router_models = {{ router_models|tojson }};
var routers_page_url = "{{ url_for('router_list') }}";
$(document).ready(function() {
global_client_graph();
global_router_graph();
global_router_firmwares_graph();