@@ -96,7 +96,7 @@ A comprehensive list of tools, and links to their XML, can be found in Calvera's
Let\'s create a `Fractal` class that uses `nova-galaxy` to run the `neutrons_fractal` tool on NDIP. You can find the complete code for this episode in the `code/episode_3` directory.
**1. `Fractal` Class (`src/nova_tutorial/app/models/fractal.py`):**
**1. `Fractal` Class (`src/nova_tutorial/app/models/fractal.py`) (Create):**
To get started, let\'s create the Fractal class. Create an empty file at `src/nova_tutorial/app/models/fractal.py`. Add the following pieces of code to the newly created file.
@@ -145,7 +145,7 @@ Note that we create a `Tool` object with the `id="neutrons_fractal"`. This tells
The line `data_store.persist()` saves your datastore after the "with" block is exited. Without calling this method, all tools, running or finished, along with their results will be discarded after the "with" block finishes execution.
**2. `main.py` - Calling the Model (`src/nova_tutorial/app/main.py`):**
**2. `main.py` - Calling the Model (`src/nova_tutorial/app/main.py`) (Modify):**
We are now going to modify the existing `main.py` file. Change the main method to match the code below.
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/app/view_models/main.py`):**
**1. Adding Fractal to the ViewModel (`src/nova_tutorial/app/view_models/main.py`) (Modify):**
***Running our Model**: We start by adding a method to bottom of our ViewModel which will run the Fractal tool.
@@ -156,7 +156,7 @@ 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/app/models/fractal.py`)**
**2. Updating our Fractal Class for pydantaic and MVVM (`src/nova_tutorial/app/models/fractal.py`) (Modify)**
***Adding new imports**: We need to add some imports for pydantic and working with base64 encodings to deal with the image. Modify your import block to match below.
* In `Fractal` in `src/nova_tutorial/app/models/fractal.py`, modify the `set_fractal_type` function from the previous exercise to use an *invalid* fractal type:
@@ -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.
@@ -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.
We\'ll add a `GridLayout` and an `InputField` to this tab.
@@ -295,7 +295,7 @@ You should now see the simple UI. When you click the "Sample Tab 1" and "Sample
Now that we understand the basics of working with Trame, let\'s make the view for the fractal tab a bit more intuitive for the user by giving them a visual indicator that the job is running.
@@ -316,7 +316,7 @@ Now that we understand the basics of working with Trame, let\'s make the view fo
We will need to add a data binding for `running`, as well. We choose to place this directly in the view model as this is not relevant to running the fractal tool on NDIP.
Pydantic uses Python type hints to define data models. When you create an instance of a Pydantic model, Pydantic automatically validates the input data against the defined types and constraints.
Here\'s a simple example (add this code to `src/advanced_pydantic/main.py`):
1. Create a User Model (`src/advanced_pydantic/main.py`) (Modify):
```python
frompydanticimportBaseModel,Field
@@ -104,7 +104,7 @@ In this example, we define a `User` model with two fields: `id` and `name`. We u
When you create an instance of the `User` model, Pydantic automatically validates the input data.
Modify the main function in `src/advanced_pydantic/main.py`
2. Create an instance of a User (`src/advanced_pydantic/main.py`) (Modify):
```python
frompydanticimportValidationError
@@ -130,7 +130,7 @@ If the input data is invalid, Pydantic raises a `ValidationError` exception with
When working with structured data, it\'s common to have nested objects. For example, a User model from the above example might have multiple Address entries. In Pydantic, we can achieve this by creating nested models.
1. Creating the Address Model (add code to `src/advanced_pydantic/main.py`).
1. Creating the Address Model (`src/advanced_pydantic/main.py`) (Modify).
The Address model represents a simple address with three fields:
@@ -150,7 +150,7 @@ class Address(BaseModel):
type:Literal["home","work"]=Field()
```
2. Using the Address Model as a Nested Field (modify User model in `src/advanced_pydantic/main.py`).
2. Using the Address Model as a Nested Field (`src/advanced_pydantic/main.py`) (Modify).
Update the User model so that it now contains:
@@ -167,7 +167,7 @@ class User(BaseModel):
addresses:List[Address]=Field(min_length=1)
```
now you can try to test the model. Modify the main function in `src/advanced_pydantic/main.py`
3. Test the model (`src/advanced_pydantic/main.py`) (Modify).
```python
defmain()->None:
@@ -202,7 +202,9 @@ For easier integration with the NOVA framework, where model field information is
Sometimes, simple validation like checking the minimum length is not enough. In such cases, you can write a custom validation function for a specific field.
For example, let\'s say we have a User model where only even IDs are allowed. We can enforce this constraint using the `@field_validator decorator` (modify `src/advanced_pydantic/main.py`):
For example, let\'s say we have a User model where only even IDs are allowed. We can enforce this constraint using the `@field_validator decorator`.
4. Using the `@field_validator decorator` (`src/advanced_pydantic/main.py`) (Modify):
```python
frompydanticimportBaseModel,Field,field_validator
@@ -241,7 +243,9 @@ Note that we used the mode="**after**" option for the validator. This ensures th
In some cases, you may need to validate the entire model, not just individual fields. This can be done by writing a custom validation function for the whole model using the `@model_validator` decorator.
For example, let\'s say we have a User model where the name and id must meet specific conditions together. For instance, we only allow users with even IDs to have names that start with a capital letter. We can enforce this logic using a @model_validator (modify `src/advanced_pydantic/main.py`):
For example, let\'s say we have a User model where the name and id must meet specific conditions together. For instance, we only allow users with even IDs to have names that start with a capital letter. We can enforce this logic using a @model_validator.
5. Using the `@model_validator decorator` (`src/advanced_pydantic/main.py`) (Modify):
```python
frompydanticimportBaseModel,Field,model_validator
@@ -327,7 +331,7 @@ poetry install
One of the great features of the NOVA Framework is that it allows an application to leverage Pydantic models to automatically validate UI elements. Let\'s walk through what that looks like in code.
First, let\'s add the following Model (create `src/trame_with_pydantic/app/models/settings.py`):
1.First, let\'s add the following Model (`src/trame_with_pydantic/app/models/settings.py`) (Create):
```python
frompydanticimportBaseModel,Field
@@ -336,7 +340,9 @@ class SettingsModel(BaseModel):
port:int=Field(default=8080,gt=0,lt=65536,title="Port Number",description="The port to listen on.",examples=["12345"])
```
Then in your ViewModel, you createa binding for this Model (modify `src/trame_with_pydantic/app/view_models/main.py` and clean up the code created by the template engine, we don't need it for this example):
2. Create a binding for the model (`src/trame_with_pydantic/app/view_models/main.py`) (Modify):
In your ViewModel, create a binding for this Model and clean up the code created by the template engine, we don't need it for this example):
```python
fromtypingimportAny,Dict
@@ -353,7 +359,7 @@ class MainViewModel:
self.settings_bind.update_in_view(self.settings)
```
In your view, remove all other fields and add the following InputField (modify `src/trame_with_pydantic/app/views/main.py`):
3.In your view, remove all other fields and add the following InputField (`src/trame_with_pydantic/app/views/main.py`) (Modify):
```python
...
@@ -370,7 +376,9 @@ In that fashion, the `InputField` seamlessly pulls information from your code\'s
### Using callbacks in ViewModel to react to validation errors
Sometimes, you may want to respond to UI validation errors beyond just marking a field as invalid (which happens automatically). In such cases, you can add a callback to the `new_bind` function (modify `src/trame_with_pydantic/app/view_models/main.py`):
Sometimes, you may want to respond to UI validation errors beyond just marking a field as invalid (which happens automatically). In such cases, you can add a callback to the `new_bind` function.
4. Using callbacks with `new_bind` (`src/trame_with_pydantic/app/view_models/main.py`) (modify):
@@ -91,7 +91,7 @@ The pandas install is only necessary for loading example data from Plotly, which
Now, we can create a view that displays a Plotly figure.
**1. `PlotlyView` View Class (`src/viz_examples/views/plotly.py`):**
**1. `PlotlyView` View Class (`src/viz_examples/views/plotly.py`) (Create):**
***Imports**: Pay special attention to the plotly import. This module contains a Trame widget that will allow us to quickly add a Plotly chart to our view.
@@ -151,7 +151,7 @@ class PlotlyView:
As with our previous examples, there is a corresponding model.
**2. `PlotlyConfig` Model Class (src/viz_examples/models/plotly.py):**
**2. `PlotlyConfig` Model Class (src/viz_examples/models/plotly.py) (Create):**
***Imports**: The graph_objects module is how we will define the content for our chart. The iris module defines an example dataset.