From e1b4e3944aee4d4284fe5fbe89134c8fda2f881f Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Wed, 11 Dec 2024 11:29:51 +0000 Subject: [PATCH 01/14] Add external notebook for managing codes --- code_setup.ipynb | 156 ++++++++++++++++++++++++++++++ home/code_setup.py | 234 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 390 insertions(+) create mode 100644 code_setup.ipynb create mode 100644 home/code_setup.py diff --git a/code_setup.ipynb b/code_setup.ipynb new file mode 100644 index 0000000..6cfbb33 --- /dev/null +++ b/code_setup.ipynb @@ -0,0 +1,156 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from aiida import load_profile\n", + "\n", + "load_profile();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# AiiDAlab Code Setup\n", + "\n", + "This page provides an interface for creating AiiDA `Code` references to executable on \n", + "an AiiDA `Computer` node." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "html" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%%html\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create new code" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as ipw\n", + "from aiidalab_widgets_base.computational_resources import _ResourceSetupBaseWidget\n", + "from aiidalab_widgets_base.utils import StatusHTML\n", + "\n", + "setup_message = StatusHTML(clear_after=15)\n", + "\n", + "widget = _ResourceSetupBaseWidget()\n", + "ipw.dlink(\n", + " (widget, \"message\"),\n", + " (setup_message, \"message\"),\n", + ")\n", + "\n", + "ipw.VBox(\n", + " [\n", + " widget,\n", + " setup_message,\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Available codes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from home.code_setup import create_paginated_table, fetch_code_data\n", + "\n", + "df = fetch_code_data()\n", + "paginated_table = create_paginated_table(df)\n", + "display(paginated_table)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.13" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/home/code_setup.py b/home/code_setup.py new file mode 100644 index 0000000..13576a8 --- /dev/null +++ b/home/code_setup.py @@ -0,0 +1,234 @@ +from __future__ import annotations + +import ipywidgets as ipw +import pandas as pd +from aiida import orm +from IPython.display import display + +CONFIG = { + "column_widths": { + "Full Label": "25%", + "Executable Path": "70%", + "Hide": "5%", + }, + "cell_style": {"padding": "2px 5px"}, + "rows_per_page": 10, +} + + +def fetch_code_data(): + """Fetch AiiDA code instances and format them into a DataFrame.""" + codes = orm.Code.collection.all() + data = [] + for code in codes: + computer = code.computer + data.append( + { + "Full Label": f"{code.label}@{computer.label}", + "Executable Path": code.get_executable(), + "Hide": code.is_hidden, + } + ) + return pd.DataFrame(data) + + +def create_header(): + """Create the header row of the table.""" + header_row = ipw.HBox( + children=[ + ipw.HTML( + value=f"
{col}
", + layout=ipw.Layout(width=CONFIG["column_widths"][col]), + ) + for col in CONFIG["column_widths"] + ], + ) + header_row.add_class("header-tr") + return header_row + + +def create_row(row, on_checkbox_change): + """Create a single row in the table.""" + hide_checkbox = ipw.Checkbox( + value=row["Hide"], + indent=False, + tooltip="Check to hide this code in code selection widgets", + layout=ipw.Layout(width="fit-content", margin="2px 2px 2px 15px"), + ) + hide_checkbox.full_label = row["Full Label"] + hide_checkbox.observe(on_checkbox_change, names="value") + table_row = ipw.HBox( + children=[ + ipw.Label( + value=str(row["Full Label"]), + layout=ipw.Layout( + width=CONFIG["column_widths"]["Full Label"], + **CONFIG["cell_style"], + ), + ), + ipw.Label( + value=str(row["Executable Path"]), + layout=ipw.Layout( + width=CONFIG["column_widths"]["Executable Path"], + **CONFIG["cell_style"], + ), + ), + ipw.Box( + children=[hide_checkbox], + layout=ipw.Layout( + width=CONFIG["column_widths"]["Hide"], + justify_content="center", + **CONFIG["cell_style"], + ), + ), + ], + layout=ipw.Layout(transition="background-color 0.3s ease", padding="5px"), + ) + table_row.add_class("tr") + return table_row + + +def render_table(df, page, on_checkbox_change): + """Render the table for the specified page.""" + start_idx = (page - 1) * CONFIG["rows_per_page"] + end_idx = start_idx + CONFIG["rows_per_page"] + page_df = df.iloc[start_idx:end_idx] + header = create_header() + rows = [create_row(row, on_checkbox_change) for _, row in page_df.iterrows()] + return ipw.VBox( + children=[header, *rows], + layout=ipw.Layout(width="100%"), + ) + + +def create_paginated_table(df): + """Create a paginated table with interactive controls.""" + + def on_page_change(_): + render_table_with_filters() + + def on_show_active(_): + render_table_with_filters() + + def on_show_all_click(_): + unhide_all_codes() + render_table_with_filters() + + def on_checkbox_change(change): + full_label = change.owner.full_label + is_hidden = change.new + update_code_visibility(full_label, is_hidden) + if show_active.value: + render_table_with_filters() + + def on_search_change(_): + render_table_with_filters() + + def update_code_visibility(full_label, is_hidden): + """Update the visibility of a `Code` node.""" + try: + code = orm.load_code(full_label) + code.is_hidden = is_hidden + df.loc[df["Full Label"] == full_label, "Hide"] = is_hidden + except Exception: + df.drop(df[df["Full Label"] == full_label].index, inplace=True) + render_table_with_filters() + + def unhide_all_codes(): + for code in orm.Code.collection.all(): + full_label = f"{code.label}@{code.computer.label}" + update_code_visibility(full_label, False) + + def render_table_with_filters(): + """Render the table with current filters applied.""" + visible_df = df.copy() + + if show_active.value: + visible_df = visible_df[~visible_df["Hide"]] + + query = search_box.value.strip().lower() + if query: + visible_df = visible_df[ + visible_df["Full Label"] + .astype(str) + .str.lower() + .str.contains(query, na=False) + | visible_df["Executable Path"] + .astype(str) + .str.lower() + .str.contains(query, na=False) + ] + + visible_df.reset_index(drop=True, inplace=True) + + total_pages = (len(visible_df) + CONFIG["rows_per_page"] - 1) // CONFIG[ + "rows_per_page" + ] + current_page.max = max(total_pages, 1) + page = min(current_page.value, total_pages) + current_page.value = page + + with table_output: + table_output.clear_output(wait=True) + display(render_table(visible_df, page, on_checkbox_change)) + + table_output = ipw.Output() + + total_pages = (len(df) + CONFIG["rows_per_page"] - 1) // CONFIG["rows_per_page"] + current_page = ipw.BoundedIntText( + value=1, + min=1, + max=total_pages, + step=1, + description="Page:", + ) + current_page.observe( + on_page_change, + "value", + ) + + show_active = ipw.Checkbox( + value=False, + description="Show active codes only", + ) + show_active.observe( + on_show_active, + "value", + ) + + show_all_button = ipw.Button( + description="Show All", + button_style="primary", + layout=ipw.Layout(margin="0 0 0 auto"), + ) + show_all_button.on_click(on_show_all_click) + + search_box = ipw.Text( + description="Search:", + placeholder="Filter by label or path...", + layout=ipw.Layout(width="50%"), + ) + search_box.observe( + on_search_change, + "value", + ) + + render_table_with_filters() + + return ipw.VBox( + children=[ + ipw.HBox( + children=[ + current_page, + show_active, + ], + ), + ipw.HBox( + children=[ + search_box, + show_all_button, + ], + ), + table_output, + ], + ) From 78ddccdd068a6bea722203ed5ed5d2ea53f73768 Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Wed, 11 Dec 2024 12:29:50 +0000 Subject: [PATCH 02/14] Refresh available codes on code creation --- code_setup.ipynb | 42 ++++++++++++++++++------------------------ home/code_setup.py | 4 +--- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/code_setup.ipynb b/code_setup.ipynb index 6cfbb33..859767c 100644 --- a/code_setup.ipynb +++ b/code_setup.ipynb @@ -29,25 +29,7 @@ "languageId": "html" } }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "%%html\n", "\n", @@ -97,7 +79,7 @@ ")\n", "\n", "ipw.VBox(\n", - " [\n", + " children=[\n", " widget,\n", " setup_message,\n", " ]\n", @@ -119,9 +101,17 @@ "source": [ "from home.code_setup import create_paginated_table, fetch_code_data\n", "\n", - "df = fetch_code_data()\n", - "paginated_table = create_paginated_table(df)\n", - "display(paginated_table)" + "output = ipw.Output()\n", + "\n", + "def render():\n", + " df = fetch_code_data()\n", + " paginated_table = create_paginated_table(df)\n", + " output.clear_output()\n", + " with output:\n", + " display(paginated_table)\n", + "\n", + "display(output)\n", + "render()" ] }, { @@ -129,7 +119,11 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "widget.quick_setup_button.on_click(\n", + " lambda _: render()\n", + ")" + ] } ], "metadata": { diff --git a/home/code_setup.py b/home/code_setup.py index 13576a8..16199fd 100644 --- a/home/code_setup.py +++ b/home/code_setup.py @@ -115,9 +115,7 @@ def on_show_all_click(_): render_table_with_filters() def on_checkbox_change(change): - full_label = change.owner.full_label - is_hidden = change.new - update_code_visibility(full_label, is_hidden) + update_code_visibility(change.owner.full_label, change.new) if show_active.value: render_table_with_filters() From 102043c59242ef8a2547fdd2a5223891fa4e8a7a Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Wed, 11 Dec 2024 13:47:08 +0000 Subject: [PATCH 03/14] Improve pagination --- code_setup.ipynb | 6 ++--- home/code_setup.py | 57 +++++++++++++++++++++++++--------------------- 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/code_setup.ipynb b/code_setup.ipynb index 859767c..1252a4a 100644 --- a/code_setup.ipynb +++ b/code_setup.ipynb @@ -103,6 +103,7 @@ "\n", "output = ipw.Output()\n", "\n", + "\n", "def render():\n", " df = fetch_code_data()\n", " paginated_table = create_paginated_table(df)\n", @@ -110,6 +111,7 @@ " with output:\n", " display(paginated_table)\n", "\n", + "\n", "display(output)\n", "render()" ] @@ -120,9 +122,7 @@ "metadata": {}, "outputs": [], "source": [ - "widget.quick_setup_button.on_click(\n", - " lambda _: render()\n", - ")" + "widget.quick_setup_button.on_click(lambda _: render())" ] } ], diff --git a/home/code_setup.py b/home/code_setup.py index 16199fd..4bccc1c 100644 --- a/home/code_setup.py +++ b/home/code_setup.py @@ -104,7 +104,10 @@ def render_table(df, page, on_checkbox_change): def create_paginated_table(df): """Create a paginated table with interactive controls.""" - def on_page_change(_): + current_page = ipw.IntText(value=1) + + def on_page_change(button): + current_page.value = int(button.description) render_table_with_filters() def on_show_active(_): @@ -137,6 +140,19 @@ def unhide_all_codes(): full_label = f"{code.label}@{code.computer.label}" update_code_visibility(full_label, False) + def generate_page_buttons(total_pages): + def create_page_button(page_num): + button = ipw.Button( + description=str(page_num), + button_style="primary" if page_num == current_page.value else "", + layout=ipw.Layout(width="40px", height="36px"), + page_number=page_num, + ) + button.on_click(on_page_change) + return button + + return [create_page_button(i) for i in range(1, total_pages + 1)] + def render_table_with_filters(): """Render the table with current filters applied.""" visible_df = df.copy() @@ -170,20 +186,9 @@ def render_table_with_filters(): table_output.clear_output(wait=True) display(render_table(visible_df, page, on_checkbox_change)) - table_output = ipw.Output() + pagination.children = generate_page_buttons(total_pages) - total_pages = (len(df) + CONFIG["rows_per_page"] - 1) // CONFIG["rows_per_page"] - current_page = ipw.BoundedIntText( - value=1, - min=1, - max=total_pages, - step=1, - description="Page:", - ) - current_page.observe( - on_page_change, - "value", - ) + table_output = ipw.Output() show_active = ipw.Checkbox( value=False, @@ -211,22 +216,22 @@ def render_table_with_filters(): "value", ) + filters = ipw.HBox( + children=[ + search_box, + show_active, + show_all_button, + ], + ) + + pagination = ipw.HBox(layout=ipw.Layout(justify_content="center")) + render_table_with_filters() return ipw.VBox( children=[ - ipw.HBox( - children=[ - current_page, - show_active, - ], - ), - ipw.HBox( - children=[ - search_box, - show_all_button, - ], - ), + filters, table_output, + pagination, ], ) From 008a11f2ff041c6602ea25a07708e6c22091cf10 Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Thu, 12 Dec 2024 06:57:20 +0000 Subject: [PATCH 04/14] Remove pandas dependency --- code_setup.ipynb | 4 +-- home/code_setup.py | 84 ++++++++++++++++++++++++---------------------- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/code_setup.ipynb b/code_setup.ipynb index 1252a4a..1d7947a 100644 --- a/code_setup.ipynb +++ b/code_setup.ipynb @@ -105,8 +105,8 @@ "\n", "\n", "def render():\n", - " df = fetch_code_data()\n", - " paginated_table = create_paginated_table(df)\n", + " data = fetch_code_data()\n", + " paginated_table = create_paginated_table(data)\n", " output.clear_output()\n", " with output:\n", " display(paginated_table)\n", diff --git a/home/code_setup.py b/home/code_setup.py index 4bccc1c..d34b49e 100644 --- a/home/code_setup.py +++ b/home/code_setup.py @@ -1,8 +1,8 @@ from __future__ import annotations import ipywidgets as ipw -import pandas as pd from aiida import orm +from aiida.common.exceptions import NotExistent from IPython.display import display CONFIG = { @@ -17,19 +17,16 @@ def fetch_code_data(): - """Fetch AiiDA code instances and format them into a DataFrame.""" - codes = orm.Code.collection.all() - data = [] - for code in codes: - computer = code.computer - data.append( - { - "Full Label": f"{code.label}@{computer.label}", - "Executable Path": code.get_executable(), - "Hide": code.is_hidden, - } - ) - return pd.DataFrame(data) + """Fetch AiiDA code instances and format them into a list of dictionaries.""" + codes: list[orm.Code] = orm.Code.collection.all() + return [ + { + "Full Label": f"{code.label}@{code.computer.label}", + "Executable Path": code.get_executable().as_posix(), + "Hide": code.is_hidden, + } + for code in codes + ] def create_header(): @@ -88,20 +85,24 @@ def create_row(row, on_checkbox_change): return table_row -def render_table(df, page, on_checkbox_change): - """Render the table for the specified page.""" +def get_page_data(data, page): start_idx = (page - 1) * CONFIG["rows_per_page"] end_idx = start_idx + CONFIG["rows_per_page"] - page_df = df.iloc[start_idx:end_idx] + return data[start_idx:end_idx] + + +def render_table(data, page, on_checkbox_change): + """Render the table for the specified page.""" + page_data = get_page_data(data, page) header = create_header() - rows = [create_row(row, on_checkbox_change) for _, row in page_df.iterrows()] + rows = [create_row(row, on_checkbox_change) for row in page_data] return ipw.VBox( children=[header, *rows], layout=ipw.Layout(width="100%"), ) -def create_paginated_table(df): +def create_paginated_table(data: list[dict]): """Create a paginated table with interactive controls.""" current_page = ipw.IntText(value=1) @@ -127,12 +128,14 @@ def on_search_change(_): def update_code_visibility(full_label, is_hidden): """Update the visibility of a `Code` node.""" + code_row = next(filter(lambda row: row["Full Label"] == full_label, data)) try: code = orm.load_code(full_label) code.is_hidden = is_hidden - df.loc[df["Full Label"] == full_label, "Hide"] = is_hidden - except Exception: - df.drop(df[df["Full Label"] == full_label].index, inplace=True) + code_row["Hide"] = is_hidden + except NotExistent: + row_index = data.index(code_row) + data.pop(row_index) render_table_with_filters() def unhide_all_codes(): @@ -153,29 +156,30 @@ def create_page_button(page_num): return [create_page_button(i) for i in range(1, total_pages + 1)] - def render_table_with_filters(): - """Render the table with current filters applied.""" - visible_df = df.copy() + def filter_data(data, show_active, query): + """Filter the data based on active status and search query.""" + filtered = data[:] - if show_active.value: - visible_df = visible_df[~visible_df["Hide"]] + if show_active: + filtered = [row for row in data if not row["Hide"]] - query = search_box.value.strip().lower() if query: - visible_df = visible_df[ - visible_df["Full Label"] - .astype(str) - .str.lower() - .str.contains(query, na=False) - | visible_df["Executable Path"] - .astype(str) - .str.lower() - .str.contains(query, na=False) + query = query.strip().lower() + filtered = [ + row + for row in data + if query in row["Full Label"].lower() + or query in row["Executable Path"].lower() ] - visible_df.reset_index(drop=True, inplace=True) + return filtered + + def render_table_with_filters(): + """Render the table with current filters applied.""" + visible_data = data[:] + visible_data = filter_data(visible_data, show_active.value, search_box.value) - total_pages = (len(visible_df) + CONFIG["rows_per_page"] - 1) // CONFIG[ + total_pages = (len(visible_data) + CONFIG["rows_per_page"] - 1) // CONFIG[ "rows_per_page" ] current_page.max = max(total_pages, 1) @@ -184,7 +188,7 @@ def render_table_with_filters(): with table_output: table_output.clear_output(wait=True) - display(render_table(visible_df, page, on_checkbox_change)) + display(render_table(visible_data, page, on_checkbox_change)) pagination.children = generate_page_buttons(total_pages) From b6e4c5c1d6afa0b32407b10be96a65a54f6a978c Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Thu, 12 Dec 2024 08:04:31 +0000 Subject: [PATCH 05/14] Fix table reset from null-result searches --- home/code_setup.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/home/code_setup.py b/home/code_setup.py index d34b49e..b9750ce 100644 --- a/home/code_setup.py +++ b/home/code_setup.py @@ -176,19 +176,20 @@ def filter_data(data, show_active, query): def render_table_with_filters(): """Render the table with current filters applied.""" - visible_data = data[:] - visible_data = filter_data(visible_data, show_active.value, search_box.value) + visible_data = filter_data(data, show_active.value, search_box.value) - total_pages = (len(visible_data) + CONFIG["rows_per_page"] - 1) // CONFIG[ - "rows_per_page" - ] + rpg = CONFIG["rows_per_page"] + total_pages = (len(visible_data) + rpg - 1) // rpg current_page.max = max(total_pages, 1) - page = min(current_page.value, total_pages) - current_page.value = page + + if total_pages == 0: + current_page.value = 1 + else: + current_page.value = min(current_page.value, total_pages) with table_output: table_output.clear_output(wait=True) - display(render_table(visible_data, page, on_checkbox_change)) + display(render_table(visible_data, current_page.value, on_checkbox_change)) pagination.children = generate_page_buttons(total_pages) From 8fd2256e8096e77360459ae296ab17ac63b6ae34 Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Fri, 13 Dec 2024 09:10:36 +0000 Subject: [PATCH 06/14] Simplify new code setup observation --- code_setup.ipynb | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/code_setup.ipynb b/code_setup.ipynb index 1d7947a..06c347e 100644 --- a/code_setup.ipynb +++ b/code_setup.ipynb @@ -17,8 +17,7 @@ "source": [ "# AiiDAlab Code Setup\n", "\n", - "This page provides an interface for creating AiiDA `Code` references to executable on \n", - "an AiiDA `Computer` node." + "This page provides an interface for creating AiiDA codes on local or remote machines." ] }, { @@ -122,7 +121,15 @@ "metadata": {}, "outputs": [], "source": [ - "widget.quick_setup_button.on_click(lambda _: render())" + "def on_code_setup_message_change(change):\n", + " if \"created\" in change[\"new\"]:\n", + " render()\n", + "\n", + "\n", + "widget.aiida_code_setup.observe(\n", + " on_code_setup_message_change,\n", + " \"message\",\n", + ")" ] } ], From 3ff3c40a8259a105fb9687d6da57569edb190a9b Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Fri, 20 Dec 2024 06:01:14 +0000 Subject: [PATCH 07/14] Change only active filter to only hidden --- home/code_setup.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/home/code_setup.py b/home/code_setup.py index b9750ce..2eb7f7b 100644 --- a/home/code_setup.py +++ b/home/code_setup.py @@ -111,7 +111,7 @@ def on_page_change(button): current_page.value = int(button.description) render_table_with_filters() - def on_show_active(_): + def on_show_hidden_click(_): render_table_with_filters() def on_show_all_click(_): @@ -120,7 +120,7 @@ def on_show_all_click(_): def on_checkbox_change(change): update_code_visibility(change.owner.full_label, change.new) - if show_active.value: + if show_hidden_only_button.value: render_table_with_filters() def on_search_change(_): @@ -156,12 +156,12 @@ def create_page_button(page_num): return [create_page_button(i) for i in range(1, total_pages + 1)] - def filter_data(data, show_active, query): - """Filter the data based on active status and search query.""" + def filter_data(data, show_hidden_only, query): + """Filter the data based on current control values.""" filtered = data[:] - if show_active: - filtered = [row for row in data if not row["Hide"]] + if show_hidden_only: + filtered = [row for row in data if row["Hide"]] if query: query = query.strip().lower() @@ -176,7 +176,11 @@ def filter_data(data, show_active, query): def render_table_with_filters(): """Render the table with current filters applied.""" - visible_data = filter_data(data, show_active.value, search_box.value) + visible_data = filter_data( + data, + show_hidden_only_button.value, + search_box.value, + ) rpg = CONFIG["rows_per_page"] total_pages = (len(visible_data) + rpg - 1) // rpg @@ -195,12 +199,12 @@ def render_table_with_filters(): table_output = ipw.Output() - show_active = ipw.Checkbox( + show_hidden_only_button = ipw.Checkbox( value=False, - description="Show active codes only", + description="Show hidden codes only", ) - show_active.observe( - on_show_active, + show_hidden_only_button.observe( + on_show_hidden_click, "value", ) @@ -224,7 +228,7 @@ def render_table_with_filters(): filters = ipw.HBox( children=[ search_box, - show_active, + show_hidden_only_button, show_all_button, ], ) From a37c2651da182d5d5a439d4896ec31e9e7ae23f9 Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Fri, 20 Dec 2024 06:05:19 +0000 Subject: [PATCH 08/14] Fix filter overriding --- home/code_setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/home/code_setup.py b/home/code_setup.py index 2eb7f7b..5070aff 100644 --- a/home/code_setup.py +++ b/home/code_setup.py @@ -167,7 +167,7 @@ def filter_data(data, show_hidden_only, query): query = query.strip().lower() filtered = [ row - for row in data + for row in filtered if query in row["Full Label"].lower() or query in row["Executable Path"].lower() ] From 168da67ae0fae531a855797858451efd30c92924 Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Fri, 20 Dec 2024 06:48:53 +0000 Subject: [PATCH 09/14] Add tooltip to show all codes button --- home/code_setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/home/code_setup.py b/home/code_setup.py index 5070aff..4838d10 100644 --- a/home/code_setup.py +++ b/home/code_setup.py @@ -210,6 +210,7 @@ def render_table_with_filters(): show_all_button = ipw.Button( description="Show All", + tooltip="Unhide all hidden codes", button_style="primary", layout=ipw.Layout(margin="0 0 0 auto"), ) From 21843ac0659e1219c50e3bed4a5c9c4f4c0189a7 Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Fri, 20 Dec 2024 06:54:46 +0000 Subject: [PATCH 10/14] Improve control layout --- home/code_setup.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/home/code_setup.py b/home/code_setup.py index 4838d10..299ad54 100644 --- a/home/code_setup.py +++ b/home/code_setup.py @@ -8,8 +8,8 @@ CONFIG = { "column_widths": { "Full Label": "25%", - "Executable Path": "70%", - "Hide": "5%", + "Executable Path": "65%", + "Hide": "10%", }, "cell_style": {"padding": "2px 5px"}, "rows_per_page": 10, @@ -199,9 +199,22 @@ def render_table_with_filters(): table_output = ipw.Output() + search_box = ipw.Text( + description="Search:", + placeholder="Filter by label or path...", + layout=ipw.Layout(width="50%"), + style={"description_width": "initial"}, + ) + search_box.observe( + on_search_change, + "value", + ) + show_hidden_only_button = ipw.Checkbox( value=False, + indent=False, description="Show hidden codes only", + layout=ipw.Layout(width="fit-content", flex="1", margin="2px 10px"), ) show_hidden_only_button.observe( on_show_hidden_click, @@ -209,29 +222,20 @@ def render_table_with_filters(): ) show_all_button = ipw.Button( - description="Show All", + description="Unhide All", tooltip="Unhide all hidden codes", button_style="primary", - layout=ipw.Layout(margin="0 0 0 auto"), + layout=ipw.Layout(margin="0", width="105px"), ) show_all_button.on_click(on_show_all_click) - search_box = ipw.Text( - description="Search:", - placeholder="Filter by label or path...", - layout=ipw.Layout(width="50%"), - ) - search_box.observe( - on_search_change, - "value", - ) - filters = ipw.HBox( children=[ search_box, show_hidden_only_button, show_all_button, ], + layout=ipw.Layout(padding="0 8px", align_items="center"), ) pagination = ipw.HBox(layout=ipw.Layout(justify_content="center")) From 8fb61a349fd4b7cca9daca03fe30d4647fc7d091 Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Tue, 24 Dec 2024 09:37:40 +0000 Subject: [PATCH 11/14] Adjust capitalization --- code_setup.ipynb | 2 +- home/code_setup.py | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/code_setup.ipynb b/code_setup.ipynb index 06c347e..6f0d425 100644 --- a/code_setup.ipynb +++ b/code_setup.ipynb @@ -15,7 +15,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# AiiDAlab Code Setup\n", + "# AiiDAlab code setup\n", "\n", "This page provides an interface for creating AiiDA codes on local or remote machines." ] diff --git a/home/code_setup.py b/home/code_setup.py index 299ad54..782c5c2 100644 --- a/home/code_setup.py +++ b/home/code_setup.py @@ -7,8 +7,8 @@ CONFIG = { "column_widths": { - "Full Label": "25%", - "Executable Path": "65%", + "Full label": "25%", + "Executable path": "65%", "Hide": "10%", }, "cell_style": {"padding": "2px 5px"}, @@ -21,8 +21,8 @@ def fetch_code_data(): codes: list[orm.Code] = orm.Code.collection.all() return [ { - "Full Label": f"{code.label}@{code.computer.label}", - "Executable Path": code.get_executable().as_posix(), + "Full label": f"{code.label}@{code.computer.label}", + "Executable path": code.get_executable().as_posix(), "Hide": code.is_hidden, } for code in codes @@ -52,21 +52,21 @@ def create_row(row, on_checkbox_change): tooltip="Check to hide this code in code selection widgets", layout=ipw.Layout(width="fit-content", margin="2px 2px 2px 15px"), ) - hide_checkbox.full_label = row["Full Label"] + hide_checkbox.full_label = row["Full label"] hide_checkbox.observe(on_checkbox_change, names="value") table_row = ipw.HBox( children=[ ipw.Label( - value=str(row["Full Label"]), + value=str(row["Full label"]), layout=ipw.Layout( - width=CONFIG["column_widths"]["Full Label"], + width=CONFIG["column_widths"]["Full label"], **CONFIG["cell_style"], ), ), ipw.Label( - value=str(row["Executable Path"]), + value=str(row["Executable path"]), layout=ipw.Layout( - width=CONFIG["column_widths"]["Executable Path"], + width=CONFIG["column_widths"]["Executable path"], **CONFIG["cell_style"], ), ), @@ -128,7 +128,7 @@ def on_search_change(_): def update_code_visibility(full_label, is_hidden): """Update the visibility of a `Code` node.""" - code_row = next(filter(lambda row: row["Full Label"] == full_label, data)) + code_row = next(filter(lambda row: row["Full label"] == full_label, data)) try: code = orm.load_code(full_label) code.is_hidden = is_hidden @@ -168,8 +168,8 @@ def filter_data(data, show_hidden_only, query): filtered = [ row for row in filtered - if query in row["Full Label"].lower() - or query in row["Executable Path"].lower() + if query in row["Full label"].lower() + or query in row["Executable path"].lower() ] return filtered @@ -222,7 +222,7 @@ def render_table_with_filters(): ) show_all_button = ipw.Button( - description="Unhide All", + description="Unhide all", tooltip="Unhide all hidden codes", button_style="primary", layout=ipw.Layout(margin="0", width="105px"), From d277602b2df9d753ed0008337a503f44ea06822b Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Wed, 8 Jan 2025 15:41:45 +0000 Subject: [PATCH 12/14] Update use of AWB class w.r.t changes in AWB --- code_setup.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code_setup.ipynb b/code_setup.ipynb index 6f0d425..1e9825e 100644 --- a/code_setup.ipynb +++ b/code_setup.ipynb @@ -66,12 +66,12 @@ "outputs": [], "source": [ "import ipywidgets as ipw\n", - "from aiidalab_widgets_base.computational_resources import _ResourceSetupBaseWidget\n", + "from aiidalab_widgets_base.computational_resources import ResourceSetupBaseWidget\n", "from aiidalab_widgets_base.utils import StatusHTML\n", "\n", "setup_message = StatusHTML(clear_after=15)\n", "\n", - "widget = _ResourceSetupBaseWidget()\n", + "widget = ResourceSetupBaseWidget()\n", "ipw.dlink(\n", " (widget, \"message\"),\n", " (setup_message, \"message\"),\n", From c892ea23e4703848bcd7647d1c2620038ba5c96c Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Wed, 8 Jan 2025 15:42:20 +0000 Subject: [PATCH 13/14] Add refresh button --- code_setup.ipynb | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/code_setup.ipynb b/code_setup.ipynb index 1e9825e..3dd14dc 100644 --- a/code_setup.ipynb +++ b/code_setup.ipynb @@ -111,7 +111,24 @@ " display(paginated_table)\n", "\n", "\n", - "display(output)\n", + "refresh_button = ipw.Button(\n", + " description=\"Refresh codes\",\n", + " button_style=\"primary\",\n", + " icon=\"refresh\",\n", + " tooltip=\"Refresh the list of codes\",\n", + " layout=ipw.Layout(width=\"fit-content\"),\n", + ")\n", + "refresh_button.on_click(lambda _: render())\n", + "\n", + "display(\n", + " ipw.VBox(\n", + " children=[\n", + " refresh_button,\n", + " output,\n", + " ]\n", + " )\n", + ")\n", + "\n", "render()" ] }, From 67d18dc906519f0a992f4f13ffcca043af013d99 Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Wed, 8 Jan 2025 16:21:26 +0000 Subject: [PATCH 14/14] Add in-notebook instructions --- code_setup.ipynb | 50 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/code_setup.ipynb b/code_setup.ipynb index 3dd14dc..830c34c 100644 --- a/code_setup.ipynb +++ b/code_setup.ipynb @@ -11,15 +11,6 @@ "load_profile();" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# AiiDAlab code setup\n", - "\n", - "This page provides an interface for creating AiiDA codes on local or remote machines." - ] - }, { "cell_type": "code", "execution_count": null, @@ -49,14 +40,45 @@ " .tr:hover {\n", " background-color: #f5f5f5;\n", " }\n", - "" + " hr {\n", + " border-top: 1px solid #d3d3d3;\n", + " }\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# AiiDAlab code setup\n", + "\n", + "This page provides an interface for creating and managing AiiDA codes on local or remote machines.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Create new code" + "
\n", + "\n", + "## Create new code\n", + "\n", + "To create a new AiiDA code for use in a calculation, follow these steps:\n", + "\n", + "#### Quick setup (using [AiiDA resource registry](https://github.com/aiidateam/aiida-resource-registry) recipes)\n", + "\n", + "1. Select the domain of your remote machine\n", + "2. Select the computer recipe\n", + "3. Select the code recipe\n", + "4. Complete the remaining fields (these depend on the combination selected in steps 1-3)\n", + "5. Click **Quick setup**\n", + "\n", + "#### Manual setup (create new resources from scratch)\n", + "\n", + "1. Tick the checkbox above the **Quick setup** button\n", + "2. Follow along with the provided instructions\n", + "\n", + "Newly created codes will be available in the **Available codes** section, as well as in code selectors provided in various apps.\n" ] }, { @@ -89,7 +111,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Available codes" + "
\n", + "\n", + "## Available codes\n", + "\n", + "Below are the available codes in the AiiDA database. Use the controls to filter the list by label, executable path, and visibility. You can mark individual codes as _hidden_ to exclude them from code selectors used in apps (note that some apps may enforce the inclusion of hidden codes). Click **Unhide all** to include all codes. If codes are deleted from the database, click **Refresh codes** to reflect the change in the list.\n" ] }, {