Skip to content

Commit

Permalink
Update guides (aiidalab#1035)
Browse files Browse the repository at this point in the history
- Allow string-based guide paths
- Discard bands plugin guides
- Discard advanced guide
- Update docs
- Update guide text
  • Loading branch information
edan-bainglass authored Jan 13, 2025
1 parent 4032a62 commit 398ea81
Show file tree
Hide file tree
Showing 14 changed files with 393 additions and 197 deletions.
21 changes: 21 additions & 0 deletions docs/source/development/guides.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,24 @@ Developers have two options when using the ``InAppGuide`` widget:
A decent amount of ``InAppGuide(identifier=<identifier>)`` instances have been placed strategically throughout the app.
Developers may suggest additional core guide sections via GitHub pull requests.
For plugin developers, additional instances of either flavor are recommended to be added in any component of the plugin in conjunction with dedicated plugin-specific guides.

Plugin guides
-------------

Plugin developers can enhance user experience while using the app by introducing custom guides.
To do so, add the following key/value entry in your plugin's ``__init__.py`` file:

.. code:: python
my_plugin = {
...
"guides": <path-to-guide>,
}
where ``path-to-guide`` is the path (``Path`` object or absolute string path) to the directory containing the guide HTML files.
On app start, the guide manager will scan the plugin entry points for the ``guides`` key and load the guides accordingly.

Guide order
-----------

When naming your guide HTML documents, prefix the file name with ``#_``. The number ``#`` will determine the order in which the guides are displayed in the list.
4 changes: 2 additions & 2 deletions src/aiidalab_qe/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,10 @@ def __init__(self, qe_auto_setup=True):

super().__init__(
children=[
InAppGuide(identifier="guide-warning", classes=["guide-warning"]),
InAppGuide(identifier="guide-header"),
self._process_loading_message,
self._wizard_app_widget,
InAppGuide(identifier="post-guide", classes=["post-guide"]),
InAppGuide(identifier="post-guide"),
]
)

Expand Down
21 changes: 14 additions & 7 deletions src/aiidalab_qe/app/result/components/summary/summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,27 @@ def _render_summary(self):
)
settings_summary.add_class("summary-panel")

self.output_download_help = ipw.HTML("""
<div style="line-height: 1.4; margin: 0; margin-bottom: 10px;">
<h2>Download the data</h2>
Once the workflow is finished, you can download raw data (i.e. input
and output files) and/or the AiiDA archive (ready to be shared or
imported into another AiiDA profile).
</div>
""")

self.output_download_container = ipw.VBox(
children=[
self.output_download_help,
ipw.HTML("""
<div style="line-height: 140%; margin: 0; margin-bottom: 10px;">
<h2>Download the data</h2>
Once the workflow is finished, you can download raw data
(i.e. input and output files) and/or the AiiDA archive
(ready to be shared or imported into another AiiDA profile).
<div style="line-height: 1.4">
Download buttons will appear here when available.
</div>
"""),
ipw.HTML("Download buttons will appear here when available."),
],
)
self.output_download_container.add_class("summary-panel")
self._render_download_widget()

container = ipw.HBox(
children=[
Expand Down Expand Up @@ -77,7 +84,7 @@ def _render_download_widget(self):
output_download_widget = WorkChainOutputs(node=process_node)
output_download_widget.layout.width = "100%"
self.output_download_container.children = [
self.output_download_container.children[0], # type: ignore
self.output_download_help,
output_download_widget,
]
self.has_download_widget = True
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def is_relaxed(self):
return "relax" in self.properties

def update(self):
self.auto_render = self.has_results
super().update()
with self.hold_trait_notifications():
if not self.is_relaxed or self.selected_view == "initial":
self.header = self.header_template.format(title="Initial")
Expand Down
24 changes: 17 additions & 7 deletions src/aiidalab_qe/app/static/styles/infobox.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,30 @@
border-left: 3px solid #add8e6;
background-color: #d9edf7;
}
.info-box p {
line-height: 24px;
.info-box .widget-html-content {
line-height: 1.5;
}
.info-box.in-app-guide.show {
display: flex !important;
}
.guide ol {
list-style: none;
}
.guide p:not(:last-of-type) {
.guide p:last-of-type {
margin-bottom: 0.5em;
}
.guide-warning {
background-color: #fcf8e3;
border-color: #ffe4c4;
.in-app-guide:has(#guide-header),
.in-app-guide:has(#post-guide) {
background-color: var(--color-success);
border-color: #8fbc8f;
}
.in-app-guide:has(#guide-header) {
margin-bottom: 1em;
}
.post-guide {
.in-app-guide:has(#post-guide) {
margin-top: 1em;
}
#post-guide {
margin: 1em 0;
}
.in-app-guide .alert {
Expand All @@ -41,3 +47,7 @@
.in-app-guide h4 {
font-weight: bold;
}

.results-panel-guide {
margin-bottom: 1em;
}
94 changes: 49 additions & 45 deletions src/aiidalab_qe/app/static/templates/guide.jinja
Original file line number Diff line number Diff line change
@@ -1,49 +1,53 @@
<div class="guide">
<p>
The QE app allows you to calculate properties in a simple 4-step process:
</p>
<div>
<p>
The QE app allows you to calculate properties in a simple 4-step process:
</p>
<ol>
<li>
🔍 <strong>Step 1:</strong> Select the structure you want to run.
</li>
<li>
⚙️ <strong>Step 2:</strong> Select the properties you are interested in.
</li>
<li>
💻 <strong>Step 3:</strong> Choose the computational resources you want to run on and submit your workflow.
</li>
<li>
🚀 <strong>Step 4:</strong> Monitor and view your workflow results.
</li>
</ol>
</div>

<ol>
<li>
🔍 <strong>Step 1:</strong> Select the structure you want to run.
</li>
<li>
⚙️ <strong>Step 2:</strong> Select the properties you are interested in.
</li>
<li>
💻 <strong>Step 3:</strong> Choose the computational resources you want to run on and submit your workflow.
</li>
<li>
🚀 <strong>Step 4:</strong> Monitor and view your workflow results.
</li>
</ol>
<div>
<p>
New users can go straight to the first step and select their structure.
</p>
<p>
Completed workflows can be viewed in the <strong>Calculation history</strong> section.
</p>
<p>
To start a new calculation in a separate tab, click the <strong>New calculation</strong> button.
</p>
<p>
You can also check out the
<a href="https://aiidalab-qe.readthedocs.io/tutorials/basic.html" target="_blank">basic</a> tutorial to get
started
with the Quantum ESPRESSO app, or try out the
<a href="https://aiidalab-qe.readthedocs.io/tutorials/advanced.html" target="_blank">advanced</a> tutorial to
learn
additional features offered by the app.
</p>
<p>
For a more in-depth dive into the app's features, please refer to the
<a href="https://aiidalab-qe.readthedocs.io/howto/index.html" target="_blank">how-to guides</a>.
</p>
</div>

<p>
New users can go straight to the first step and select their structure.
</p>

<p>
Completed workflows can be viewed in the <strong>Calculation history</strong> section.
</p>

<p>
To start a new calculation in a separate tab, click the <strong>New calculation</strong> button.
</p>

<p>
You can also check out the
<a href="https://aiidalab-qe.readthedocs.io/tutorials/basic.html" target="_blank">basic</a> tutorial to get started
with the Quantum ESPRESSO app, or try out the
<a href="https://aiidalab-qe.readthedocs.io/tutorials/advanced.html" target="_blank">advanced</a> tutorial to learn
additional features offered by the app.
</p>

<p>
For a more in-depth dive into the app's features, please refer to the
<a href="https://aiidalab-qe.readthedocs.io/howto/index.html" target="_blank">how-to guides</a>.
</p>

<p>
Alternatively, you can select one of our in-app guides below to walk through an example use-case.
</p>
<div>
<h3>In-app guides</h3>
<p>
Alternatively, you can select one of our in-app guides below to walk through an example use case.
</p>
</div>
</div>
5 changes: 3 additions & 2 deletions src/aiidalab_qe/common/guide_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,12 @@ def _on_active_guide_change(self, _):

def _fetch_plugin_guides(self):
"""Fetch guides from plugins."""
entries: dict[str, Path] = get_entry_items("aiidalab_qe.properties", "guides")
entries: dict = get_entry_items("aiidalab_qe.properties", "guides")
for identifier, guides in entries.items():
path = Path(guides)
if identifier not in self._guides:
self._guides[identifier] = {}
for guide in sorted(guides.glob("*"), key=lambda x: x.stem.split("_")[0]):
for guide in sorted(path.glob("*"), key=lambda x: x.stem.split("_")[0]):
stem = guide.stem.split("_", maxsplit=1)[1]
self._guides[identifier][stem] = guide.absolute()

Expand Down
36 changes: 25 additions & 11 deletions src/aiidalab_qe/common/panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from aiida.common.extendeddicts import AttributeDict
from aiidalab_qe.app.parameters import DEFAULT_PARAMETERS
from aiidalab_qe.common.code.model import CodeModel
from aiidalab_qe.common.infobox import InAppGuide
from aiidalab_qe.common.mixins import Confirmable, HasModels, HasProcess
from aiidalab_qe.common.mvc import Model
from aiidalab_qe.common.widgets import (
Expand Down Expand Up @@ -529,8 +530,7 @@ def has_results(self):
return node and node.is_finished_ok

def update(self):
if self.has_results:
self.auto_render = True
self.auto_render = self.has_results

def update_process_status_notification(self):
if self._completed_process:
Expand Down Expand Up @@ -620,17 +620,30 @@ def render(self):
if not self._model.has_process:
return

self.guide = InAppGuide(
identifier=f"{self._model.identifier}-results",
classes=["results-panel-guide"],
)

self.results_container = ipw.VBox()

if self._model.auto_render:
self.children = [self.results_container]
self.children = [
self.guide,
self.results_container,
]
self._load_results()
else:
self._render_controls()
self.children += (self.results_container,)
self.children = [
self.guide,
self._get_controls_section(),
self.results_container,
]
if self._model.identifier == "structure":
self._load_results()

self.rendered = True

def _on_process_change(self, _):
self._model.update()

Expand All @@ -644,10 +657,9 @@ def _on_load_results_click(self, _):
def _load_results(self):
self.results_container.children = [self.loading_message]
self._render()
self.rendered = True
self._post_render()

def _render_controls(self):
def _get_controls_section(self) -> ipw.VBox:
self.process_status_notification = ipw.HTML()
ipw.dlink(
(self._model, "process_status_notification"),
Expand Down Expand Up @@ -681,10 +693,12 @@ def _render_controls(self):
]
)

self.children = [
self.process_status_notification,
self.load_controls,
]
return ipw.VBox(
children=[
self.process_status_notification,
self.load_controls,
]
)

def _render(self):
raise NotImplementedError()
Expand Down
Loading

0 comments on commit 398ea81

Please sign in to comment.