Skip to content

Commit

Permalink
Merge branch 'main' into plugin/api_add_update_input
Browse files Browse the repository at this point in the history
  • Loading branch information
superstar54 authored Apr 8, 2024
2 parents add20cc + 0aab1e6 commit 1f7c56c
Show file tree
Hide file tree
Showing 16 changed files with 533 additions and 57 deletions.
1 change: 1 addition & 0 deletions docs/source/development/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ This guide explains the architecture of the application and how to extend the fu

architecture
plugin
plugin_registry
4 changes: 4 additions & 0 deletions docs/source/development/plugin.rst
Original file line number Diff line number Diff line change
Expand Up @@ -362,4 +362,8 @@ Further Reading
QuantumESPRESSO app comes with several built-in plugins, which can be found in the ``aiidalab_qe.plugins`` folder.
You can also use them as a start point to create your own plugins.


You can register your plugin to facilitate its discovery and use by the community.
Please refer to the :doc:`Plugin registry </development/plugin_registry>` for more details.

.. _aiidalab-qe-plugin-demos: https://github.com/aiidalab/aiidalab-qe-plugin-demos
46 changes: 46 additions & 0 deletions docs/source/development/plugin_registry.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@


Plugin Registry
=========================================

If you are either in the process of creating a new plugin or already have one developed, you're encouraged to register your plugin here to become part of the official AiiDAlab Quantum ESPRESSO App plugin ecosystem.

Registering Your Plugin
-----------------------

To include your plugin in the registry, follow these steps:

1. Fork this `repository <https://github.com/aiidalab/aiidalab-qe>`_.

2. Add your plugin to the `plugins.yaml` file. Place your entry at the end of the file, following this example:

.. code-block:: yaml
aiidalab-qe-xyz:
description: "Quantum ESPRESSO plugin for XYZ by AiiDAlab."
author: "Alice Doe"
github: "https://github.com/alicedoe/aiidalab-qe-xyz"
documentation: "https://aiidalab-qe-xyz.readthedocs.io/"
pip: "aiidalab-qe-xyz"
3. Submit a Pull Request. Direct it to `this repository's Pull Requests section <https://github.com/aiidalab/aiidalab-qe/pulls>`_.

Plugin Entry Requirements
-------------------------

**Required Keys**

- **Top-level key:** The plugin's distribution name, which should be lowercase and prefixed by ``aiidalab-`` or ``aiida-``. For example, ``aiidalab-qe-coolfeature`` or ``aiidalab-neutron``.
- **description:** A brief description of your plugin.

**Optional Keys**

- **github:** If provided, this should be the URL to the plugin's GitHub homepage.

At least one of ``github`` or ``pip`` is required.

- **pip:** The PyPI package name for your plugin, useful for installation via pip. Example: ``aiida-quantum``.
- **documentation:** The URL to your plugin's online documentation, such as ReadTheDocs.
- **author:** The developer of the plugin.

By following these guidelines, you can ensure your plugin is correctly listed and accessible within the AiiDAlab Quantum ESPRESSO app, facilitating its discovery and use by the community.
196 changes: 196 additions & 0 deletions plugin_list.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## AiiDAlab Quantum ESPRESSO Plugin manager\n",
"\n",
"This page lets you manage the plugins. You can find all the plugins that available in the official AiiDAlab Quantum ESPRESSO Plugin registry. You can install and remove plugins from this page.\n",
"\n",
"### Plugin registry\n",
"\n",
"If you are starting to develop a new plugin or if you already have one, and want it discoveried and used by the community. Please refer to this [page](https://aiidalab-qe.readthedocs.io/development/plugin_registry.html) to learn how to register a plugin.\n",
"\n",
"\n",
"### Available plugins\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import requests\n",
"import yaml\n",
"\n",
"# URL of the YAML file\n",
"filepath = 'https://raw.githubusercontent.com/aiidalab/aiidalab-qe-plugin-registry/main/plugins.yaml'\n",
"\n",
"# Fetch the contents of the URL\n",
"response = requests.get(filepath)\n",
"\n",
"# Check if the request was successful\n",
"if response.status_code == 200:\n",
" # Load the YAML content\n",
" data = yaml.safe_load(response.content)\n",
" # Now 'data' contains the YAML file's contents as a Python object\n",
"else:\n",
" print(f\"Failed to fetch the YAML file: HTTP {response.status_code}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import subprocess\n",
"from threading import Thread\n",
"\n",
"import ipywidgets as ipw\n",
"from IPython.display import display\n",
"\n",
"\n",
"def is_package_installed(package_name):\n",
" import importlib\n",
" package_name = package_name.replace('-', '_')\n",
" try:\n",
" importlib.import_module(package_name)\n",
" return True\n",
" except ImportError:\n",
" return False\n",
"\n",
"\n",
"def stream_output(process, output_widget):\n",
" \"\"\"Reads output from the process and forwards it to the output widget.\"\"\"\n",
" while True:\n",
" output = process.stdout.readline()\n",
" if process.poll() is not None and output == '':\n",
" break\n",
" if output:\n",
" output_widget.value += f\"\"\"<div style=\"background-color: #3B3B3B; color: #FFFFFF;\">{output}</div>\"\"\"\n",
"\n",
"\n",
"def execute_command_with_output(command, output_widget, install_btn, remove_btn, action=\"install\"):\n",
" \"\"\"Execute a command and stream its output to the given output widget.\"\"\"\n",
" output_widget.value = \"\" # Clear the widget\n",
" process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)\n",
" # Create a thread to read the output stream and write it to the output widget\n",
" thread = Thread(target=stream_output, args=(process, output_widget))\n",
" thread.start()\n",
" 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",
" else:\n",
" output_widget.value += \"\"\"<div style=\"background-color: #3B3B3B; color: #FF0000;\">Command failed.</div>\"\"\"\n",
" return False\n",
"\n",
"\n",
"def install_package(pip, github, output_container, install_btn, remove_btn, accordion, index):\n",
" if pip:\n",
" command = [\"pip\", \"install\", pip]\n",
" else:\n",
" command = [\"pip\", \"install\", \"git+\" + github]\n",
" result = execute_command_with_output(command, output_container, install_btn, remove_btn)\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",
" 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",
" 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",
"accordion = ipw.Accordion()\n",
"\n",
"for i, (plugin_name, plugin_data) in enumerate(data.items()):\n",
" installed = is_package_installed(plugin_name)\n",
" \n",
" # Output container with customized styling\n",
" output_container = ipw.HTML(\n",
" value=\"\"\"\n",
" <div style=\"background-color: #3B3B3B; color: #FFFFFF; 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",
" if 'documentation' in plugin_data:\n",
" details += f\"Documentation: <a href='{plugin_data['documentation']}' target='_blank'>Visit</a><br>\"\n",
" if 'github' in plugin_data:\n",
" details += f\"Github: <a href='{plugin_data.get('github')}' target='_blank'>Visit</a>\"\n",
"\n",
" 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",
"\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",
" ])\n",
"\n",
" title_with_icon = f\"{plugin_name} {'✅' if installed else '☐'}\"\n",
" accordion.set_title(i, title_with_icon)\n",
" accordion.children = list(accordion.children) + [box]\n",
"\n",
"display(accordion)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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
}
17 changes: 17 additions & 0 deletions plugins.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
aiida-bader:
description: AiiDA plugin for the Bader analysis
author: Xing Wang
github: https://github.com/superstar54/aiida-bader
documentation: https://aiida-bader.readthedocs.io/
pip: aiida-bader

aiidalab-qe-vibroscopy:
description: Plugin to compute vibrational properties of materials via the aiida-vibroscopy AiiDA plugin
author: Miki Bonacci, Andres Ortega Guerrero
github: https://github.com/mikibonacci/aiidalab-qe-vibroscopy

aiidalab-qe-muon:
description: Plugin to compute muon stopping sites and related properties via the aiida-muon and aiida-musconv AiiDA plugins
author: Miki Bonacci
github: https://github.com/mikibonacci/aiidalab-qe-muon
Loading

0 comments on commit 1f7c56c

Please sign in to comment.