Improve display of task results, records and host facts
Pass the text, json, lists and dicts to pygments in order to get the nice formatting and highlighting from it. Change-Id: I3846fc55a70ea5a0f0487ee8afc6b13d55b50f4b
This commit is contained in:
parent
788bbac413
commit
cbd3ae1e9b
|
@ -24,6 +24,8 @@ from os import path
|
|||
from pygments import highlight
|
||||
from pygments.formatters import HtmlFormatter
|
||||
from pygments.lexers import YamlLexer
|
||||
from pygments.lexers import JsonLexer
|
||||
from pygments.lexers.special import TextLexer
|
||||
|
||||
|
||||
def configure_template_filters(app):
|
||||
|
@ -87,6 +89,26 @@ def configure_template_filters(app):
|
|||
YamlLexer(),
|
||||
formatter)
|
||||
|
||||
@app.template_filter('pygments_formatter')
|
||||
def jinja_pygments_formatter(data):
|
||||
formatter = HtmlFormatter(cssclass='codehilite')
|
||||
|
||||
if isinstance(data, str) or isinstance(data, unicode):
|
||||
data.rstrip()
|
||||
try:
|
||||
data = json.loads(data)
|
||||
data = json.dumps(data, indent=4, sort_keys=True)
|
||||
lexer = JsonLexer()
|
||||
except (ValueError, TypeError):
|
||||
lexer = TextLexer()
|
||||
elif isinstance(data, dict) or isinstance(data, list):
|
||||
data = json.dumps(data, indent=4, sort_keys=True)
|
||||
lexer = JsonLexer()
|
||||
else:
|
||||
lexer = TextLexer()
|
||||
|
||||
return highlight(Markup(data).unescape(), lexer, formatter)
|
||||
|
||||
@app.template_filter('fast_count')
|
||||
def jinja_fast_count(query):
|
||||
return fast_count(query)
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
|
||||
/* Table styling */
|
||||
/* Wrap pre tags so they don't print endlessly horizontally */
|
||||
.table pre, .results pre {
|
||||
.records pre, .facts pre, .results .codehilite pre {
|
||||
white-space: pre-wrap;
|
||||
white-space: -moz-pre-wrap;
|
||||
white-space: -pre-wrap;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% if record.type == 'json' or record.type == 'dict' -%}
|
||||
<pre>{{ record.value | to_nice_json | escape }}</pre>
|
||||
{{ record.value | pygments_formatter | safe }}
|
||||
{% elif record.type == 'list' -%}
|
||||
<ul class="text-left">
|
||||
{% for item in record.value -%}
|
||||
|
|
|
@ -1 +1 @@
|
|||
<a href="#" data-toggle="modal" data-target="#task_result_modal" data-load="{{ result.id }}/"><span class="{{ result.derived_status }} label">{{ result.derived_status |upper }}</span></a>
|
||||
<a href="#" data-toggle="modal" data-target="#task_result_modal" data-load="{{ result.id }}/"><span class="{{ result.derived_status }} label">{{ result.derived_status | upper }}</span></a>
|
|
@ -6,7 +6,7 @@
|
|||
<h2>Host facts: <strong>{{ host.name }}</strong></h2>
|
||||
<p>Last updated: {{ host.facts.timestamp | datefmt }}</p>
|
||||
<p>Ansible version: <strong>{{ host.playbook.ansible_version }}</strong></p>
|
||||
<table class="table table-striped table-bordered table-hover table-condensed">
|
||||
<table class="table table-striped table-bordered table-hover table-condensed facts">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Fact</th>
|
||||
|
@ -17,7 +17,7 @@
|
|||
{% for fact, value in facts -%}
|
||||
<tr>
|
||||
<td id="{{ fact }}"><a href="#{{ fact }}">{{ fact }}</a></td>
|
||||
<td><pre>{{ value | to_nice_json | escape }}</pre></td>
|
||||
<td>{{ value | pygments_formatter | safe }}</td>
|
||||
</tr>
|
||||
{% endfor -%}
|
||||
</tbody>
|
||||
|
|
|
@ -239,7 +239,7 @@
|
|||
<span class="pficon pficon-help pull-right" data-toggle="tooltip" data-placement="bottom" data-html="true" title="{% include "tips/records.html" %}"></span>
|
||||
</h3>
|
||||
{% if record_count -%}
|
||||
<table class="table table-striped table-bordered table-hover table-condensed" id="records_{{ playbook.id }}">
|
||||
<table class="table table-striped table-bordered table-hover table-condensed records" id="records_{{ playbook.id }}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span class="pull-right">Key</span></th>
|
||||
|
@ -313,7 +313,7 @@
|
|||
</div>
|
||||
<div class="modal-body embed-responsive embed-responsive-item">
|
||||
<div class="row text-center"><strong>Loading task details...</strong></div>
|
||||
<div class="spinner spinner-lg iframe-spinner"></div>
|
||||
<div class="spinner spinner-lg"></div>
|
||||
<iframe id="result_iframe" class="iframe" data-src="{{ url_for('result.index') }}"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -4,19 +4,13 @@
|
|||
{% if result is mapping -%}
|
||||
{% if 'item' in result -%}
|
||||
<{{ hdr }}>Item</{{ hdr }}>
|
||||
<pre>{{ result.item | to_nice_json | escape }}</pre>
|
||||
{{ result.item | pygments_formatter | safe }}
|
||||
{% endif %}
|
||||
|
||||
{% for attr in result.keys()|sort if attr not in ['item', 'changed', 'stdout_lines'] -%}
|
||||
{% if result[attr]|default(False) -%}
|
||||
<{{ hdr }}>{{ attr|title }}</{{ hdr }}>
|
||||
{% if result[attr] is string -%}
|
||||
<pre>{{ result[attr] }}</pre>
|
||||
{% elif result[attr] is mapping or result[attr] is iterable -%}
|
||||
<pre>{{ result[attr] | to_nice_json | escape }}</pre>
|
||||
{% else -%}
|
||||
<pre>{{ result | to_nice_json | escape }}</pre>
|
||||
{% endif -%}
|
||||
{% for attr in result.keys() | sort if attr not in ['item', 'changed', 'stdout_lines'] -%}
|
||||
{% if result[attr] | default(False) -%}
|
||||
<{{ hdr }}>{{ attr | title }}</{{ hdr }}>
|
||||
{{ result[attr] | pygments_formatter | safe }}
|
||||
{% endif -%}
|
||||
{% endfor -%}
|
||||
{% elif result is iterable and result is not string %}
|
||||
|
@ -29,10 +23,11 @@
|
|||
</div>
|
||||
{% endfor %}
|
||||
{% else -%}
|
||||
<pre>{{ result | to_nice_json | escape }}</pre>
|
||||
{{ result | pygments_formatter | safe }}
|
||||
{% endif -%}
|
||||
{% endmacro %}
|
||||
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12 results">
|
||||
|
@ -44,15 +39,16 @@
|
|||
<p>Time: <strong>{{ task_result.time_start | datefmt }}</strong></p>
|
||||
<p>Ansible version: <strong>{{ task_result.task.playbook.ansible_version }}</strong></p>
|
||||
{% set result = task_result.result | from_json %}
|
||||
{% if 'results' in result -%}
|
||||
{% for item in result.results -%}
|
||||
<h2>Result {{loop.index}}</h2>
|
||||
|
||||
{% if 'results' in result %}
|
||||
{% for item in result.results %}
|
||||
<h2>Result #{{ loop.index }}</h2>
|
||||
{{ display_result(item, 'h3') }}
|
||||
{% endfor -%}
|
||||
{% else -%}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{{ display_result(result, 'h2') }}
|
||||
{% endif -%}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -107,6 +107,69 @@ class TestFilters(TestAra):
|
|||
|
||||
self.assertEqual(res, u'"definitely not json"')
|
||||
|
||||
def test_jinja_pygments_formatter_string_simple(self):
|
||||
data = "string"
|
||||
t = self.env.from_string('{{ data | pygments_formatter | safe }}')
|
||||
res = t.render(data=data)
|
||||
|
||||
# This is ugly, sorry
|
||||
expected = u'''<div class="codehilite"><pre><span></span>string\n</pre></div>\n''' # flake8: noqa
|
||||
self.assertEqual(res, expected)
|
||||
|
||||
def test_jinja_pygments_formatter_string_json(self):
|
||||
data = '{"one": "value", "two": "value"}'
|
||||
t = self.env.from_string('{{ data | pygments_formatter | safe }}')
|
||||
res = t.render(data=data)
|
||||
|
||||
# This is ugly, sorry
|
||||
expected = u'''<div class="codehilite"><pre><span></span><span class="p">{</span>\n <span class="nt">"one"</span><span class="p">:</span> <span class="s2">"value"</span><span class="p">,</span> \n <span class="nt">"two"</span><span class="p">:</span> <span class="s2">"value"</span>\n<span class="p">}</span>\n</pre></div>\n''' # flake8: noqa
|
||||
self.assertEqual(res, expected)
|
||||
|
||||
def test_jinja_pygments_formatter_unicode(self):
|
||||
data = u"string"
|
||||
t = self.env.from_string('{{ data | pygments_formatter | safe }}')
|
||||
res = t.render(data=data)
|
||||
|
||||
# This is ugly, sorry
|
||||
expected = u'''<div class="codehilite"><pre><span></span>string\n</pre></div>\n''' # flake8: noqa
|
||||
self.assertEqual(res, expected)
|
||||
|
||||
def test_jinja_pygments_formatter_list(self):
|
||||
data = ['one', 'two']
|
||||
t = self.env.from_string('{{ data | pygments_formatter | safe }}')
|
||||
res = t.render(data=data)
|
||||
|
||||
# This is ugly, sorry
|
||||
expected = u'''<div class="codehilite"><pre><span></span><span class="p">[</span>\n <span class="s2">"one"</span><span class="p">,</span> \n <span class="s2">"two"</span>\n<span class="p">]</span>\n</pre></div>\n''' # flake8: noqa
|
||||
self.assertEqual(res, expected)
|
||||
|
||||
def test_jinja_pygments_formatter_dict(self):
|
||||
data = {'one': 'value', 'two': 'value'}
|
||||
t = self.env.from_string('{{ data | pygments_formatter | safe }}')
|
||||
res = t.render(data=data)
|
||||
|
||||
# This is ugly, sorry
|
||||
expected = u'''<div class="codehilite"><pre><span></span><span class="p">{</span>\n <span class="nt">"one"</span><span class="p">:</span> <span class="s2">"value"</span><span class="p">,</span> \n <span class="nt">"two"</span><span class="p">:</span> <span class="s2">"value"</span>\n<span class="p">}</span>\n</pre></div>\n''' # flake8: noqa
|
||||
self.assertEqual(res, expected)
|
||||
|
||||
def test_jinja_pygments_formatter_integer(self):
|
||||
data = 1
|
||||
t = self.env.from_string('{{ data | pygments_formatter | safe }}')
|
||||
res = t.render(data=data)
|
||||
|
||||
# This is ugly, sorry
|
||||
expected = u'''<div class="codehilite"><pre><span></span>1\n</pre></div>\n''' # flake8: noqa
|
||||
self.assertEqual(res, expected)
|
||||
|
||||
def test_jinja_pygments_formatter_boolean(self):
|
||||
data = True
|
||||
t = self.env.from_string('{{ data | pygments_formatter | safe }}')
|
||||
res = t.render(data=data)
|
||||
|
||||
# This is ugly, sorry
|
||||
expected = u'''<div class="codehilite"><pre><span></span>True\n</pre></div>\n''' # flake8: noqa
|
||||
self.assertEqual(res, expected)
|
||||
|
||||
def test_jinja_yamlhighlight(self):
|
||||
data = """- name: Test thing
|
||||
hosts: localhost
|
||||
|
|
Loading…
Reference in New Issue