UI: Add playbook result search, filtering and ordering

This is a first iteration that adds a form in the task results pane
which allows to search by:
- host id
- task id
- status
- changed

Ordering by date and duration is done by clicking on the respective
headers.

Change-Id: Iaa67fee1e182807e69d7df7c39bf2ee1d0b67bd4
This commit is contained in:
David Moreau Simard 2020-09-06 11:45:23 -04:00
parent 532ff7ddde
commit 08f5e324d0
3 changed files with 94 additions and 10 deletions

View File

@ -27,3 +27,13 @@ class PlaybookSearchForm(forms.Form):
widget=forms.CheckboxSelectMultiple, choices=models.Playbook.STATUS, required=False
)
label = forms.CharField(label="Playbook label", max_length=255, required=False)
class ResultSearchForm(forms.Form):
host = forms.CharField(label="Host id", max_length=10, required=False)
task = forms.CharField(label="Task id", max_length=10, required=False)
changed = forms.BooleanField(label="Changed", required=False)
status = forms.MultipleChoiceField(
widget=forms.CheckboxSelectMultiple, choices=models.Result.STATUS, required=False
)

View File

@ -9,7 +9,7 @@
<summary>Records</summary>
{% if records %}
<ul class="pf-c-list">
{% for record in playbook.records %}
{% for record in records %}
<li><a href="../records/{{ record.id }}.html">{{ record.key }}</a></li>
{% endfor %}
</ul>
@ -25,7 +25,7 @@
{% endfor %}
</ul>
</details>
<details id="hosts" open="true">
<details id="hosts" {% if not request.GET %}open="true"{% endif %}>
<summary><i class="fas fa-server"></i> Hosts</summary>
<table class="pf-c-table pf-m-grid-md pf-m-compact" role="grid" id="host-table">
<thead>
@ -102,6 +102,74 @@
<details id="results" open="true">
<summary>Task results</summary>
{% if not static_generation %}
<div class="pf-l-flex">
<div class="pf-l-flex">
<form method="get" class="pf-c-form">
<div class="pf-c-form__group pf-m-inline">
<div class="pf-l-flex__item pf-m-flex-1" for="{{ search_form.host.id_for_label }}">
<label class="pf-c-form__label" for="{{ search_form.host.id_for_label }}">
<span class="pf-c-form__label-text">Host id</span>
</label>
<div class="pf-c-form__horizontal-group">
<input class="pf-c-form-control" type="text" id="host" name="host" value="{{ search_form.host.value | default_if_none:'' }}" />
</div>
</div>
<div class="pf-l-flex__item pf-m-flex-1" for="{{ search_form.task.id_for_label }}">
<label class="pf-c-form__label" for="{{ search_form.task.id_for_label }}">
<span class="pf-c-form__label-text">Task id</span>
</label>
<div class="pf-c-form__horizontal-group">
<input class="pf-c-form-control" type="text" id="task" name="task" value="{{ search_form.task.value | default_if_none:'' }}" />
</div>
</div>
<div class="pf-l-flex__item">
<div class="pf-c-form__group">
<label class="pf-c-form__label" for="status">
<span class="pf-c-form__label-text">Status</span>
</label>
<div class="pf-c-form__group pf-m-inline">
<fieldset class="pf-c-form__fieldset" aria-labelledby="select-checkbox-expanded-label">
{% for value, text in search_form.status.field.choices %}
{# searching ignored or changed is broken: https://github.com/ansible-community/ara/issues/150 #}
{% if value not in "ignored,changed,unknown" %}
<label class="pf-c-check pf-c-select__menu-item" for="{{ value }}">
{% if value in search_form.status.data %}
<input class="pf-c-check__input" type="checkbox" id="{{ value }}" name="status" value="{{ value }}" checked />
{% else %}
<input class="pf-c-check__input" type="checkbox" id="{{ value }}" name="status" value="{{ value }}" />
{% endif %}
<span class="pf-c-check__label">{{ value }}</span>
</label>
{% endif %}
{% endfor %}
<label class="pf-c-check pf-c-select__menu-item" for="changed">
<input class="pf-c-check__input" type="checkbox" id="changed" name="changed" value=True {% if search_form.changed.value %}checked{% endif %} />
<span class="pf-c-check__label">changed</span>
</label>
</fieldset>
</div>
</div>
</div>
<div class="pf-l-flex__item">
<div class="pf-c-form__actions">
<button class="pf-c-button pf-m-primary" type="submit"><i class="fas fa-search"></i> Search</button>
</div>
</div>
{% if request.GET %}
<div class="pf-l-flex__item">
<div class="pf-c-form__actions">
<a href="{% url 'ui:playbook' playbook.id %}">
<button class="pf-c-button pf-m-plain pf-m-link" type="button" aria-label="Remove">
<i class="fas fa-times" aria-hidden="true"></i> Clear filters
</button>
</a>
</div>
</div>
{% endif %}
</div>
</form>
</div>
</div>
{% include "partials/pagination.html" with data=results %}
{% endif %}
<table class="pf-c-table pf-m-grid-md pf-m-compact" role="grid" id="result-table">
@ -111,11 +179,11 @@
<th role="columnheader" scope="col">Host</th>
<th role="columnheader" scope="col">Task</th>
<th role="columnheader" scope="col">Action</th>
<th role="columnheader" scope="col" class="pf-m-fit-content">
Duration
<th role="columnheader" scope="col" class="pf-m-fit-content pf-c-table__sort">
{% include "partials/sort_by_duration.html" %}
</th>
<th role="columnheader" scope="col" class="pf-m-fit-content">
Date
<th role="columnheader" scope="col" class="pf-m-fit-content pf-c-table__sort">
{% include "partials/sort_by_date.html" %}
</th>
</tr>
</thead>
@ -132,7 +200,7 @@
<a href="../results/{{ result.id }}.html">{{ result.task.name }}</a>
</td>
<td role="cell" data-label="Action" class="pf-m-fit-content">
<a href="../files/{{ task.file.id }}.html#line-{{ result.task.lineno }}">{{ result.task.action }}</a>
<a href="../files/{{ result.task.file }}.html#line-{{ result.task.lineno }}">{{ result.task.action }}</a>
</td>
<td role="cell" data-label="Duration" class="pf-m-fit-content">
{{ result.duration | format_duration }}

View File

@ -80,13 +80,18 @@ class Playbook(generics.RetrieveAPIView):
models.Record.objects.filter(playbook=playbook.data["id"]).all(), many=True
)
results = models.Result.objects.filter(playbook=playbook.data["id"])
search_form = forms.ResultSearchForm(request.GET)
order = "-started"
if "order" in request.GET:
order = request.GET["order"]
result_queryset = models.Result.objects.filter(playbook=playbook.data["id"]).order_by(order).all()
result_filter = filters.ResultFilter(request.GET, queryset=result_queryset)
page = self.paginate_queryset(results)
page = self.paginate_queryset(result_filter.qs)
if page is not None:
serializer = serializers.ListResultSerializer(page, many=True)
else:
serializer = serializers.ListResultSerializer(results, many=True)
serializer = serializers.ListResultSerializer(result_filter, many=True)
for result in serializer.data:
task_id = result["task"]
@ -109,6 +114,7 @@ class Playbook(generics.RetrieveAPIView):
"records": records.data,
"results": paginated_results.data,
"current_page_results": current_page_results,
"search_form": search_form
})
# fmt: on