Revamp fact pages and tables to datatables

This commit is contained in:
redref
2017-02-05 18:55:19 +01:00
parent f13100664a
commit a21bd0ac1d
5 changed files with 152 additions and 74 deletions

View File

@@ -412,11 +412,10 @@ def node(env, node_name):
query.add(EqualsOperator("certname", node_name))
node = get_or_abort(puppetdb.node, node_name)
facts = node.facts()
return render_template(
'node.html',
node=node,
facts=yield_or_stop(facts),
envs=envs,
current_env=env,
columns=REPORTS_COLUMNS[:2])
@@ -661,73 +660,102 @@ def facts(env):
current_env=env)
@app.route('/fact/<fact>', defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
@app.route('/<env>/fact/<fact>')
def fact(env, fact):
"""Fetches the specific fact from PuppetDB and displays its value per
@app.route('/fact/<fact>',
defaults={'env': app.config['DEFAULT_ENVIRONMENT'], 'value': None})
@app.route('/<env>/fact/<fact>', defaults={'value': None})
@app.route('/fact/<fact>/<value>',
defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
@app.route('/<env>/fact/<fact>/<value>')
def fact(env, fact, value):
"""Fetches the specific fact(/value) from PuppetDB and displays per
node for which this fact is known.
:param env: Searches for facts in this environment
:type env: :obj:`string`
:param fact: Find all facts with this name
:type fact: :obj:`string`
:param fact: Find all facts with this value
:type fact: :obj:`string`
"""
envs = environments()
check_env(env, envs)
# we can only consume the generator once, lists can be doubly consumed
# om nom nom
render_graph = False
if fact in graph_facts:
if fact in graph_facts and not value:
render_graph = True
if env == '*':
query = None
else:
query = EqualsOperator("environment", env)
localfacts = [f for f in yield_or_stop(puppetdb.facts(
name=fact, query=query))]
return Response(stream_with_context(stream_template(
return render_template(
'fact.html',
name=fact,
fact=fact,
value=value,
render_graph=render_graph,
facts=localfacts,
envs=envs,
current_env=env)))
current_env=env)
@app.route('/fact/<fact>/<value>',
defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
@app.route('/<env>/fact/<fact>/<value>')
def fact_value(env, fact, value):
"""On asking for fact/value get all nodes with that fact.
@app.route('/fact/<fact>/json',
defaults={'env': app.config['DEFAULT_ENVIRONMENT'],
'node': None, 'value': None})
@app.route('/<env>/fact/<fact>/json', defaults={'node': None, 'value': None})
@app.route('/fact/<fact>/<value>/json',
defaults={'env': app.config['DEFAULT_ENVIRONMENT'], 'node': None})
@app.route('/<env>/fact/<fact>/<value>/json', defaults={'node': None})
@app.route('/node/<node>/facts/json',
defaults={'env': app.config['DEFAULT_ENVIRONMENT'],
'fact': None, 'value': None})
@app.route('/<env>/node/<node>/facts/json',
defaults={'fact': None, 'value': None})
def fact_ajax(env, node, fact, value):
"""Fetches the specific facts matching (node/fact/value) from PuppetDB and
return a JSON table
:param env: Searches for facts in this environment
:type env: :obj:`string`
:param fact: Find all facts for this node
:type fact: :obj:`string`
:param fact: Find all facts with this name
:type fact: :obj:`string`
:param value: Filter facts whose value is equal to this
:type value: :obj:`string`
:param fact: Find all facts with this value
:type fact: :obj:`string`
"""
draw = int(request.args.get('draw', 0))
envs = environments()
check_env(env, envs)
if env == '*':
query = None
else:
query = EqualsOperator("environment", env)
render_graph = False
if fact in graph_facts and not value and not node:
render_graph = True
facts = get_or_abort(puppetdb.facts,
name=fact,
value=value,
query=query)
localfacts = [f for f in yield_or_stop(facts)]
return render_template(
'fact.html',
query = AndOperator()
if node:
query.add(EqualsOperator("certname", node))
if env != '*':
query.add(EqualsOperator("environment", env))
if len(query.operations) == 0:
query = None
# Generator needs to be converted (graph / total)
facts = [f for f in get_or_abort(
puppetdb.facts,
name=fact,
value=value,
facts=localfacts,
query=query)]
total = len(facts)
return render_template(
'fact.json.tpl',
fact=fact,
node=node,
value=value,
draw=draw,
total=total,
total_filtered=total,
render_graph=render_graph,
facts=facts,
envs=envs,
current_env=env)

View File

@@ -76,6 +76,8 @@
// Paging options
"lengthMenu": {{ length_selector }},
"pageLength": {{ default_length }},
// Search as regex (does not apply if serverSide)
"search": {"regex": true},
// Default sort
"order": [[ 0, "desc" ]],
// Custom options

View File

@@ -1,50 +1,45 @@
{% extends 'layout.html' %}
{% import '_macros.html' as macros %}
{% block javascript %}
{% if render_graph %}
var chart = null;
var data = [
{% for fact in facts|groupby('value') %}
{
label: '{{ fact.grouper.replace("\n", " ") }}',
value: {{ fact.list|length }}
},
{% endfor %}
{
value: 0,
}
]
var fact_values = data.map(function(item) { return [item.label, item.value]; }).filter(function(item){return item[0];}).sort(function(a,b){return b[1] - a[1];});
var realdata = fact_values.slice(0, 15);
var otherdata = fact_values.slice(15);
if (otherdata.length > 0) {
realdata.push(["other", otherdata.reduce(function(a,b){return a + b[1];},0)]);
}
{% endif %}
{% endblock javascript %}
{% block onload_script %}
$('table').tablesort();
{% if render_graph %}
chart = c3.generate({
{% macro extra_options(caller) %}
// No per page AJAX
'serverSide': false,
{% endmacro %}
{{ macros.datatable_init(table_html_id="facts_table", ajax_url=url_for('fact_ajax', env=current_env, fact=fact, value=value), default_length=config.NORMAL_TABLE_COUNT, length_selector=config.TABLE_COUNT_SELECTOR, extra_options=extra_options) }}
{% if render_graph %}
table.on('xhr', function(e, settings, json){
var fact_values = json['chart'].map(function(item) { return [item.label, item.value]; }).filter(function(item){return item[0];}).sort(function(a,b){return b[1] - a[1];});
var realdata = fact_values.slice(0, 15);
var otherdata = fact_values.slice(15);
if (otherdata.length > 0) {
realdata.push(["other", otherdata.reduce(function(a,b){return a + b[1];},0)]);
}
c3.generate({
bindto: '#factChart',
data: {
columns: realdata,
type : '{{config.GRAPH_TYPE|default('pie')}}',
}
});
{% endif %}
})
{% endif %}
{% endblock onload_script %}
{% block content %}
{% if render_graph %}
<div id="factChart" width="300" height="300"></div>
<h1>{{name}}{% if value %}/{{value}}{% endif %} ({{facts|length}})</h1>
{% if value %}
{{macros.facts_table(facts, current_env=current_env, autofocus=True, show_node=True, show_value=False, margin_bottom=10)}}
{% else %}
{{macros.facts_table(facts, current_env=current_env, autofocus=True, show_node=True, link_facts=True, margin_bottom=10)}}
{% endif %}
<h1>{{ fact }}{% if value %}/{{ value }}{% endif %}</h1>
<table id="facts_table" class='ui fixed very basic compact table stackable'>
<thead>
<tr>
<th>Node</th>
{% if not value %}<th>Value</th>{% endif %}
</tr>
</thead>
<tbody>
</tbody>
</table>
{% endblock content %}

View File

@@ -0,0 +1,38 @@
{
"draw": {{draw}},
"recordsTotal": {{total}},
"recordsFiltered": {{total_filtered}},
"data": [
{% for fact_h in facts -%}
{%- if not loop.first %},{%- endif -%}
[
{%- if not fact -%}
{{ fact_h.name | jsonprint }}
{%- if node or value %},{% endif -%}
{%- endif -%}
{%- if not node -%}
{% filter jsonprint %}<a href="{{ url_for('node', env=current_env, node_name=fact_h.node) }}">{{ fact_h.node }}</a>{% endfilter %}
{%- if not value %},{% endif -%}
{%- endif -%}
{%- if not value -%}
{%- if fact_h.value is mapping -%}
{% filter jsonprint %}<a href="{{ url_for('fact', env=current_env, fact=fact_h.name, value=fact_h.value) }}"><pre>{{ fact_h.value | jsonprint }}</pre></a>{% endfilter %}
{%- else -%}
{% filter jsonprint %}<a href="{{ url_for('fact', env=current_env, fact=fact_h.name, value=fact_h.value) }}"><pre>{{ fact_h.value }}</pre></a>{% endfilter %}
{%- endif -%}
{%- endif -%}
]
{% endfor -%}
]
{%- if render_graph %},
"chart": [
{% for fact_h in facts | groupby('value') -%}
{%- if not loop.first %},{%- endif -%}
{
"label": {{ fact_h.grouper.replace("\n", " ") | jsonprint }},
"value": {{ fact_h.list|length }}
}
{% endfor %}
]
{% endif %}
}

View File

@@ -17,7 +17,13 @@
'pagingType': 'simple',
"bFilter": false,
{% endmacro %}
{% macro facts_extra_options(caller) %}
'paging': false,
// No per page AJAX
'serverSide': false,
{% endmacro %}
{{ macros.datatable_init(table_html_id="reports_table", ajax_url=url_for('reports_ajax', env=current_env, node_name=node.name), default_length=config.LITTLE_TABLE_COUNT, length_selector=config.TABLE_COUNT_SELECTOR, extra_options=extra_options) }}
{{ macros.datatable_init(table_html_id="facts_table", ajax_url=url_for('fact_ajax', env=current_env, node=node.name), default_length=config.LITTLE_TABLE_COUNT, length_selector=config.TABLE_COUNT_SELECTOR, extra_options=facts_extra_options) }}
{% endblock onload_script %}
{% block content %}
@@ -69,7 +75,16 @@
</div>
<div class='column'>
<h1>Facts</h1>
{{macros.facts_table(facts, link_facts=True, condensed=True, current_env=current_env)}}
<table id="facts_table" class='ui fixed very basic very compact table stackable'>
<thead>
<tr>
<th>Name</th>
<th>Value</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
{% endblock content %}