Skip to content

Commit

Permalink
Merge pull request #777 from debrief/maintenance-gui-v4
Browse files Browse the repository at this point in the history
Maintenance GUI - Phase 4
  • Loading branch information
IanMayo authored Mar 1, 2021
2 parents 90c5bb3 + e4b1886 commit 6511b69
Show file tree
Hide file tree
Showing 11 changed files with 689 additions and 109 deletions.
56 changes: 53 additions & 3 deletions pepys_admin/maintenance/column_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,11 @@ def create_column_data(data_store, table_object, set_percentage=None):
.all()
)
nationality_names = [nationality.name for nationality in all_nationalities]
nationality_ids = [
str(nationality.nationality_id) for nationality in all_nationalities
]
details["values"] = nationality_names
details["ids"] = nationality_ids
elif details["system_name"] == "privacy_name":
# Get privacy names as a special case, as we want to sort by level
all_privacies = (
Expand All @@ -155,15 +159,26 @@ def create_column_data(data_store, table_object, set_percentage=None):
.all()
)
privacy_names = [priv.name for priv in all_privacies]
privacy_ids = [str(priv.privacy_id) for priv in all_privacies]
details["values"] = privacy_names
details["ids"] = privacy_ids
else:
# For all other columns, no special processing is needed
all_records = data_store.session.query(ap_obj.target_class).all()
values = [
str_if_not_none(getattr(record, ap_obj.value_attr))
# Sort the values and IDs lists together, so that ids[x] is still the
# ID for values[x]
values_and_ids = [
(
str_if_not_none(getattr(record, ap_obj.value_attr)),
str(getattr(record, get_primary_key_for_table(ap_obj.target_class))),
)
for record in all_records
]
details["values"] = sorted(remove_duplicates_and_nones(values))
sorted_values_and_ids = sorted(values_and_ids, key=lambda x: x[0])
sorted_values = [item[0] for item in sorted_values_and_ids]
sorted_ids = [item[1] for item in sorted_values_and_ids]
details["values"] = sorted_values
details["ids"] = sorted_ids

column_data[get_display_name(ap_name)] = details

Expand All @@ -176,3 +191,38 @@ def create_column_data(data_store, table_object, set_percentage=None):
set_percentage(100)

return column_data


def column_data_to_edit_data(column_data, table_object):
"""
Converts the original column_data dictionary into a dictionary of data
for configuring the editing UI.
:param column_data: column_data dictionary, as provided by create_column_data and used in FilterWidget
:type column_data: dict
:param table_object: SQLAlchemy Table object, such as Platform or Nationality
:type table_object: SQLAlchemy Table object
:return: Dictionary giving structure of columns for editing GUI
:rtype: dict
"""
edit_data = {}

for key, value in column_data.items():
if key == "created date":
# Don't allow to edit the created date
continue
if value["type"] == "id":
# Don't allow to edit ID columns
continue
table_attr = getattr(table_object, value["system_name"])
if not isinstance(
table_attr, sqlalchemy.ext.associationproxy.ColumnAssociationProxyInstance
):
if "values" in value:
# If this isn't a foreign keyed column then don't provide a dropdown list
# as we only want dropdown lists for foreign keyed columns
del value["values"]

edit_data[key] = value

return edit_data
93 changes: 93 additions & 0 deletions pepys_admin/maintenance/dialogs/edit_dialog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
from asyncio import Future

from prompt_toolkit.layout.containers import HSplit, VSplit
from prompt_toolkit.layout.dimension import D
from prompt_toolkit.widgets import Button, Label
from prompt_toolkit.widgets.dialogs import Dialog

from pepys_admin.maintenance.column_data import column_data_to_edit_data
from pepys_admin.maintenance.widgets.entry_display_widget import EntryDisplayWidget
from pepys_admin.maintenance.widgets.entry_edit_widget import EntryEditWidget


class EditDialog:
def __init__(self, column_data, table_object, entries):
"""
A dialog for editing object values.
:param column_data: The column_data dictionary for the given table object
:type column_data: dict
:param table_object: SQLAlchemy Table object, such as Platform, Sensor or Nationality
:type table_object: SQLAlchemy Table Object
:param entries: List of SQLAlchemy objects representing the objects to be edited
:type entries: list
"""
self.future = Future()

ok_button = Button(text="OK", handler=self.handle_ok)
cancel_button = Button(text="Cancel", handler=self.handle_cancel)

# Convert the column_data into the structure we need for editing the data
# This removes un-needed columns, and un-needed values lists
edit_data = column_data_to_edit_data(column_data, table_object)

self.entry_edit_widget = EntryEditWidget(edit_data)
self.entry_display_widget = EntryDisplayWidget(edit_data, entries)

lh_side = HSplit(
[Label("Current values:", style="class:table-title"), self.entry_display_widget],
padding=1,
)
rh_side = HSplit(
[Label("New values:", style="class:table-title"), self.entry_edit_widget], padding=1
)

instructions = Label(
"Press TAB to move between fields. Only non-empty new values will replace current values",
style="class:instruction-text-dark",
)

if len(entries) < 10:
display_strs = []
for entry in entries:
display_str = " - ".join(
[
str(getattr(entry, field_name))
for field_name in entry._default_preview_fields
]
)
display_strs.append(display_str)
selected_items_text = "\n".join(display_strs)
else:
selected_items_text = f"{len(entries)} items selected"
selected_items_ui = HSplit(
[Label("Selected items: ", style="class:table-title"), Label(selected_items_text)]
)
self.body = HSplit(
[instructions, selected_items_ui, VSplit([lh_side, rh_side], padding=2)], padding=1
)

self.dialog = Dialog(
title="Edit item(s)",
body=self.body,
buttons=[ok_button, cancel_button],
width=D(preferred=100),
modal=True,
)

# Get the keybindings for the dialog and add a binding for Esc
# to close the dialog
dialog_kb = self.dialog.container.container.content.key_bindings

@dialog_kb.add("escape")
def _(event) -> None:
self.handle_cancel()

def handle_ok(self):
self.future.set_result(self.entry_edit_widget.output)

def handle_cancel(self):
self.future.set_result(None)

def __pt_container__(self):
return self.dialog
Loading

0 comments on commit 6511b69

Please sign in to comment.