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

View File

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

View File

@@ -1,50 +1,45 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% import '_macros.html' as macros %} {% 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 %} {% block onload_script %}
$('table').tablesort(); {% macro extra_options(caller) %}
{% if render_graph %} // No per page AJAX
chart = c3.generate({ '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', bindto: '#factChart',
data: { data: {
columns: realdata, columns: realdata,
type : '{{config.GRAPH_TYPE|default('pie')}}', type : '{{config.GRAPH_TYPE|default('pie')}}',
} }
}); });
{% endif %} })
{% endif %}
{% endblock onload_script %} {% endblock onload_script %}
{% block content %} {% block content %}
{% if render_graph %}
<div id="factChart" width="300" height="300"></div> <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 %} {% 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 %} {% 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', 'pagingType': 'simple',
"bFilter": false, "bFilter": false,
{% endmacro %} {% 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="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 %} {% endblock onload_script %}
{% block content %} {% block content %}
@@ -69,7 +75,16 @@
</div> </div>
<div class='column'> <div class='column'>
<h1>Facts</h1> <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>
</div> </div>
{% endblock content %} {% endblock content %}