Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it possible to change sorting method via UI #128

Merged
merged 10 commits into from
Jun 20, 2024
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -285,10 +285,10 @@ Sorting method can be changed under the ``[howitz]``-section by adding::

Valid sorting methods are:

* *raw* - Unchanged order in which Zino server sends events (by ID ascending).
* *raw* - The same order in which Zino server sends events (by ID, ascending).
* *lasttrans* - Newest transaction first, all IGNORED at the bottom. Default sorting in curitz.

* *severity* - Events of same color grouped together. The most severe (red) at the top and ignored at the bottom. Existing method in Ritz TK, but it is called 'default' there.
* *severity* - Events with highest priority first, grouped by event type. Priority takes into account both whether event signifies any disturbance, event's administrative phase and event's type, so there might not be continuous blocks of color. Existing method in Ritz TK, but it is called 'default' there.
* *down-rev* - Shortest/none downtime first. Identical to an existing method in Ritz TK.
* *down* - Longest downtime first. Identical to an existing method in Ritz TK.
* *upd-rev* - Events with the most recent update date first. Identical to an existing method in Ritz TK.
Expand Down
4 changes: 2 additions & 2 deletions src/howitz/config/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ class HowitzConfig(ServerConfig, StorageConfig):
devmode: bool = Literal[False]
refresh_interval: int = 5
timezone: str = DEFAULT_TIMEZONE
sort_by: str = EventSort.DEFAULT
sort_by: str = str(EventSort.DEFAULT)


class DevHowitzConfig(DevServerConfig, DevStorageConfig):
devmode: bool = Literal[True]
refresh_interval: int = 5
timezone: str = DEFAULT_TIMEZONE
sort_by: str = EventSort.DEFAULT
sort_by: str = str(EventSort.DEFAULT)
58 changes: 46 additions & 12 deletions src/howitz/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,25 +42,29 @@ class EventColor(StrEnum):

# Inspired by https://stackoverflow.com/a/54732120
class EventSort(Enum):
AGE = "age", "opened", True # Newest events first
AGE_REV = "age-rev", "opened", False # Oldest events first
UPD = "upd", "updated", False # Events with the oldest update date first
UPD_REV = "upd-rev", "updated", True # Events with the most recent update date first
DOWN = "down", "get_downtime", True # Longest downtime first
DOWN_REV = "down-rev", "get_downtime", False # Shortest/none downtime first

LASTTRANS = "lasttrans", "updated", True # Newest transaction first, all IGNORED at the bottom. Default sorting at SSC
SEVERITY = "severity", "", True # Events of same color grouped together. The most severe (red) at the top and ignored at the bottom
DEFAULT = "raw", "", None # Unchanged order in which Zino server sends events (by ID ascending)
# Name, relevant event attribute, is_reversed, displayed name, description
AGE = "age", "opened", True, "Age", "Newest events first"
AGE_REV = "age-rev", "opened", False, "Age reversed", "Oldest events first"
UPD = "upd", "updated", False, "Activity", "Events with the oldest update date first"
UPD_REV = "upd-rev", "updated", True, "Activity reversed", "Events with the most recent update date first"
DOWN = "down", "get_downtime", True, "Downtime", "Events with longest downtime first"
DOWN_REV = "down-rev", "get_downtime", False, "Downtime reversed", "Events with shortest/none downtime first"

LASTTRANS = ("lasttrans", "updated", True, "Last transaction",
"Events with the most recent update date first, all IGNORED events are at the bottom")
SEVERITY = "severity", "", True, "Severity", "Events with highest priority first, grouped by event type. Priority takes into account both whether event signifies any disturbance, event's administrative phase and event's type, so there might not be continuous blocks of color"
DEFAULT = "raw", "", None, "Raw", "The same order in which Zino server sends events (by ID, ascending)"

def __new__(cls, *args, **kwds):
obj = object.__new__(cls)
obj._value_ = args[0]
return obj

def __init__(self, _: str, attribute: str = None, reversed: bool = None):
def __init__(self, _: str, attribute: str = None, reversed: bool = None, display_name: str = None, description: str = None):
self._attribute = attribute
self._reversed = reversed
self._display_name = display_name
self._description = description

def __str__(self):
return self.value
Expand All @@ -73,6 +77,14 @@ def attribute(self):
def reversed(self):
return self._reversed

@property
def display_name(self):
return self._display_name

@property
def description(self):
return self._description


def auth_handler(username, password):
# check user credentials in database
Expand All @@ -91,7 +103,7 @@ def auth_handler(username, password):
session["expanded_events"] = {}
session["errors"] = {}
session["event_ids"] = []
session["sort_by"] = current_app.howitz_config.get("sort_by", "default")
session["sort_by"] = current_app.howitz_config.get("sort_by", "raw")
session["events_last_refreshed"] = None
return user

Expand Down Expand Up @@ -758,6 +770,28 @@ def clear_flapping(i):
raise MethodNotAllowed(description='Cant clear flapping on a non-port event.')


@main.route('/events/table/change_sort_by', methods=['GET', 'POST'])
def change_events_order():
if request.method == 'POST':
# Get new sort method from the request
new_sort = request.form['sort-method']
session["sort_by"] = new_sort
session.modified = True

# Rerender whole events table
events = current_app.cache.get("events")
if events:
table_events = get_sorted_table_event_list(events)
else:
table_events = get_current_events()

return render_template('/responses/resort-events.html', event_list=table_events)

elif request.method == 'GET':
return render_template('/components/popups/modals/forms/sort-table-form.html', sort_methods=EventSort,
current_sort=EventSort(session["sort_by"]))


@main.route('/navbar/show-user-menu', methods=["GET"])
def show_user_menu():
return render_template('/responses/show-user-menu.html')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<form>
<div class="space-y-1">
<div class="space-y-0.5 leading-normal">
<fieldset class="">
<legend class="font-semibold">Change the order in which events are displayed in the
table
</legend>
<p class="mt-2">Sort events by:</p>
<div class="mt-2 space-y-1 px-4">
{% for sort in sort_methods %}
<div class="relative flex gap-x-3">
<div class="flex h-6 items-center">
<input id="{{ sort }}" value="{{ sort }}" type="radio" name="sort-method"
{% if sort == current_sort %}checked{% endif %}
class="h-4 w-4 bg-sky-100/40 border-white text-sky-600 focus:ring-sky-600">
</div>
<div class="text-sm">
<label for="{{ sort }}"
class="block font-medium">{{ sort.display_name }}</label>
<p class="text-xs text-sky-100/40">{{ sort.description }}</p>
</div>
</div>
{% endfor %}
</div>
</fieldset>
</div>
</div>

<div class="mt-2 flex items-center justify-start gap-x-6">
<button
type="submit"
hx-post="/events/table/change_sort_by"
hx-target="#eventlist-list"
hx-swap="innerHTML"
hx-indicator="#bulk-update-indicator"
class="mr-1 inline-flex items-center py-2 px-4 font-medium text-center text-white rounded-lg focus:ring-4 bg-sky-700 hover:bg-sky-800 focus:outline-none focus:ring-sky-900">
Save
</button>

<p id="bulk-update-indicator"
class="flex-inline mt-2 animate-pulse text-white bulk-update-htmx-indicator">
Updating events order...
</p>
</div>
</form>
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
role="dialog"
aria-modal="true"
hx-target-error="body"
hx-swap-oob="true"
>
<div
class="modal-underlay inset-0 bg-gray-500 bg-opacity-70 transition-opacity"
Expand All @@ -19,15 +20,20 @@
id="modal-title">{{ modal_title }}</h3>
</div>

<div class="bg-slate-700 p-4 text-white">
{% if path_to_content %}
{% include path_to_content %}
{% else %}
<p class="py-6 text-center">
Nothing to display
</p>
{% endif %}

<div class="bg-slate-700 px-4 pb-4 pt-5 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div class="w-full mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left">
<div id="{{ modal_id }}-content" class="text-white">
{% if path_to_content %}
{% include path_to_content %}
{% else %}
<p class="py-6 text-center">
Nothing to display
</p>
{% endif %}
</div>
</div>
</div>
</div>

<div class="bg-slate-800 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6">
Expand Down
4 changes: 4 additions & 0 deletions src/howitz/templates/components/toolbar/table-sort-btn.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
hx-on:mouseenter="htmx.removeClass(htmx.find('#sort-popover'), 'invisible')"
hx-on:mouseleave="htmx.addClass(htmx.find('#sort-popover'), 'invisible')"
hx-on:click="htmx.removeClass(htmx.find('#sort-menu-dropdown'), 'invisible')"
hx-get="/events/table/change_sort_by"
hx-target="#sort-menu-dropdown-content"
hx-swap="innerHTML"
hx-trigger="click"
aria-describedby="sort-popover"
>
<svg class="fill-teal-300/90 hover:fill-teal-300 p-1.5 size-full"
Expand Down
7 changes: 7 additions & 0 deletions src/howitz/templates/responses/resort-events.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{% with event_list=event_list %}
{% include "/components/table/event-rows.html" %}
{% endwith %}

{% with modal_id='sort-menu-dropdown', modal_title='Sort events' %}
{% include "/components/popups/modals/table-operation-modal.html" %}
{% endwith %}