Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update guides #1035

Merged
merged 13 commits into from
Jan 13, 2025
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.
edan-bainglass marked this conversation as resolved.
Show resolved Hide resolved
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.
edan-bainglass marked this conversation as resolved.
Show resolved Hide resolved
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
Loading