diff --git a/episodes/03-Nova-Galaxy.md b/episodes/03-Nova-Galaxy.md index e8ef3584dc3a6a3379bc2a3929f34c12a387a3d6..863f59580752c6b3180476efc8450689fdcc0354 100755 --- a/episodes/03-Nova-Galaxy.md +++ b/episodes/03-Nova-Galaxy.md @@ -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. diff --git a/episodes/04-MVVM-Design-Pattern.md b/episodes/04-MVVM-Design-Pattern.md index e4fee0d3775572bf8c1a2a4327592de247894eba..4b4069bcebf8f5bd61df347d279ab42b4e390088 100755 --- a/episodes/04-MVVM-Design-Pattern.md +++ b/episodes/04-MVVM-Design-Pattern.md @@ -146,7 +146,7 @@ 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/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. @@ -191,7 +191,7 @@ class Fractal(BaseModel): self.image_data = f"data:image/png;base64,{b64encode(image_file.read()).decode()}" ``` -**3. Updating our MainModel Class to add the new Fractal Class (`src/nova_tutorial/app/models/main_model.py`)** +**3. Updating our MainModel Class to add the new Fractal Class (`src/nova_tutorial/app/models/main_model.py`) (Modify):** * **Add Fractal to imports**: Add an import for the Fractal class into our MainModel. @@ -206,7 +206,7 @@ from .fractal import Fractal # Import Fractal fractal: Fractal = Field(default_factory=Fractal) #Add Fractal Model ``` -**4. Creating a FractalTab (`src/nova_tutorial/app/views/fractal_tab.py`):** +**4. Creating a FractalTab (`src/nova_tutorial/app/views/fractal_tab.py`) (Create):** * **Create a fractal tab**: Create a new file and add the following code: @@ -230,7 +230,7 @@ class FractalTab: vuetify.VImg(src=("config.fractal.image_data",), height="400", width="400") ``` -**5. Modify the tab panel (`src/nova_tutorial/app/views/tabs_panel.py`):** +**5. Modify the tab panel (`src/nova_tutorial/app/views/tabs_panel.py`) (Modify):** * **Add Fractal Tab to the tab panel**: Modify the tab panel to add our new Fractal tab @@ -241,7 +241,7 @@ class FractalTab: vuetify.VTab("Sample Tab 2", value=3) ``` -**6. Modify the tab panel content (`src/nova_tutorial/app/views/tab_content_panel.py`):** +**6. Modify the tab panel content (`src/nova_tutorial/app/views/tab_content_panel.py`) (Modify):** * **Add FractalTab to imports**: Import the newly created FractalTab class into our tab_content_panel. @@ -261,7 +261,7 @@ from .fractal_tab import FractalTab # Import the FractalTab SampleTab2() ``` -**7. `main.py` - Calling the Model (`src/nova_tutorial/app/main.py`):** +**7. `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. @@ -303,6 +303,7 @@ If you don't want Trame to launch a tab by default, you can instead run ```poetr ::::::::::::::::::::::::::::::::::::::: challenge **Trigger Pydantic Validation Error (Programmatic)** + * 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: ```python diff --git a/episodes/05-Working-with-Trame.md b/episodes/05-Working-with-Trame.md index 206bb2c3033dc2a7ddb5e4e80f44813653f922ec..2af872b59ed67284bb3fae4325b5c943dbafedbe 100755 --- a/episodes/05-Working-with-Trame.md +++ b/episodes/05-Working-with-Trame.md @@ -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/app/views/main.py`:** +**1. `src/nova_tutorial/app/views/main.py` (Modify):** ```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/app/views/sample_tab_1.py` (Modify):** +**2. `src/nova_tutorial/app/views/sample_tab_1.py` (Modify):** We\'ll add an `InputField` and a `VBoxLayout` to this tab. @@ -241,7 +241,7 @@ class SampleTab1: Since `config.file` doesn't exist yet, we\'ll need to add it to the model. -**3. `nova_tutorial/app/models/main_model.py` (Modify):** +**3. `src/nova_tutorial/app/models/main_model.py` (Modify):** ```python username: str = Field( @@ -256,7 +256,7 @@ Since `config.file` doesn't exist yet, we\'ll need to add it to the model. fractal: Fractal = Field(default_factory=Fractal) ``` -**4. `nova_tutorial/app/views/sample_tab_2.py` (Modify):** +**4. `src/nova_tutorial/app/views/sample_tab_2.py` (Modify):** 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. -**5. `nova_tutorial/app/views/fractal_tab.py` (Modify):** +**5. `src/nova_tutorial/app/views/fractal_tab.py` (Modify):** ```python def __init__(self, view_model: MainViewModel) -> None: @@ -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. -**6. `nova_tutorial/app/view_models/main.py` (Modify):** +**6. `src/nova_tutorial/app/view_models/main.py` (Modify):** ```python def __init__(self, model: MainModel, binding: BindingInterface): diff --git a/episodes/06-Advanced-Data-Modeling.md b/episodes/06-Advanced-Data-Modeling.md index 8d394b41244f09c03b62e0be1f1afd30ead979de..bdd0d704334e516d9570d39d4bc3a68f3049fd78 100755 --- a/episodes/06-Advanced-Data-Modeling.md +++ b/episodes/06-Advanced-Data-Modeling.md @@ -90,7 +90,7 @@ poetry install 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 from pydantic import BaseModel, 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 from pydantic import ValidationError @@ -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 def main() -> 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 from pydantic import BaseModel, 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 from pydantic import BaseModel, 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 from pydantic import BaseModel, 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 from typing import Any, 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): ```python class MainViewModel: diff --git a/episodes/07-Advanced-Visualizations.md b/episodes/07-Advanced-Visualizations.md index ffa6d6a322c582ec5ba01e99406f03cadf85868e..9e1683e027507e56d5a6df961130c57c74d838d2 100755 --- a/episodes/07-Advanced-Visualizations.md +++ b/episodes/07-Advanced-Visualizations.md @@ -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.