Skip to content

Commit

Permalink
Merge branch 'main' into docs_job_search
Browse files Browse the repository at this point in the history
  • Loading branch information
superstar54 authored May 2, 2024
2 parents 13d28b4 + 7650cca commit 2c33e2e
Show file tree
Hide file tree
Showing 11 changed files with 670 additions and 417 deletions.
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

# -- Project information -----------------------------------------------------

version = "v24.04.0rc2"
version = "v24.04.0rc3"
release = f"{version}-dev"
project = "Quantum ESPRESSO App"
copyright_first_year = "2023"
Expand Down
62 changes: 49 additions & 13 deletions plugin_list.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,14 @@
"outputs": [],
"source": [
"import subprocess\n",
"import sys\n",
"from threading import Thread\n",
"\n",
"import ipywidgets as ipw\n",
"from IPython.display import display\n",
"\n",
"\n",
"\n",
"def is_package_installed(package_name):\n",
" import importlib\n",
" package_name = package_name.replace('-', '_')\n",
Expand Down Expand Up @@ -83,12 +85,10 @@
" thread.join() # Wait for the thread to finish\n",
"\n",
" if process.returncode == 0 and action == \"install\":\n",
" output_widget.value += \"\"\"<div style=\"background-color: #3B3B3B; color: #008000;\">Command executed successfully.</div>\"\"\"\n",
" install_btn.disabled = True\n",
" remove_btn.disabled = False\n",
" return True\n",
" elif process.returncode == 0 and action == \"remove\":\n",
" output_widget.value += \"\"\"<div style=\"background-color: #3B3B3B; color: #008000;\">Command executed successfully.</div>\"\"\"\n",
" install_btn.disabled = False\n",
" remove_btn.disabled = True\n",
" return True\n",
Expand All @@ -97,29 +97,52 @@
" return False\n",
"\n",
"\n",
"def install_package(pip, github, output_container, install_btn, remove_btn, accordion, index):\n",
"def install_package(package_name, pip, github, output_container, message_container, install_btn, remove_btn, accordion, index):\n",
" if pip:\n",
" command = [\"pip\", \"install\", pip]\n",
" else:\n",
" command = [\"pip\", \"install\", \"git+\" + github]\n",
" message_container.value = f\"\"\"<div style=\"color: #000000;\">Installing {package_name}...</div>\"\"\"\n",
" result = execute_command_with_output(command, output_container, install_btn, remove_btn)\n",
" # if the package was installed successfully\n",
" if result:\n",
" # restart daemon\n",
" accordion.set_title(index, f\"{accordion.get_title(index)[:-2]} ✅\")\n",
" command = [\"verdi\", \"daemon\", \"restart\"]\n",
" subprocess.run(command, capture_output=True, shell=False)\n",
"\n",
"\n",
"def remove_package(package_name, output_container, install_btn, remove_btn, accordion, index):\n",
" message_container.value += \"\"\"<div style=\"color: #008000;\">Initiating test to load the plugin...</div>\"\"\"\n",
" # Test plugin functionality\n",
" command = [sys.executable, '-m', 'aiidalab_qe', 'test-plugin', package_name]\n",
" # Execute the command\n",
" result = subprocess.run(command, capture_output=True, text=True)\n",
" if result.returncode == 0:\n",
" # restart daemon\n",
" message_container.value = \"\"\"<div style=\"color: #008000;\">Loading plugin test passed.</div>\"\"\"\n",
" message_container.value += \"\"\"<div style=\"color: #008000;\">Plugin installed successfully.</div>\"\"\"\n",
" accordion.set_title(index, f\"{accordion.get_title(index)[:-2]} ✅\")\n",
" command = [\"verdi\", \"daemon\", \"restart\"]\n",
" subprocess.run(command, capture_output=True, shell=False)\n",
" else:\n",
" # uninstall the package\n",
" message_container.value = f\"\"\"<div style=\"color: #FF0000;\">The plugin '{package_name}' was installed successfully but plugin functionality test failed: {result.stderr}. </div>\"\"\"\n",
" message_container.value += \"\"\"<div style=\"color: #FF0000;\">This may be due to compatibility issues with the current AiiDAlab QEApp version. Please contact the plugin author for further assistance.</div>\"\"\"\n",
" message_container.value += \"\"\"<div style=\"color: #FF0000;\">To prevent potential issues, the plugin will now be uninstalled.</div>\"\"\"\n",
" remove_package(package_name, output_container, message_container, install_btn, remove_btn, accordion, index)\n",
"\n",
"\n",
"def remove_package(package_name, output_container, message_container, install_btn, remove_btn, accordion, index):\n",
" message_container.value += f\"\"\"<div style=\"color: #FF0000;\">Removing {package_name}...</div>\"\"\"\n",
" package_name = package_name.replace('-', '_')\n",
" command = [\"pip\", \"uninstall\", \"-y\", package_name]\n",
" result = execute_command_with_output(command, output_container, install_btn, remove_btn, action=\"remove\")\n",
" if result:\n",
" message_container.value += f\"\"\"<div style=\"color: #008000;\">{package_name} removed successfully.</div>\"\"\"\n",
" accordion.set_title(index, f\"{accordion.get_title(index)[:-2]} ☐\")\n",
" command = [\"verdi\", \"daemon\", \"restart\"]\n",
" subprocess.run(command, capture_output=True, shell=False)\n",
"\n",
"\n",
"def run_remove_button(package_name, output_container, message_container, install_btn, remove_btn, accordion, index):\n",
" message_container.value = \"\"\n",
" remove_package(package_name, output_container, message_container, install_btn, remove_btn, accordion, index)\n",
"\n",
"\n",
"accordion = ipw.Accordion()\n",
"\n",
"for i, (plugin_name, plugin_data) in enumerate(data.items()):\n",
Expand All @@ -137,6 +160,18 @@
" border='2px solid #CCCCCC'\n",
" )\n",
" )\n",
" # Output container with customized styling\n",
" message_container = ipw.HTML(\n",
" value=\"\"\"\n",
" <div style=\"color: #000000; height: 100%; overflow: auto;\">\n",
" </div>\n",
" \"\"\",\n",
" layout=ipw.Layout(\n",
" max_height='250px', \n",
" overflow='auto',\n",
" border='2px solid #CCCCCC'\n",
" )\n",
" )\n",
" \n",
" details = f\"Author: {plugin_data.get('author', 'N/A')}<br>\" \\\n",
" f\"Description: {plugin_data.get('description', 'No description available')}<br>\"\n",
Expand All @@ -148,13 +183,14 @@
" install_btn = ipw.Button(description=\"Install\", button_style='success', disabled=installed)\n",
" remove_btn = ipw.Button(description=\"Remove\", button_style='danger', disabled=not installed)\n",
"\n",
" install_btn.on_click(lambda btn, pip=plugin_data.get('pip', None), github=plugin_data.get('github', ''), oc=output_container, ib=install_btn, rb=remove_btn, ac=accordion, index=i: install_package(pip, github, oc, ib, rb, ac, index))\n",
" remove_btn.on_click(lambda btn, pn=plugin_name, oc=output_container, ib=install_btn, rb=remove_btn, ac=accordion, index=i: remove_package(pn, oc, ib, rb, ac, index))\n",
" install_btn.on_click(lambda btn, pn=plugin_name, pip=plugin_data.get('pip', None), github=plugin_data.get('github', ''), oc=output_container, mc=message_container, ib=install_btn, rb=remove_btn, ac=accordion, index=i: install_package(pn, pip, github, oc, mc, ib, rb, ac, index))\n",
" remove_btn.on_click(lambda btn, pn=plugin_name, oc=output_container, mc=message_container, ib=install_btn, rb=remove_btn, ac=accordion, index=i: run_remove_button(pn, oc, mc, ib, rb, ac, index))\n",
"\n",
" box = ipw.VBox([\n",
" ipw.HTML(details),\n",
" ipw.HBox([install_btn, remove_btn]),\n",
" output_container # Include the output container in the VBox\n",
" message_container,\n",
" output_container,\n",
" ])\n",
"\n",
" title_with_icon = f\"{plugin_name} {'✅' if installed else '☐'}\"\n",
Expand Down
4 changes: 2 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = aiidalab_qe
version = 24.4.0rc2
version = 24.4.0rc3
description = Package for the AiiDAlab QE app
long_description = file: README.md
long_description_content_type = text/markdown
Expand Down Expand Up @@ -66,7 +66,7 @@ categories =
quantum

[bumpver]
current_version = "v24.04.0rc2"
current_version = "v24.04.0rc3"
version_pattern = "v0Y.0M.PATCH[PYTAGNUM]"
commit_message = "Bump version {old_version} -> {new_version}"
commit = True
Expand Down
24 changes: 23 additions & 1 deletion src/aiidalab_qe/__main__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""For running the app from the command line used for post_install script."""

from pathlib import Path

import sys
import click
from aiida import load_profile

Expand Down Expand Up @@ -78,5 +78,27 @@ def download_pseudos(dest):
raise click.ClickException(f"Failed to download pseudo potentials: {error}")


@cli.command()
@click.argument(
"plugin_name",
default="aiidalab_qe",
)
@click.option("-p", "--profile", default=_DEFAULT_PROFILE)
def test_plugin(plugin_name, profile):
load_profile(profile)
from aiidalab_qe.app.utils import test_plugin_functionality

try:
success, message = test_plugin_functionality(plugin_name)
if success:
click.secho("Plugin is loaded successfully!", fg="green")
else:
click.secho(f"Failed to load plugin: {message}", fg="red", err=True)
sys.exit(1) # Exit with status 1 to indicate failure
except Exception as error:
click.secho(f"Failed to load plugin: {error}", fg="red", err=True)
sys.exit(1) # Exit with status 1 to indicate failure


if __name__ == "__main__":
cli()
67 changes: 66 additions & 1 deletion src/aiidalab_qe/app/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
from importlib.metadata import distributions


def print_error(entry_point, e):
print(f"\033[91mFailed to load plugin entry point {entry_point.name}.\033[0m")
print(
"\033[93mThis may be due to compatibility issues with the current QEApp version.\033[0m"
)
print("\033[93mPlease contact the plugin author for further assistance.\033[0m")
print(
"\033[93mThus, the plugin will not be available. However, you can still use the rest of the app.\033[0m"
)
print(f"\033[91mError message: {e}\033[0m\n")


# load entry points
def get_entries(entry_point_name="aiidalab_qe.properties"):
from importlib.metadata import entry_points

entries = {}
for entry_point in entry_points().get(entry_point_name, []):
entries[entry_point.name] = entry_point.load()
try:
# Attempt to load the entry point
loaded_entry_point = entry_point.load()
entries[entry_point.name] = loaded_entry_point
except Exception as e:
print_error(entry_point, e)

return entries

Expand All @@ -16,3 +36,48 @@ def get_entry_items(entry_point_name, item_name="outline"):
for name, entry_point in entries.items()
if entry_point.get(item_name, False)
}


def get_entry_points_for_package(
package_name: str, group: str = "aiidalab_qe.properties"
):
"""Find the entry points for the specified package"""
entry_points_list = []

dist = next(
(d for d in distributions() if d.metadata["Name"] == package_name), None
)
if not dist:
raise ValueError(f"Package '{package_name}' not found.")
# Retrieve all entry points associated with this distribution
if dist.entry_points:
for ep in dist.entry_points:
if ep.group == group:
entry_points_list.append(ep)
else:
print(f"No entry points found for package '{package_name}'.")
return entry_points_list


def test_plugin_functionality(plugin_name):
"""Test the functionality of the plugin.
1) loading all entry points.
2) check if the plugin use correct QEAppComputationalResourcesWidget
"""
from aiidalab_qe.common.widgets import QEAppComputationalResourcesWidget

try:
eps = get_entry_points_for_package(plugin_name)
# check if we can load all entry points
for ep in eps:
loaded_ep = ep.load()
# check if the code uses the correct widget
for name, code in loaded_ep.get("code", {}).items():
if not isinstance(code, QEAppComputationalResourcesWidget):
return (
False,
f"\nPlugin {plugin_name} code {name} must use QEAppComputationalResourcesWidget class",
)
except Exception as e:
return False, f"Failed to get entry points for package {plugin_name}: {e}"
return True, ""
Loading

0 comments on commit 2c33e2e

Please sign in to comment.