Commit ed25e69c authored by Ayres, Andrew's avatar Ayres, Andrew
Browse files

Minor code snippet fixes

parent 58814b42
Loading
Loading
Loading
Loading
Loading
+23 −18
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@ exercises: 3
# Using NDIP for Backend Computations
In this section, we will start using the `nova-galaxy` library to interact with the NDIP platform and run a neutron analysis tool.  First, ensure you have set your `GALAXY_URL` and `GALAXY_API_KEY` as environment variables, as explained in the notes at the end of this episode.  We also need to add `nova-galaxy` as a project dependency.

From the command line, type `poetry add nova-galaxy@^0.4.0`. This command will add the nova-galaxy library to the pyproject.toml file as a project dependency. Then run `poetry install` to update your project dependencies.
From the command line, type `poetry add nova-galaxy@^0.7.0`. This command will add the nova-galaxy library to the pyproject.toml file as a project dependency. Then run `poetry install` to update your project dependencies.

## Interacting with NDIP via `nova-galaxy`

@@ -60,6 +60,7 @@ To get started, let\'s create the Fractal class. Create an empty file at `src/no
*   **Imports**:  The `Fractal Class` will start by importing the necessary classes from `nova-galaxy`:

    ```python
    import os
    from nova.galaxy import Connection, Parameters, Tool
    ```

@@ -77,6 +78,7 @@ To get started, let\'s create the Fractal class. Create an empty file at `src/no

    *   **Instantiate `Connection`, `Tool`, and `Parameters`**: We create instances of the `Connection`, `Tool`, and `Parameters` classes:
        ```python
        def run_fractal_tool(self):
            conn = Connection(galaxy_url=self.galaxy_url, galaxy_key=self.galaxy_key)
            tool = Tool(id="neutrons_fractal")
            params = Parameters()
@@ -86,14 +88,15 @@ To get started, let\'s create the Fractal class. Create an empty file at `src/no
        Note that we create a `Tool` object with the `id="neutrons_fractal"`. This tells `nova-galaxy` which NDIP tool we want to run. The obvious question at this point is how do we know the id of the tool and what parameters it expects? We can look at the tool\'s launch page in calvera for some hints but ultimately we have to look at the tool\'s [xml file](https://code.ornl.gov/ndip/galaxy-tools/-/blob/dev/tools/neutrons/test_tools/fractal.xml?ref_type=heads). 

    *   **Connect and Run the Tool**:  The `with conn.connect() as galaxy_connection:` block establishes a connection to NDIP and ensures proper handling of the connection:

        ```python
            with conn.connect() as galaxy_connection:
                data_store = galaxy_connection.create_data_store(name="fractal_store")
                data_store.persist()
                print("Executing fractal tool. This might take a few minutes.")
                output = tool.run(data_store, params)
            output.get_dataset("output").download("image.png")

                output.get_dataset("output").download("tmp.png")
            print("Fractal tool finished successfully.")
        ```


@@ -103,13 +106,15 @@ We are now going to modify the existing `main.py` file. Change the main method t

*   **Instantiate and Run**: In the `main()` function, we create an instance of `Fractal` and call the `run_fractal_tool()` method, wrapped in a `try...except` block for basic error handling:
    ```python
    def main():
    import sys
    from .models.fractal import Fractal

    def main() -> None:
        fractal = Fractal()
        try:
            fractal.run_fractal_tool()
        except Exception as e:
            print(f"Error running fractal tool: {e}")

    ```

## Running the tool
+74 −27
Original line number Diff line number Diff line
@@ -141,9 +141,9 @@ InputField(v_model="config.username")

Let\'s see how to implement the MVVM pattern using `nova-mvvm` and incorporate Pydantic for data validation.

**1. Adding Fractal to the ViewModel (`src/nova_tutorial/view_models/main.py`):**
**1. Adding Fractal to the ViewModel (`src/nova_tutorial/app/view_models/main.py`):**

*   **Running our Model**:  We start by adding a method to our ViewModel which will run the Fractal tool.
*   **Running our Model**:  We start by adding a method to bottom of our ViewModel which will run the Fractal tool.

```python
    def run_fractal(self) -> None:
@@ -151,18 +151,20 @@ Let\'s see how to implement the MVVM pattern using `nova-mvvm` and incorporate P
        self.update_view()

```
**2. Updating our Fractal Class for pydantaic and MVVM (`src/nova/tutorial/models/fractal.py**
**2. Updating our Fractal Class for pydantaic and MVVM (`src/nova_tutorial/app/models/fractal.py**

*   **Adding new imports**: We need to add some imports for pydantic and working with base64 encondings to deal with the image.
*   **Adding new imports**: We need to add some imports for pydantic and working with base64 encondings to deal with the image. Modify your import block to match below.

```python
    import os
    from base64 import b64encode
    from typing import Literal

    from pydantic import BaseModel, Field
    from nova.galaxy import Connection, Parameters, Tool
```

*   **Update class variables:** Now we'll update fractal_type to support pydantic and add an image variable to store the image. Modify the variable declarations to the following: 
*   **Update class variables:** Now we'll update fractal_type and other class variables to support pydantic. We'll also add an image variable to store the image. Modify the variable declarations to the following: 

```python
    class Fractal(BaseModel):
@@ -182,7 +184,21 @@ Let\'s see how to implement the MVVM pattern using `nova-mvvm` and incorporate P
                self.image_data = f"data:image/png;base64,{b64encode(image_file.read()).decode()}"
```

**3. Creating a FractalTab (`src/nova_tutorial/views/fractal_tab.py`):**
**3. Updating our MainModel Class to add the new Fractal Class (`src/nova_tutorial/app/models/main_model.py**
*   **Add Fractal to imports**: Add an import for the Fractal class into our MainModel.

```python
from .fractal import Fractal  # Import Fractal
```

*   **Add the Fractal Model to the MainModel**: Modify the end of the MainModel class so that it matches the code below. 

```python
    password: str = Field(default="test_password", title="User Password")
    fractal: Fractal = Field(default_factory=Fractal) #Add Fractal Model
```

**4. Creating a FractalTab (`src/nova_tutorial/app/views/fractal_tab.py`):**

*   **Create a fractal tab**: Create a new file and add the following code:

@@ -206,8 +222,9 @@ Let\'s see how to implement the MVVM pattern using `nova-mvvm` and incorporate P
            )
            vuetify.VImg(src=("config.fractal.image_data",), height="400", width="400")
```
**4. Modify the tab panel (`src/nova_tutorial/views/tab_panel.py`):**
    Modify the tab panel to add our new Fractal tab

**5. Modify the tab panel (`src/nova_tutorial/app/views/tabs_panel.py`):**
*   **Add Fractal Tab to the tab panel**: Modify the tab panel to add our new Fractal tab

```python
        with vuetify.VTabs(v_model=("active_tab", 0), classes="pl-5"):
@@ -216,8 +233,14 @@ Let\'s see how to implement the MVVM pattern using `nova-mvvm` and incorporate P
            vuetify.VTab("Sample Tab 2", value=3)
```

**5. Modify the tab panel content (`src/nova_tutorial/views/tab_content_panel.py`):**
    Add our new Fractal Tab to the tab content panel.
**6. Modify the tab panel content (`src/nova_tutorial/app/views/tab_content_panel.py`):**
*   **Add FractalTab to imports**: Import the newly created FractalTab class into our tab_content_panel.

```python
from .fractal_tab import FractalTab  # Import the FractalTab
```

*   **Add the Fractal Tab to our existing tabs**: Add the Fractal Tab lines to the vuetify.VWindow section and modify the values.

```python
        with vuetify.VWindow(v_model="active_tab"):
@@ -229,6 +252,30 @@ Let\'s see how to implement the MVVM pattern using `nova-mvvm` and incorporate P
                SampleTab2()
```

**7. `main.py` - Calling the Model (`src/nova_tutorial/app/main.py`):**

We are now going to modify the existing `main.py` file. Change the main method to match the code below.

*   **Instantiate and Run**: In the `main()` function, we create an instance of `Fractal` and call the `run_fractal_tool()` method, wrapped in a `try...except` block for basic error handling:
```python
import sys
from .models.fractal import Fractal


def main() -> None:
    kwargs = {}
    from .views.main import MainApp

    app = MainApp()
    for arg in sys.argv[2:]:
        try:
            key, value = arg.split("=")
            kwargs[key] = int(value)
        except Exception:
            pass
    app.server.start(**kwargs)
```

## Running the application

To run the code, use the following command in the top level of your `nova_tutorial` project:
@@ -241,7 +288,7 @@ You should see `Fractal tool finished successfully.` printed to the console, alt

:::::::::::::::::::::::::::::::::::::::  challenge
**Trigger Pydantic Validation Error (Programmatic)**
    *   In `FractalViewModel` in `src/nova_tutorial/view_models/fractal_view_model.py`, modify the `update_fractal_programmatically` function from the previous exercise to use an *invalid* fractal type:
    *   In `FractalViewModel` in `src/nova_tutorial/app/view_models/fractal_view_model.py`, modify the `update_fractal_programmatically` function from the previous exercise to use an *invalid* fractal type:
        ```python
        def update_fractal_programmatically(new_type: str):
            self.fractal_type = new_type # Use the setter which includes validation
@@ -260,7 +307,7 @@ You should see `Fractal tool finished successfully.` printed to the console, alt

:::::::::::::::::::::::::::::::::::::::  challenge
**Inspect ViewModel State**
    *   In `src/nova_tutorial/view_models/fractal_view_model.py`, add `print` statements within the `FractalViewModel.__init__` method to print the initial values of `self._fractal_type`, `self._job_status`, and `self._message`.
    *   In `src/nova_tutorial/app/view_models/fractal_view_model.py`, add `print` statements within the `FractalViewModel.__init__` method to print the initial values of `self._fractal_type`, `self._job_status`, and `self._message`.
    *   Run the application (`poetry run app`). Observe the output in the console. Verify that the initial values are printed as expected.
    *   Now, modify the `FractalViewModel.__init__` method to change the initial value of `self._message` to "Application starting...". Run the application again and confirm that the printed initial message has changed.

@@ -268,7 +315,7 @@ You should see `Fractal tool finished successfully.` printed to the console, alt

:::::::::::::::::::::::::::::::::::::::  challenge
**Programmatic State Update and Binding:**
    *   In `FractalViewModel` in `src/nova_tutorial/view_models/fractal_view_model.py`, after the line `self.fractal_type_bind = binding.new_bind(...)` in `__init__`, add the following lines:
    *   In `FractalViewModel` in `src/nova_tutorial/app/view_models/fractal_view_model.py`, after the line `self.fractal_type_bind = binding.new_bind(...)` in `__init__`, add the following lines:
        ```python
        print("Initial fractal type:", self._fractal_type) # Print initial value

+3 −3
Original line number Diff line number Diff line
@@ -75,7 +75,7 @@ Layouts are responsible for arraging your content in a consistent manner. In Tra

`nova-trame` provides a basic layout and theme that you can access via the `ThemedApp` class. The template app will setup your main view class to inherit from `ThemedApp` already, so let\'s look at how the layout is defined and how we can add content to slots.

**1. `nova_tutorial/views/main.py`:**
**1. `nova_tutorial/app/views/main.py`:**

```python
class MainApp(ThemedApp):
@@ -215,7 +215,7 @@ For a more detailed explanation of how to work with our layout and theme, please

Now, let\'s add some UI components to the Sample Tabs in our application to demonstrate how to use these components. We\'ll modify the `sample_tab_1.py` and `sample_tab_2.py` files to include these components.

**2. `nova_tutorial/views/sample_tab_1.py` (Modify):**
**2. `nova_tutorial/app/views/sample_tab_1.py` (Modify):**

We\'ll add an `InputField` and a `VBoxLayout` to this tab.

@@ -239,7 +239,7 @@ class SampleTab1:
            RemoteFileInput(v_model="config.file", base_paths=["/SNS"])
```

**3. `nova_tutorial/views/sample_tab_2.py` (Modify):**
**3. `nova_tutorial/app/views/sample_tab_2.py` (Modify):**

We\'ll add a `GridLayout` and an `InputField` to this tab.