diff --git a/changelog.d/+shrink-stored-filter-widget.changed.md b/changelog.d/+shrink-stored-filter-widget.changed.md new file mode 100644 index 000000000..bb7a0b14e --- /dev/null +++ b/changelog.d/+shrink-stored-filter-widget.changed.md @@ -0,0 +1 @@ +Made the controls for storing filters take up less horizontal space. diff --git a/src/argus/htmx/incident/filter.py b/src/argus/htmx/incident/filter.py index c7f06753c..dfb94d21c 100644 --- a/src/argus/htmx/incident/filter.py +++ b/src/argus/htmx/incident/filter.py @@ -15,6 +15,9 @@ QuerySetFilter = filter_backend.QuerySetFilter LOG = logging.getLogger(__name__) +MIN_LEVEL = min(Level).value +MAX_LEVEL = max(Level).value + class RangeInput(forms.NumberInput): template_name = "django/forms/widgets/range.html" @@ -78,9 +81,9 @@ class IncidentFilterForm(TagFieldMixin, forms.Form): help_text='Press "Enter" after each completed tag', ) maxlevel = forms.IntegerField( - widget=RangeInput(attrs={"step": "1", "min": min(Level).value, "max": max(Level).value}), + widget=RangeInput(attrs={"step": "1", "min": MIN_LEVEL, "max": MAX_LEVEL}), label="Level <=", - initial=max(Level).value, + initial=MAX_LEVEL, required=False, ) @@ -90,7 +93,7 @@ class IncidentFilterForm(TagFieldMixin, forms.Form): "sourceSystemIds": [], "source_types": [], "tags": [], - "maxlevel": max(Level).value, + "maxlevel": MAX_LEVEL, } def __init__(self, *args, **kwargs): @@ -148,8 +151,13 @@ def to_filterblob(self): filterblob = {} - filterblob["open"] = self._open_tristate() - filterblob["acked"] = self._acked_tristate() + open = self._open_tristate() + if open is not None: + filterblob["open"] = open + + acked = self._acked_tristate() + if acked is not None: + filterblob["acked"] = acked sourceSystemIds = self.cleaned_data.get("sourceSystemIds", []) if sourceSystemIds: @@ -190,44 +198,54 @@ def get_success_url(self): return reverse("htmx:filter-list") +# Not a view! def incident_list_filter(request, qs, use_empty_filter=False): LOG = logging.getLogger(__name__ + ".incident_list_filter") - LOG.debug("GET at start: %s", request.GET) - filter_pk, filter_obj = request.session.get("selected_filter", None), None + LOG.debug("<<<>>>FORM>>>> early return") + return form, qs + if form.is_valid(): - LOG.debug("Cleaned data: %s", form.cleaned_data) + LOG.debug("Cleaned data: %s, getting query set", form.cleaned_data) filterblob = form.to_filterblob() + LOG.debug("using filterblob: %s", filterblob) qs = QuerySetFilter.filtered_incidents(filterblob, qs) else: - if not request.GET: - LOG.debug("empty form") - else: - LOG.debug("Dirty form: %s", form.errors) - for field, error_messages in form.errors.items(): - messages.error(request, f"{field}: {','.join(error_messages)}") + LOG.debug("Dirty form: %s", form.errors) + for field, error_messages in form.errors.items(): + messages.error(request, f"{field}: {','.join(error_messages)}") + LOG.debug(">>>>FORM>>>>") return form, qs def _convert_filterblob(filterblob): """Converts values in filterblob so it can be used as valid input for IncidentFilterForm""" + filterblob = filterblob.copy() if "open" in filterblob.keys(): open_state = filterblob["open"] @@ -250,10 +268,9 @@ def _convert_filterblob(filterblob): return filterblob -def _normalize_form_data(request): +def _normalize_form_data(raw_data): """Normalizes form data from request, especially the 'tags' parameter.""" - raw_data = request.POST if request.method == "POST" else request.GET data = dict(raw_data.items()) for key in raw_data: value = raw_data.getlist(key, []) diff --git a/src/argus/htmx/incident/views.py b/src/argus/htmx/incident/views.py index 4317defd0..880c25725 100644 --- a/src/argus/htmx/incident/views.py +++ b/src/argus/htmx/incident/views.py @@ -113,10 +113,27 @@ def incident_update(request: HtmxHttpRequest, action: str): return HttpResponseClientRefresh() +def set_selected_filter(request, filter_obj): + if filter_obj: + request.session["selected_filter_pk"] = str(filter_obj.pk) + request.session["selected_filter_name"] = filter_obj.name + else: + request.session["selected_filter_pk"] = None + request.session.pop("selected_filter_name", None) + + +def get_selected_filter(request): + filter_id = request.session.get("selected_filter_pk", None) + if filter_id: + return get_object_or_404(Filter, pk=filter_id, user=request.user) + return None + + @require_GET def filter_form(request: HtmxHttpRequest): + set_selected_filter(request, None) LOG = logging.getLogger(__name__ + ".filter_form") - request.session["selected_filter"] = None + LOG.debug("filter_form calling incident_list_filter") incident_list_filter = get_filter_function() filter_form, _ = incident_list_filter(request, None) context = {"filter_form": filter_form} @@ -129,13 +146,14 @@ def create_filter(request: HtmxHttpRequest): from argus.htmx.incident.filter import create_named_filter filter_name = request.POST.get("filter_name", None) + LOG.debug("create_filter calling incident_list_filter") incident_list_filter = get_filter_function() filter_form, _ = incident_list_filter(request, None) if filter_name and filter_form.is_valid(): filterblob = filter_form.to_filterblob() _, filter_obj = create_named_filter(request, filter_name, filterblob) if filter_obj: - request.session["selected_filter"] = str(filter_obj.id) + set_selected_filter(request, filter_obj) return HttpResponseClientRefresh() messages.error(request, "Failed to create filter") return HttpResponseBadRequest() @@ -144,6 +162,7 @@ def create_filter(request: HtmxHttpRequest): @require_POST def update_filter(request: HtmxHttpRequest, pk: int): filter_obj = get_object_or_404(Filter, id=pk) + LOG.debug("update_filter calling incident_list_filter") incident_list_filter = get_filter_function() filter_form, _ = incident_list_filter(request, None) if filter_form.is_valid(): @@ -152,11 +171,12 @@ def update_filter(request: HtmxHttpRequest, pk: int): filter_obj.save() # Immediately select the newly updated filter - keep or not? - # request.session["selected_filter"] = str(filter_obj.id) + # set_selected_filter(request, filter_obj) messages.success(request, f"Updated filter '{filter_obj.name}'.") return HttpResponseClientRefresh() - messages.error(request, f"Failed to update filter '{filter_obj.name}'.") + errors = f": {filter_form.errors}" if filter_form.errors else "" + messages.error(request, f'Failed to update filter "{filter_obj.name}": {errors}') return HttpResponseBadRequest() @@ -166,8 +186,8 @@ def delete_filter(request: HtmxHttpRequest, pk: int): deleted_id = filter_obj.delete() if deleted_id: messages.success(request, f"Deleted filter {filter_obj.name}.") - if request.session.get("selected_filter") == str(pk): - request.session["selected_filter"] = None + if request.session.get("selected_filter_pk") == str(pk): + set_selected_filter(request, None) return HttpResponseClientRefresh() @@ -185,22 +205,33 @@ def get_existing_filters(request: HtmxHttpRequest): @require_GET def filter_select(request: HtmxHttpRequest): + LOG = logging.getLogger(__name__ + ".filter_select") + context = {} + template_name = "htmx/incident/_incident_list_filter_incidents.html" + + LOG.debug("GET when selecting filter: %s", request.GET) filter_id = request.GET.get("filter", None) - if filter_id and get_object_or_404(Filter, id=filter_id): - request.session["selected_filter"] = filter_id - incident_list_filter = get_filter_function() - filter_form, _ = incident_list_filter(request, None) - context = {"filter_form": filter_form} - return render(request, "htmx/incident/_incident_filterbox.html", context=context) + if filter_id: + use_empty_filter = False + filter_obj = get_object_or_404(Filter, id=filter_id) + set_selected_filter(request, filter_obj) + LOG.debug("Selecting filter: %s", filter_obj.name) else: - request.session["selected_filter"] = None - if request.htmx.trigger: - incident_list_filter = get_filter_function() - filter_form, _ = incident_list_filter(request, None, use_empty_filter=True) - context = {"filter_form": filter_form} - return render(request, "htmx/incident/_incident_filterbox.html", context=context) - else: - return retarget(HttpResponse(), "#incident-filter-select") + use_empty_filter = True + set_selected_filter(request, None) + LOG.debug("Selecting empty filter") + + if request.htmx.trigger: + LOG.debug("filter_select calling incident_list_filter") + incident_list_filter = get_filter_function() + LOG.debug("nao 1 %s", request.GET) + filter_form, _ = incident_list_filter(request, None, use_empty_filter=use_empty_filter) + context["filter_form"] = filter_form + LOG.debug("nao 2 %s", request.GET) + return render(request, template_name, context=context) + + LOG.debug("boo %s", request.GET) + return retarget(HttpResponse(), "#incident-filter-select") def dedupe_querydict(querydict: QueryDict): @@ -219,12 +250,16 @@ def dedupe_querydict(querydict: QueryDict): def add_param_to_querydict(querydict: QueryDict, key: str, value: Any): "Set key to value if missing from querydict" + LOG = logging.getLogger(__name__ + ".add_param_to_querydict") qd = querydict.copy() + LOG.debug("Current QueryDict: %s, about to alter %s", qd, key) if value is None: + LOG.debug("Unchanged QueryDict: %s, value is None", querydict) return querydict if key not in qd: if isinstance(value, Iterable): if not value: + LOG.debug("Unchanged QueryDict: %s, value is falsey Iterable", querydict) return querydict if isinstance(value, str): value = [value] @@ -234,7 +269,9 @@ def add_param_to_querydict(querydict: QueryDict, key: str, value: Any): value = [str(value)] qd.setlist(key, value) qd._mutable = False + LOG.debug("Altered QueryDict: %s", qd) return qd + LOG.debug("Unchanged QueryDict: %s, key not found", querydict) return querydict @@ -263,6 +300,7 @@ def search_tags(request): @require_GET def incident_list(request: HtmxHttpRequest) -> HttpResponse: LOG = logging.getLogger(__name__ + ".incident_list") + LOG.debug("<=====VIEW=====<") LOG.debug("GET at start: %s", request.GET) request.GET = dedupe_querydict(request.GET) LOG.debug("after dedupe: %s", request.GET) @@ -276,6 +314,11 @@ def incident_list(request: HtmxHttpRequest) -> HttpResponse: total_count = qs.count() last_refreshed = make_aware(datetime.now()) + # Stored filters + existing_filters = Filter.objects.filter(user=request.user) + + # Get filters storable in Filter.filter + LOG.debug("calling incident_list_filter") incident_list_filter = get_filter_function() filter_form, qs = incident_list_filter(request, qs) @@ -327,14 +370,22 @@ def incident_list(request: HtmxHttpRequest) -> HttpResponse: LOG.debug("GET at end: %s", request.GET) context = { - "columns": columns, + "page_title": "Incidents", + "base": base_template, + # filter box "filter_form": filter_form, + # storing filters + "stored_filters": existing_filters, + # table + "columns": columns, + # refresh info "refresh_info": refresh_info, "refresh_info_forms": GET_forms, - "page_title": "Incidents", - "base": base_template, + "filtered_count": filtered_count, + "count": total_count, "page": page, "last_page_num": last_page_num, "second_to_last_page": last_page_num - 1, } + LOG.debug(">=====VIEW=====>") return render(request, "htmx/incident/incident_list.html", context=context) diff --git a/src/argus/htmx/templates/htmx/_base_confirm_dialog.html b/src/argus/htmx/templates/htmx/_base_confirm_dialog.html index 04d8a1e91..1d06b5592 100644 --- a/src/argus/htmx/templates/htmx/_base_confirm_dialog.html +++ b/src/argus/htmx/templates/htmx/_base_confirm_dialog.html @@ -1,3 +1,4 @@ +
{% include "htmx/incident/_filter_select.html" %} - {% include "htmx/incident/_filter_create_modal.html" with dialog_id="create-filter-dialog" button_title="Create filter" button_class="btn-sm text-xs join-item" header="Create new filter" explanation="Create new filter from currently selected filter parameters" cancel_text="Cancel" submit_text="Submit" %} - {% include "htmx/incident/_filter_update_dropdown.html" %} - {% include "htmx/incident/_filter_delete_dropdown.html" %} + {% include "htmx/incident/_filter_create_modal.html" with dialog_id="create-filter-dialog" button_title="Save new" button_class="btn-sm text-xs join-item" header="Create new filter" explanation="Create new filter from currently selected filter parameters" cancel_text="Cancel" submit_text="Submit" %} + {% with item_id=request.session.selected_filter_pk item_title=request.session.selected_filter_name item_class="btn btn-sm text-xs join-item rounded-l-none! ml-1" %} + {% if request.session.selected_filter_pk %} + {% url 'htmx:filter-update' pk=request.session.selected_filter_pk as update_filter_url %} + {% include "htmx/incident/_filter_controls_confirm_dialog.html" with filter_url=update_filter_url modal_button_name="Update" dialog_id="filter-update-confirm" action="Update filter" confirmation_message="Are you sure you want to override this filter?" %} + {% url 'htmx:filter-delete' pk=request.session.selected_filter_pk as delete_filter_url %} + {% include "htmx/incident/_filter_controls_confirm_dialog.html" with filter_url=delete_filter_url modal_button_name="Delete" dialog_id="filter-delete-confirm" action="Delete filter" confirmation_message="Are you sure you want to delete this filter?" %} + {% else %} + + + {% endif %} + {% endwith %}
diff --git a/src/argus/htmx/templates/htmx/incident/_update_filter_confirm_dialog.html b/src/argus/htmx/templates/htmx/incident/_filter_controls_confirm_dialog.html similarity index 65% rename from src/argus/htmx/templates/htmx/incident/_update_filter_confirm_dialog.html rename to src/argus/htmx/templates/htmx/incident/_filter_controls_confirm_dialog.html index 00f515b10..90b64b34b 100644 --- a/src/argus/htmx/templates/htmx/incident/_update_filter_confirm_dialog.html +++ b/src/argus/htmx/templates/htmx/incident/_filter_controls_confirm_dialog.html @@ -1,5 +1,6 @@ + {% extends "htmx/_base_confirm_dialog.html" %} {% block confirm_action_control %} - hx-post="{% url 'htmx:filter-update' pk=filter.id %}" + hx-post="{{ filter_url }}" hx-include="#incident-filter-box fieldset" {% endblock confirm_action_control %} diff --git a/src/argus/htmx/templates/htmx/incident/_filter_create_modal.html b/src/argus/htmx/templates/htmx/incident/_filter_create_modal.html index 3464b128d..2b2296e60 100644 --- a/src/argus/htmx/templates/htmx/incident/_filter_create_modal.html +++ b/src/argus/htmx/templates/htmx/incident/_filter_create_modal.html @@ -1,3 +1,4 @@ + {% extends "htmx/_base_form_modal.html" %} {% block form_control %} hx-post="{% url 'htmx:filter-create' %}" diff --git a/src/argus/htmx/templates/htmx/incident/_filter_delete_dropdown.html b/src/argus/htmx/templates/htmx/incident/_filter_delete_dropdown.html deleted file mode 100644 index d23b198dc..000000000 --- a/src/argus/htmx/templates/htmx/incident/_filter_delete_dropdown.html +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/src/argus/htmx/templates/htmx/incident/_filter_update_dropdown.html b/src/argus/htmx/templates/htmx/incident/_filter_update_dropdown.html deleted file mode 100644 index 58d0092da..000000000 --- a/src/argus/htmx/templates/htmx/incident/_filter_update_dropdown.html +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/src/argus/htmx/templates/htmx/incident/_incident_filterbox.html b/src/argus/htmx/templates/htmx/incident/_incident_filterbox.html index 6bbcc0721..7bd7b2a6b 100644 --- a/src/argus/htmx/templates/htmx/incident/_incident_filterbox.html +++ b/src/argus/htmx/templates/htmx/incident/_incident_filterbox.html @@ -1,3 +1,4 @@ + {% load widget_tweaks %}
+
+ {% include "htmx/incident/_incident_filterbox.html" %} + {% include "htmx/incident/_filter_controls.html" %} +
diff --git a/src/argus/htmx/templates/htmx/incident/_incident_list_menubar.html b/src/argus/htmx/templates/htmx/incident/_incident_list_menubar.html index 456475123..705d1ae58 100644 --- a/src/argus/htmx/templates/htmx/incident/_incident_list_menubar.html +++ b/src/argus/htmx/templates/htmx/incident/_incident_list_menubar.html @@ -1,3 +1,4 @@ +
{% block menu_tabs %}
-
- {% include "htmx/incident/_incident_filterbox.html" %} - {% include "htmx/incident/_filter_controls.html" %} -
+ {% include "htmx/incident/_incident_list_filter_incidents.html" %}