Skip to content

Commit

Permalink
Generalize and simplify
Browse files Browse the repository at this point in the history
  • Loading branch information
edan-bainglass committed Dec 9, 2024
1 parent ff247c0 commit bd8cc1a
Show file tree
Hide file tree
Showing 15 changed files with 343 additions and 220 deletions.
52 changes: 3 additions & 49 deletions src/aiidalab_qe/app/configuration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,36 +115,13 @@ def render(self):
children=[
ipw.VBox(
children=[
InAppGuide(
children=[
ipw.HTML("""
<div>
Here we select the properties to calculate.
<div class="alert alert-success">
Select <b>Electronic band structure</b> and
<b>Projected density of states (PDOS)</b>
</div>
</div>
""")
],
),
InAppGuide(identifier="properties-selection"),
*self.property_children,
]
),
ipw.VBox(
children=[
InAppGuide(
children=[
ipw.HTML("""
<div>
Here we can customize the calculation parameters.
<div class="alert alert-success">
Click on each tab to customize its settings.
</div>
</div>
""")
],
),
InAppGuide(identifier="calculation-settings"),
self.tabs,
],
),
Expand All @@ -171,30 +148,7 @@ def render(self):
self.confirm_button.on_click(self.confirm)

self.children = [
InAppGuide(
children=[
ipw.HTML("""
<div>
In this step, we define the workflow tasks, including structure
relaxation and which properties to compute, and select the
parameters of the calculations.
<div class="alert alert-success">
<h4>Tasks</h4>
<ol>
<li>Select <b>Full geometry</b> relaxation</li>
<li>Open <b>Step 2.1</b> to select properties</li>
<li>Open <b>Step 2.2</b> to customize parameters</li>
<li>Click <b>Confirm</b> to proceed</li>
</ol>
</div>
<div class="alert alert-warning">
<b>Note:</b> Changes after confirmation will unconfirm this
step and reset the following steps.
</div>
</div>
""")
],
),
InAppGuide(identifier="configuration-step"),
self.structure_set_message,
ipw.HTML("""
<div style="padding-top: 0px; padding-bottom: 0px">
Expand Down
14 changes: 1 addition & 13 deletions src/aiidalab_qe/app/configuration/advanced/advanced.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,19 +275,7 @@ def render(self):
self.pseudos.render()

self.children = [
InAppGuide(
children=[
ipw.HTML("""
<div>
The Advanced settings allow you to finely tune the calculation.
<div class="alert alert-warning">
In this walkthrough, we will not modify advanced settings
and proceed with the defaults.
</div>
</div>
""")
],
),
InAppGuide(identifier="advanced-settings"),
ipw.HBox(
children=[
self.clean_workdir,
Expand Down
22 changes: 1 addition & 21 deletions src/aiidalab_qe/app/configuration/basic/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,27 +61,7 @@ def render(self):
)

self.children = [
InAppGuide(
children=[
ipw.HTML("""
<div>
The basic settings panel provides top-level calculation
settings including the electronic and magnetic properties of
the material. It also provides a choice of three protocols
that pre-configure many calculation settings, balancing
speed with accuracy.
<div class="alert alert-success">
Select the <b>fast</b> protocol
</div>
<div class="alert alert-warning">
Note: Due to the limited resources provided for the
demo server, we select the <b>fast</b> protocol to
reduce the cost of the calculation.
</div>
</div>
""")
],
),
InAppGuide(identifier="basic-settings"),
ipw.HTML("""
<div style="line-height: 140%; padding-top: 10px; padding-bottom: 10px">
Below you can indicate both if the material should be treated as an
Expand Down
12 changes: 1 addition & 11 deletions src/aiidalab_qe/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,17 +110,7 @@ def __init__(self, qe_auto_setup=True):

super().__init__(
children=[
InAppGuide(
children=[
ipw.HTML("""
<div>
You've activated an in-app guide. Follow along below to learn
how to use the Quantum ESPRESSO app.
</div>
""")
],
classes=["guide-warning"],
),
InAppGuide(identifier="guide-warning", classes=["guide-warning"]),
self.new_workchain_button,
self._process_loading_message,
self._wizard_app_widget,
Expand Down
52 changes: 1 addition & 51 deletions src/aiidalab_qe/app/structure/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,57 +152,7 @@ def render(self):
)

self.children = [
InAppGuide(
children=[
ipw.HTML("""
<div>
In this step, you can select a structure as follows:
<ul>
<li>
<b>Upload file</b>:
upload a structure file from your computer.
</li>
<li>
<b>OPTIMADE</b>:
search for structures in the OPTIMADE database.
</li>
<li>
<b>AiiDA database</b>:
search for structures in your AiiDA database.
</li>
<li>
<b>From Examples</b>:
select a structure from a list of example structures.
</li>
</ul>
Once selected, you may inspect the structure. You can also edit
the structure using the available structure editors. When done,
you can choose to modify the structure label and/or provide a
description. These will be attached to the input structure node
in your AiiDA database. When you are ready, click "Confirm" to
proceed to the next step.
<br>
<div class="alert alert-success">
<h4>Tasks</h4>
<ol>
<li>Click on the <b>From examples</b> tab</li>
<li>Select <b>Gold</b> from the dropdown list</li>
<li>Click the <b>Confirm</b> button to proceed.</li>
</ol>
</div>
<div class="alert alert-warning">
<b>Warning:</b> If the confirmed structure is not yet stored
in the AiiDA database, it will be stored automatically when
you proceed to the next step.
</div>
<div class="alert alert-warning">
<b>Warning:</b> Changes after confirmation will unconfirm
this step and reset the following steps.
</div>
</div>
"""),
],
),
InAppGuide(identifier="structure-step"),
ipw.HTML("""
<p>
Select a structure from one of the following sources and then
Expand Down
20 changes: 1 addition & 19 deletions src/aiidalab_qe/app/submission/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,25 +159,7 @@ def render(self):
)

self.children = [
InAppGuide(
children=[
ipw.HTML("""
<div>
In this step, we define the resources to be used in the
calculation. The global resources are used to define resources
across all workflow calculations. Optionally, you can override
the resource settings for specific calculations.
<div class="alert alert-success">
<h4>Tasks</h4>
<ol>
<li>Select resources</li>
<li>Click <b>Submit</b> to proceed</li>
</ol>
</div>
</div>
""")
],
),
InAppGuide(identifier="submission-step"),
ipw.HTML("""
<div style="padding-top: 0px; padding-bottom: 0px">
<h4>Codes</h4>
Expand Down
10 changes: 7 additions & 3 deletions src/aiidalab_qe/app/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import traitlets as tl
from IPython.display import display

from aiidalab_qe.common.guide_manager import guide_manager
from aiidalab_qe.common.widgets import LoadingWidget


Expand Down Expand Up @@ -95,16 +96,19 @@ def _on_job_history_toggle(self, change: dict):

def _on_guide_select(self, change: dict):
"""Sets the current active guide."""
from aiidalab_qe.common.infobox import guide_manager

guide_manager.active_guide = change["new"]

def _set_guide_options(self, _):
"""Fetch the available guides."""
self._view.guide_selection.options = ["none", *guide_manager.get_guides()]

def _set_event_handlers(self) -> None:
"""Set up event handlers."""
self._view.guide_toggle.observe(self._on_guide_toggle, "value")
self._view.about_toggle.observe(self._on_about_toggle, "value")
self._view.job_history_toggle.observe(self._on_job_history_toggle, "value")
self._view.guide_selection.observe(self._on_guide_select, "value")
self._view.on_displayed(self._set_guide_options)


class AppWrapperModel(tl.HasTraits):
Expand Down Expand Up @@ -196,7 +200,7 @@ def __init__(self) -> None:
self.about = ipw.HTML(env.from_string(about_template).render())

self.guide_selection = ipw.RadioButtons(
options=["none", "basic"],
options=["none"],
description="Guides:",
value="none",
)
Expand Down
50 changes: 50 additions & 0 deletions src/aiidalab_qe/common/guide_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from __future__ import annotations

from pathlib import Path

import traitlets as tl
from bs4 import BeautifulSoup

import aiidalab_qe
from aiidalab_qe.app.utils import get_entry_items


class GuideManager(tl.HasTraits):
active_guide = tl.Unicode("none", allow_none=True)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
guides = Path(aiidalab_qe.__file__).parent.joinpath("guides").glob("*")
self._guides = {guide.stem: guide.absolute() for guide in guides}
self._fetch_plugin_guides()

self.content = BeautifulSoup()

self.observe(
self._on_active_guide_change,
"active_guide",
)

@property
def has_guide(self):
return self.active_guide != "none"

def get_guides(self):
return [*self._guides.keys()]

def get_guide_section_by_id(self, content_id: str):
return self.content.find(attrs={"id": content_id})

def _on_active_guide_change(self, _):
guide_path = self._guides.get(self.active_guide)
html = Path(guide_path).read_text() if guide_path else ""
self.content = BeautifulSoup(html, "html.parser")

def _fetch_plugin_guides(self):
entries: dict[str, Path] = get_entry_items("aiidalab_qe.properties", "guides")
for guides in entries.values():
for guide in guides.glob("*"):
self._guides[guide.stem] = guide.absolute()


guide_manager = GuideManager()
43 changes: 22 additions & 21 deletions src/aiidalab_qe/common/infobox.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from __future__ import annotations

import ipywidgets as ipw
import traitlets as tl


class InfoBox(ipw.VBox):
Expand All @@ -23,34 +22,30 @@ def __init__(self, classes: list[str] | None = None, **kwargs):
self.add_class(custom_class)


class GuideManager(tl.HasTraits):
active_guide = tl.Unicode("none")


guide_manager = GuideManager()


class InAppGuide(InfoBox):
"""The `InfoAppGuide` is used to set up in-app guides that may be toggle in unison."""
"""The `InfoAppGuide` is used to set up toggleable in-app guides."""

def __init__(
self,
guide_class: str = "qe-app",
children: list | None = None,
identifier: str = "",
classes: list[str] | None = None,
**kwargs,
):
"""`InAppGuide` constructor.
Parameters
----------
`guide_class` : `str`, optional
The identifier used to toggle the guide.
The default `qe-app` identifies built-in guide sections.
children : `list`, optional
The children of the guide.
`identifier` : `str`
The identifier used to load the guide file.
`classes` : `list[str]`, optional
One or more CSS classes.
"""
from aiidalab_qe.common.guide_manager import guide_manager

self.guide_class = guide_class
self.manager = guide_manager

super().__init__(
classes=[
Expand All @@ -60,7 +55,15 @@ def __init__(
**kwargs,
)

guide_manager.observe(
if children:
self.children = children
elif identifier:
self.children = []
self.identifier = identifier
else:
raise ValueError("No content or path identifier provided")

self.manager.observe(
self._on_active_guide_change,
"active_guide",
)
Expand All @@ -74,9 +77,7 @@ def _on_active_guide_change(self, _):
self._toggle_guide()

def _toggle_guide(self):
active_guide = guide_manager.active_guide
not_generic = self.guide_class != "qe-app"
if active_guide == "none" or (not_generic and active_guide != self.guide_class):
self.layout.display = "none"
else:
self.layout.display = "flex"
if hasattr(self, "identifier"):
html = self.manager.get_guide_section_by_id(self.identifier)
self.children = [ipw.HTML(str(html))] if html else []
self.layout.display = "flex" if self.manager.has_guide else "none"
Loading

0 comments on commit bd8cc1a

Please sign in to comment.