diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml old mode 100644 new mode 100755 index f59a678ba0f1a90ed3f43adb32f5ec7e2461e893..aa8ab65127f3a91600ab2a436a6d8542020a97e7 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -36,7 +36,7 @@ pages: - docker run -v `pwd`/public:/app/site tutorial Rscript -e "sandpaper::build_lesson()" - cp $CALVERA_DOCS_SSH_KEY key && chmod 600 key - cat key - - rsync -avz -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i key" public/docs/ cloud@10.64.195.250:/home/cloud/nova-tutorial/tutorial + - rsync -avz --mkpath -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i key" public/docs/ cloud@10.64.196.237:/home/cloud/nova-tutorial/tutorial artifacts: paths: - public @@ -46,6 +46,26 @@ pages: - if: $CI_COMMIT_REF_NAME != $CI_DEFAULT_BRANCH when: manual - when: on_success - tags: - rse-multi-builder + +preview: + stage: deploy + script: + - docker build -t tutorial -f dockerfiles/Dockerfile . + - mkdir public + - docker run -v `pwd`/public:/app/site tutorial Rscript -e "sandpaper::build_lesson()" + - cp $CALVERA_DOCS_SSH_KEY key && chmod 600 key + - cat key + - rsync -avz --mkpath -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i key" public/docs/ cloud@10.64.196.237:/home/cloud/nova-tutorial/tutorial/preview + artifacts: + paths: + - public + rules: + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + when: never + - if: $CI_COMMIT_BRANCH == "Preview" + when: manual + - when: on_success + tags: + - rse-multi-builder \ No newline at end of file diff --git a/code/episode_4/src/nova_tutorial/app/main.py b/code/episode_4/src/nova_tutorial/app/main.py index add49c1138d458f114cf3804dae7bc507d8446f4..2977dbe12154af3fdb3492345ca13d194b1bd09a 100755 --- a/code/episode_4/src/nova_tutorial/app/main.py +++ b/code/episode_4/src/nova_tutorial/app/main.py @@ -1,7 +1,6 @@ """Main Application.""" import sys -from .models.fractal import Fractal def main() -> None: diff --git a/code/episode_5/src/nova_tutorial/app/views/sample_tab_1.py b/code/episode_5/src/nova_tutorial/app/views/sample_tab_1.py index ccdfc4e2696a8d1a7f1df1642278df3ae7e46565..5f5fd62c37ea0c629f38ad62fab101111564fb48 100755 --- a/code/episode_5/src/nova_tutorial/app/views/sample_tab_1.py +++ b/code/episode_5/src/nova_tutorial/app/views/sample_tab_1.py @@ -10,5 +10,5 @@ class SampleTab1: self.create_ui() def create_ui(self) -> None: - RemoteFileInput(v_model="file", base_paths=["/HFIR", "/SNS"]) + RemoteFileInput(v_model="config.file", base_paths=["/HFIR", "/SNS"]) InputField(v_model="config.username") diff --git a/episodes/01-Introduction.md b/episodes/01-Introduction.md index 9452e5863ca69f4932f7adf28af13638baf7645d..b3220fe66ae6a228320f5225fb473135fb719618 100755 --- a/episodes/01-Introduction.md +++ b/episodes/01-Introduction.md @@ -93,6 +93,12 @@ poetry install # Install dependencies for this episode poetry run app # Run the application for this episode ``` +::::::::::::::::::::::::::::::::::::::::: callout + +If you are using the analysis cluster for the tutorial, then please note that `poetry run app` will by default attempt to bind to port 8080 and will fail if the port is already in use. This can happen if others are on the same node as you running the same commands. If this happens, you can change the port the application binds to with `poetry run app --port {myport}`. + +:::::::::::::::::::::::::::::::::::::::::::::::: + This structure ensures that each code example is isolated and runnable, making it easier for you to follow along with the tutorial and experiment with the code. ## References diff --git a/episodes/02-Copy-Template.md b/episodes/02-Copy-Template.md index 4db170b55b3f7b25a325ab6810de48df90fc7946..6267abff6af17db7cf3a331c37c396290f3f2d65 100755 --- a/episodes/02-Copy-Template.md +++ b/episodes/02-Copy-Template.md @@ -36,7 +36,7 @@ The setup section detailed the prerequisites required for the tutorial. One of t To clone the template application, run the following command: ```bash -copier copy https://code.ornl.gov/ndip/project-templates/nova-application-template.git nova_tutorial +copier copy https://code.ornl.gov/ndip/project-templates/nova-application-template-tutorial.git nova_tutorial ``` This command will download the template to a directory called `nova_tutorial`. Copier will prompt you with a series of questions. Please answer the questions as follows: @@ -170,6 +170,101 @@ The template includes a basic GitLab CI configuration file (`.gitlab-ci.yml`). ::::::::::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::::: +## Deploying Your Tool to NDIP + +Now that we have our template application set up, we need to integrate it with the NDIP platform. The template includes built-in utilities to streamline this process, handling the GitLab repository setup and Galaxy tool XML management. + +### Initialize Your Project Repository + +You can initialize your Git repository and push it to the correct location in the NDIP GitLab: + +```bash +poetry run init-repo +``` + +::::::::::::::::::::::::::::::::::::::::: callout +If prompted for a username and password by GitLab, then please use your three-character ID as the username and the Personal Access Token you set up earlier as the password. +:::::::::::::::::::::::::::::::::::::::::::::::: + +This script will: + +1. Initialize a Git repository (if not already done) +2. Set up the remote to point to the configured repository URL +3. Add all project files to the repository +4. Create an initial commit (if needed) +5. Push the code to the GitLab repository + +### Continuous Integration and Container Building + +Once your code is pushed to GitLab, the included CI/CD pipeline will automatically build a Docker container for your application. The CI configuration is already set up in the `.gitlab-ci.yml` file and includes: + +1. Running tests to verify your code works correctly +2. Building a Docker image containing your application +3. Pushing the image to the Harbor container registry (at `savannah.ornl.gov/ndip/tool-sources/tutorial/YOUR_USERNAME-nova-tutorial`) + +The Docker image tag is derived from your project's version in `pyproject.toml`. Each time you update the version and push, a new container will be built automatically. + +### Tool XML File + +The template has already generated a Galaxy tool XML file for your project. You can find this file at: + +``` +xml/tool.xml +``` + +This file defines how your tool appears and functions within the NDIP platform. It includes: + +- A unique tool ID (now manually configured for the tutorial) +- The correct container reference pointing to your GitLab repository +- Command to run your application inside the container +- Help and description text for users + +After the manual changes we made in the previous step, your tool XML will be correctly configured for the tutorial environment. + +### Pushing the Tool XML to Galaxy Tools Repository + +To deploy your tool to the NDIP platform, you need to add the XML file to the galaxy-tools repository. The template includes a utility for this: + +```bash +poetry run deploy-tool +``` + +This script will: + +1. Clone the Galaxy tools repository +2. Copy your tool XML file to the correct location (now configured as `tools/neutrons/tutorials/YOUR_USERNAME-nova-tutorial.xml`) +3. Commit the changes +4. Push to the `prototype` branch of the galaxy-tools repository + +Once your XML file is pushed to the prototype branch, an automated CI job will deploy your tool to the calvera-test instance. You can then access your tool through the NDIP web interface at https://calvera-test.ornl.gov. + +::::::::::::::::::::::::::::::::::::::::: callout +The tool XML utility has been enhanced to check for the existence of your Docker image before proceeding with the push. This helps prevent deployment errors by ensuring your container has been built first. +:::::::::::::::::::::::::::::::::::::::::::::::: + +### Understanding Your Tool's Integration + +Let's understand the key components that make your tool work in NDIP: + +1. **Repository Structure**: + - Your code is hosted at `https://code.ornl.gov/ndip/tool-sources/tutorial/YOUR_USERNAME-nova-tutorial` + - The Docker container is built automatically by CI and stored at `code.ornl.gov:4567/ndip/tool-sources/tutorial/YOUR_USERNAME-nova-tutorial` + +2. **Tool XML File**: + - Defines your tool for Galaxy/NDIP + - References your container so NDIP knows which image to run + - Configures the command to run your application + - Is stored in the galaxy-tools repository at `tools/neutrons/tutorials/YOUR_USERNAME-nova-tutorial.xml` + +3. **Deployment Process**: + - When you push code to your repository → CI builds a new container + - When you run `push-xml` → The utility checks if your container exists and pushes your tool XML to the galaxy-tools prototype branch + - After XML is merged → Your tool appears in the NDIP interface + +::::::::::::::::::::::::::::::::::::::::: callout +In a production environment, when your tool is ready for users, you would create a merge request from the prototype branch to the dev branch. The NDIP team reviews these changes, merges them, and your tool will be deployed to the production instance during the next deployment. +:::::::::::::::::::::::::::::::::::::::::::::::: + ## References * **Nova Documentation**: https://nova-application-development.readthedocs.io/en/latest/ @@ -180,7 +275,10 @@ The template includes a basic GitLab CI configuration file (`.gitlab-ci.yml`). :::::::::::::::::::::::::::::::::::::::: keypoints - Nova provides a template application to help get started developing your application. -- Use the copier tool to set clone the template application. +- Use the copier tool to clone the template application. - Poetry is a project management tool used to install dependencies and manage virtual environments. - The template application includes everything you need to get started such as basic CI, dockerfile, and tests. +- Docker containers package your application and all its dependencies for deployment. +- Galaxy tool XML files define how your tool appears and functions in NDIP. +- Tools are deployed by adding their XML files to the galaxy-tools repository's prototype branch. :::::::::::::::::::::::::::::::::::::::::::::::::: diff --git a/episodes/04-MVVM-Design-Pattern.md b/episodes/04-MVVM-Design-Pattern.md index bc7714a6b80dbd800af4fb0d78826863c8cb944e..06ba11d5450c11cf7286e79fa2e7bd77ccc0bab2 100755 --- a/episodes/04-MVVM-Design-Pattern.md +++ b/episodes/04-MVVM-Design-Pattern.md @@ -142,78 +142,77 @@ Finally, we connect a UI component to the connector object. The template applica InputField(v_model="config.username") ``` -## Implementing MVVM with `nova-mvvm` and Pydantic +## Project Structure -Let\'s see how to implement the MVVM pattern using `nova-mvvm` and incorporate Pydantic for data validation. +The template creates a well-organized project structure following best practices, including the Model-View-ViewModel (MVVM) design pattern. This structure promotes code maintainability, testability, and separation of concerns. Here's a breakdown of the key directories and files: -**1. Adding Fractal to the ViewModel (`src/nova_tutorial/app/view_models/main.py`) (Modify):** +* `nova_tutorial/`: The root directory of your project. This is the top-level directory containing all project files and subdirectories. -* **Running our Model**: We start by adding a method to bottom of our ViewModel which will run the Fractal tool. +* `nova_tutorial/src/`: This directory contains all the source code for your application. The separation into `src` helps distinguish your application code from configuration files, tests, and other project-related files that reside in the root. -```python - def run_fractal(self) -> None: - self.model.fractal.run_fractal_tool() - self.update_view() -``` +* `nova_tutorial/src/nova_tutorial/`: This is the main Python package for your application. Its name (`nova_tutorial` in this case) is used when importing modules within your project. Inside this directory, you'll find the core application logic, organized according to the MVVM pattern: -**2. Updating our Fractal Class for pydantaic and MVVM (`src/nova_tutorial/app/models/fractal.py`) (Modify)** + * `nova_tutorial/src/nova_tutorial/app/`: This directory contains the main application logic, further subdivided to reflect the MVVM structure. -* **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. + * `nova_tutorial/src/nova_tutorial/app/models/`: **(Model)** This is where you define your data models and business logic. These classes represent the data your application works with and the rules for manipulating that data. -```python -import os -from base64 import b64encode -from typing import Literal + * `nova_tutorial/src/nova_tutorial/app/view_models/`: **(ViewModel)** This directory holds the ViewModels. These classes act as intermediaries between the Models and the Views. They prepare data for display and handle user interactions from the View. -from pydantic import BaseModel, Field -from nova.galaxy import Connection, Parameters, Tool -``` + * `nova_tutorial/src/nova_tutorial/app/views/`: **(View)** This directory contains the user interface (UI) components. These are built using Trame and Vuetify (via `nova-trame`). They are responsible for displaying data and capturing user input. -* **Update class variables:** Now we will update fractal_type and other class variables to support pydantic. We will also add an image variable to store the image. Modify the variable declarations to the following: + * `nova_tutorial/src/nova_tutorial/app/main.py`: The entry point for your NOVA application. This file initializes and starts the Trame server and the `MainApp` view. -```python -class Fractal(BaseModel): - fractal_type: Literal["mandelbrot", "julia", "random", "markus"] = Field(default="mandelbrot") - galaxy_url: str = Field(default_factory=lambda: os.getenv("GALAXY_URL"), description="NDIP Galaxy URL") - galaxy_key: str = Field(default_factory=lambda: os.getenv("GALAXY_API_KEY"), description="NDIP Galaxy API Key") - image_data: str = Field(default="", description="Base64 encoded PNG") +* `nova_tutorial/tests/`: Contains unit tests for your application. A well-structured project should include tests to ensure code quality and prevent regressions. The tests are typically organized to mirror the structure of your application code (e.g., tests for models, view models, and potentially UI components). - def set_fractal_type(self, fractal_type: str): - self.fractal_type = fractal_type -``` +* `nova_tutorial/README.md`: A Markdown file providing a description of your project, instructions for setup and usage, and any other relevant information. -* **Decode the image data:** Finally, we need to decode the image that we receive as the output from the tool execution. Modify the section where we execute the tool to the following: +* `pyproject.toml`: A configuration file for Poetry, the dependency management and packaging tool used by NOVA. It specifies project dependencies, build settings, and other metadata. -```python - output.get_dataset("output").download("tmp.png") +## Implementing MVVM with `nova-mvvm` and Pydantic - with open("tmp.png", "rb") as image_file: - self.image_data = f"data:image/png;base64,{b64encode(image_file.read()).decode()}" -``` +Let's implement the MVVM pattern, starting with the UI and basic ViewModel connections, and then building up the Model functionality. -**3. Updating our MainModel Class to add the new Fractal Class (`src/nova_tutorial/app/models/main_model.py`) (Modify):** +**1. Initial Setup and ViewModel Basics** -* **Add Fractal to imports**: Add an import for the Fractal class into our MainModel. +First, let's simplify our `main.py` and set up the basic structure of our UI and ViewModel interaction. We'll create a button in the UI that, when clicked, will eventually run our Fractal tool. For now, it will just trigger a placeholder method in the ViewModel. -```python -from .fractal import Fractal # Import Fractal +* **`main.py` - Simplifying the Application Entry Point (`src/nova_tutorial/app/main.py`) (Modify):** + +We're removing the direct Fractal tool execution from `main()`. The application will now solely focus on launching the NOVA app. + + ```python +import sys + +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) ``` -* **Add the Fractal Model to the MainModel**: Modify the end of the MainModel class so that it matches the code below. +* **Adding a Placeholder Method to the ViewModel (`src/nova_tutorial/app/view_models/main.py`) (Modify):** + +Add a `run_fractal` method to the `MainViewModel`. For now, it just prints a message to the console. This confirms that the button click is connected to the ViewModel. ```python - password: str = Field(default="test_password", title="User Password") - fractal: Fractal = Field(default_factory=Fractal) #Add Fractal Model + def run_fractal(self) -> None: + print("run_fractal method called!") ``` -**4. Creating a FractalTab (`src/nova_tutorial/app/views/fractal_tab.py`) (Create):** +* **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: +This is the UI for our Fractal interaction. It includes a button that calls the `run_fractal` method in the ViewModel. We don't have image display yet. ```python from trame.widgets import vuetify3 as vuetify -from nova.trame.view.components import InputField from nova_tutorial.app.view_models.main import MainViewModel class FractalTab: @@ -225,14 +224,13 @@ class FractalTab: InputField(v_model="config.fractal.fractal_type") vuetify.VBtn( "Run Fractal", - click=self.view_model.run_fractal # calls the run_fractal_tool method - ) - vuetify.VImg(src=("config.fractal.image_data",), height="400", width="400") + click=self.view_model.run_fractal + ) ``` -**5. Modify the tab panel (`src/nova_tutorial/app/views/tabs_panel.py`) (Modify):** +* **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 +Add the "Fractal" tab to the tab bar. ```python with vuetify.VTabs(v_model=("active_tab", 0), classes="pl-5"): @@ -241,17 +239,14 @@ class FractalTab: vuetify.VTab("Sample Tab 2", value=3) ``` -**6. Modify the tab panel content (`src/nova_tutorial/app/views/tab_content_panel.py`) (Modify):** +* **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. +Display the `FractalTab` content when the "Fractal" tab is selected. ```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 + # ... (rest of the file) ... with vuetify.VWindow(v_model="active_tab"): with vuetify.VWindowItem(value=1): FractalTab(self.view_model) # Add FractalTab @@ -261,40 +256,93 @@ from .fractal_tab import FractalTab # Import the FractalTab SampleTab2() ``` -**7. `main.py` - Calling the Model (`src/nova_tutorial/app/main.py`) (Modify):** +**Demonstration (Initial UI and ViewModel Connection):** + +Run the application: `poetry run app` + +You should see a new "Fractal" tab in the application. Click the "Run Fractal" button. You should see "run_fractal method called!" printed in your terminal. This demonstrates that the button click in the View is successfully triggering the `run_fractal` method in the ViewModel, even though the method doesn't do anything substantial yet. This establishes the basic MVVM wiring. + +**2. Fractal Model and Pydantic Integration** + +Now, let's build out the `Fractal` model using Pydantic and integrate it into our `MainModel`. -We are now going to modify the existing `main.py` file. Change the main method to match the code below. +* **Updating our Fractal Class for pydantaic and MVVM (`src/nova_tutorial/app/models/fractal.py`) (Modify)** -* **Instantiate and Run**: In the `main()` function, we no longer need to setup the Fractal tool as it's managed via our MVVM application now. +* **Adding new imports**: Add imports for Pydantic and base64 handling. ```python -import sys -from .models.fractal import Fractal +import os +from base64 import b64encode +from typing import Literal +from pydantic import BaseModel, Field +from nova.galaxy import Connection, Parameters, Tool + ``` -def main() -> None: - kwargs = {} - from .views.main import MainApp +* **Update class variables:** Use Pydantic's `Field` for type hinting and validation. Add the `image_data` field. - app = MainApp() - for arg in sys.argv[2:]: - try: - key, value = arg.split("=") - kwargs[key] = int(value) - except Exception: - pass - app.server.start(**kwargs) +```python +class Fractal(BaseModel): + fractal_type: Literal["mandelbrot", "julia", "random", "markus"] = Field(default="mandelbrot") + galaxy_url: str = Field(default_factory=lambda: os.getenv("GALAXY_URL"), description="NDIP Galaxy URL") + galaxy_key: str = Field(default_factory=lambda: os.getenv("GALAXY_API_KEY"), description="NDIP Galaxy API Key") + image_data: str = Field(default="", description="Base64 encoded PNG") + + def set_fractal_type(self, fractal_type: str): + self.fractal_type = fractal_type ``` -## Running the application +* **Decode the data:** Update how the image is decoded. +```python + output.get_dataset("output").download("tmp.png") -To run the code, use the following command in the top level of your `nova_tutorial` project: + with open("tmp.png", "rb") as image_file: + self.image_data = f"data:image/png;base64,{b64encode(image_file.read()).decode()}" +``` + +* **Updating our MainModel Class to add the new Fractal Class (`src/nova_tutorial/app/models/main_model.py`) (Modify):** + +Import and include the `Fractal` model as a field in the `MainModel`. + +```python +from .fractal import Fractal # Import Fractal -```bash -poetry run app +class MainModel(BaseModel): + # ... (other fields) ... + password: str = Field(default="test_password", title="User Password") + fractal: Fractal = Field(default_factory=Fractal) #Add Fractal Model ``` -The application should launch a tab in your web browser. The GUI will have a `FRACTAL` tab and a few sample tabs which were created by the template application. The run button on the `Fractal` tab can be used to launch the `Fractal` NDIP tool. The tool will take a few minutes to complete but when it does, the resulting `Fractal` image will be displayed. +* **Connect the UI elements in FractalTab (`src/nova_tutorial/app/views/fractal_tab.py`) (Modify):** + +Update the create UI section to use InputField and the image. +```python +from nova.trame.view.components import InputField + + # ...(rest of file)... + def create_ui(self) -> None: + InputField(v_model="config.fractal.fractal_type") + vuetify.VBtn( + "Run Fractal", + click=self.view_model.run_fractal + ) + vuetify.VImg(src=("config.fractal.image_data",), height="400", width="400") +``` + +* **Add Full Functionality to the View Model (`src/nova_tutorial/app/view_models/main.py`) (Modify)** +Update the code in the run_fractal method. + +```python + def run_fractal(self) -> None: + self.model.fractal.run_fractal_tool() + self.update_view() +``` + +**Final Demonstration (Full Application):** + +Run the application: `poetry run app` + +Now, when you click "Run Fractal," the Fractal tool will execute in Galaxy, and the resulting image will be displayed in the UI. You can also change the `fractal_type` using the input field. This demonstrates the complete MVVM flow, with data binding, Pydantic validation, and the interaction between the View, ViewModel, and Model. ::::::::::::::::::::::::::::::::::::::::: callout If you don't want Trame to launch a tab by default, you can instead run ```poetry run app --server```. diff --git a/episodes/05-Working-with-Trame.md b/episodes/05-Working-with-Trame.md index fb210aacbd592583ad77b204f54a3edc7575bb92..8e7aac5c677f25ccc76cf008624dcbbf90150133 100755 --- a/episodes/05-Working-with-Trame.md +++ b/episodes/05-Working-with-Trame.md @@ -78,6 +78,22 @@ Layouts are responsible for arraging your content in a consistent manner. In Tra **1. `src/nova_tutorial/app/views/main.py` (Modify):** ```python +import logging + +from nova.mvvm.trame_binding import TrameBinding +from nova.trame import ThemedApp +from nova.trame.view import layouts +from trame.app import get_server +from trame.widgets import vuetify3 as vuetify + +from ..mvvm_factory import create_viewmodels +from ..view_models.main import MainViewModel +from .tab_content_panel import TabContentPanel +from .tabs_panel import TabsPanel + +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + class MainApp(ThemedApp): """Main application view class. Calls rendering of nested UI elements.""" @@ -184,10 +200,33 @@ class SampleTab1: self.create_ui() def create_ui(self) -> None: - RemoteFileInput(v_model="file", base_paths=["/HFIR", "/SNS"]) + RemoteFileInput(v_model="config.file", base_paths=["/HFIR", "/SNS"]) InputField(v_model="config.username") ``` +**6. `src/nova_tutorial/app/models/main_model.py` (Modify):** + +Add a `file` field to the `MainModel` to store the selected file path. We use `Optional[str]` because initially, no file will be selected. + +```python +from pydantic import BaseModel, Field +from .fractal import Fractal + + +class MainModel(BaseModel): + username: str = Field( + default="test_name", + min_length=1, + title="User Name", + description="Please provide the name of the user", + examples=["user"], + ) + password: str = Field(default="test_password", title="User Password") + file: str = Field(default="", title="Select a File") + fractal: Fractal = Field(default_factory=Fractal) +``` + + :::::::::::::::::::::::::::::::: callout If you want to connect your application to the analysis cluster, then it will need to be run on a computer where the filesystem is mounted. If your application is deployed through our platform, then we can ensure that your application runs in the correct environment to support your needs. @@ -252,7 +291,7 @@ By combining these layout components, you can create complex and responsive UI l As an example, we can use the layout classes to center the "Run Fractal" button. -**6. `src/nova_tutorial/app/views/main.py` (Modify):** +**7. `src/nova_tutorial/app/views/main.py` (Modify):** ```python from nova.trame.view import layouts @@ -267,6 +306,14 @@ from nova.trame.view import layouts ) ``` +:::::::::::::::::::::::::::::::::::::::: callout + +In the above example, we use the `classes` parameter to `HBoxLayout` to add the `my-2` CSS class to the element. This parameter can be used on any Trame component to customize your interface's appearance without having to write CSS. + +The `my-2` class is provided by [Vuetify](https://vuetifyjs.com/) and gives the element vertical margin (space above and below the element). https://vuetifyjs.com/en/styles/spacing documents this class and other classes related to spacing. There are also many other pages on the Vuetify docs describing classes that together give you a wide range of options for customizing your interface. + +:::::::::::::::::::::::::::::::::::::::::::::::: + For a more detailed explanation of how to work with our layout and theme, please refer to the [`nova-trame documentation`](https://nova-application-development.readthedocs.io/projects/nova-trame/en/stable/working_with_trame.html). ## Running the application diff --git a/episodes/07-Advanced-Visualizations.md b/episodes/07-Advanced-Visualizations.md index 58ff00369c9293d877a0cc38f456c888a166d26d..c9bb217b1973f05467485bfbe5a84e824994a57c 100755 --- a/episodes/07-Advanced-Visualizations.md +++ b/episodes/07-Advanced-Visualizations.md @@ -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) (Create):** +**2. `PlotlyConfig` Model Class (src/viz_examples/app/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. @@ -218,7 +218,7 @@ class PlotlyConfig(BaseModel): First, let's add replace the sample tabs from the template with the following: -**3. `src/viz_examples/views/tab_content_panel.py` (Modify):** +**3. `src/app/viz_examples/app/views/tab_content_panel.py` (Modify):** * **Import `PlotlyView`** diff --git a/episodes/08-Next-Steps.md b/episodes/08-Next-Steps.md index 420968fa9555329aa83c02eb48230a7a7c2e0dcb..7999ba8ce6022cf2c8cfec2a14952041a46b16b8 100755 --- a/episodes/08-Next-Steps.md +++ b/episodes/08-Next-Steps.md @@ -5,79 +5,46 @@ exercises: 0 --- # 8. Development Cycle and Next Steps -In this section, we will look at other resources you may want to integrate with your application and outline the process for taking an application like the one we\'ve created in this tutorial and deploying it to the NOVA/NDIP platform. While we won\'t actually perform the deployment in this tutorial, we will cover the key steps and resources involved. +In this section, we will look at other resources you may want to integrate with your application. ## ONCat Integration If needed, you can integrate your application with ONCat via [pyoncat](https://pypi.org/project/pyoncat/2.1/). If you need to access non-public information with the API then you will need to use an authenticated client in `pyoncat`. We strongly recommend you email oncat-support@ornl.gov explaining the use case for your ONCat integration as they can advise you on the most appropriate form of authentication for your application and how to set it up. -## Containerizing Your Application +## Advanced Container Configurations -The first step in deploying your application to the NOVA/NDIP platform is to package it as a Docker container. Docker containers provide a lightweight and portable way to package your application and all of its dependencies. This ensures that your application will run consistently across different environments. - -Fortunately, the template application we used in this tutorial already includes a `Dockerfile` that you can use as a starting point. The Dockerfile is a set of instructions that Docker uses to build your container image. - -Here\'s what the Dockerfile typically includes: - -* **Base Image:** Specifies the base operating system and environment for your application. -* **Dependencies:** Describes how to install any required libraries or packages. -* **Application Files:** Defines how to copy your application code into the container. -* **Entrypoint:** Sets the command that is run when the container starts. - -To containerize your application, you would: - -1. Navigate to the top level of your project (where the `dockerfiles` folder is). -2. Run the docker build command in the following format `docker build -t : -f dockerfiles/Dockerfile .` -3. Test your docker container using the command `docker run :`. -4. Push your docker container to a container registry. - -After the docker container is deployed to a registry, it can then be used by the platform. +The basic container configuration we created in Episode 2 works well for most applications, but there are some advanced configurations that may be useful for more complex applications. ::::::::::::::::::::::::::::::::::::::::: callout -GPU acceleration in a container is possible but beyond the scope of this tutorial. Typically, a base container is chosen which already has all of the gpu dependencies installed such as ```regproxy.ornl.gov/hub_proxy/kitware/trame:py3.10-glvnd-2024-12```. The team has built similar containers already which can be used as a reference for development, such as, ```https://code.ornl.gov/ndip/trame-apps/ct-scan-visualizer/``` +GPU acceleration in a container is possible but beyond the scope of this tutorial. Typically, a base container is chosen which already has all of the GPU dependencies installed such as ```regproxy.ornl.gov/hub_proxy/kitware/trame:py3.10-glvnd-2024-12```. The team has built similar containers already which can be used as a reference for development, such as ```https://code.ornl.gov/ndip/trame-apps/ct-scan-visualizer/``` :::::::::::::::::::::::::::::::::::::::::::::::::: -## Defining Your Tool with XML - -Once your application is containerized, you will also need to define your tool using an XML file. This XML file describes your tool to the NDIP platform, including its inputs, outputs, parameters, and the Docker container image that should be used to run the tool. The NDIP platform makes use of the Galaxy tool framework. -The XML file includes: +## Development Lifecycle -* **Tool ID:** A unique identifier for your tool. -* **Name and Description:** User-friendly name and description of the tool. -* **Inputs:** Defines the input parameters, including their types, labels, and optional constraints. -* **Outputs:** Describes the output files or datasets produced by the tool. -* **Container Image:** Specifies the Docker image that should be used to run the tool. -* **Command:** Specifies the command line that is executed inside the docker container. +As we saw in Episode 2, we can deploy our tools to the NDIP platform by adding XML files to the galaxy-tools repository's prototype branch. Let's discuss what happens after that initial deployment. -You can find numerous examples of Galaxy tool XML files in the NDIP GitLab repository: -[https://code.ornl.gov/ndip/galaxy-tools](https://code.ornl.gov/ndip/galaxy-tools) +### Continued Development -Detailed documentation on creating tool XML files is available on the Calvera documentation site: -[https://calvera.ornl.gov/docs/dev](https://calvera.ornl.gov/docs/dev) +Once your tool is deployed to the platform, you may want to continue development to fix bugs or add new features. The process for continuing development on an existing tool is similar to getting a new tool on the platform. -## Development Lifecycle +You will continue to develop on the *prototype* branch, where you can push and test changes. Once you are satisfied with the new version of your tool, submit a merge request to update the tool in the *dev* branch. The NDIP team will review these changes, perform the merge, and the new version of the tool will be updated on the NDIP production instance, Calvera, during the next deployment. -### How to get a new tool on NDIP +### Versioning Your Tools -After creating your tool\'s XML file, it needs to be added to the NDIP platform. For testing your tool on the platform, it should be added to the *prototype* branch of the GitLab repository linked above in a repository folder `tools/neutrons`, or it's subfolder. With a git commit, an automated CI job will push your tool to the calvera-test instance. Test your application via the web browser interface. After you\'ve verified that your tool is performing as expected, submit a merge request to the repository\'s *dev* branch and engage with our team. +As you continue to develop your tool, it's important to keep track of versions. The XML file we created in Episode 2 includes a version attribute that you should update whenever you make significant changes to your tool. The *dev* branch is used as a staging branch for tools that are ready to be put in front of users. Tools here will be added to the NDIP production instance, Calvera, during the next deployment. -### Continued Development - -The process for continuing development on an existing tool is similar to getting a new tool on the platform. You will continue to develop on the *prototype* branch, where you can push and test changes. Once you are satisified with the new version of your tool, submit a merge request to update the tool in the *dev* branch. Our team will review these changes, perform the merge, and the new version of the tool will be updated on the NDIP production instance, Calvera, during the next deployment. - -## Putting It All Together +## Future Tool Enhancements -Once you have your Docker container and tool XML file, you would: +As you become more familiar with NDIP and the Galaxy platform, you might want to explore more advanced features: -1. Upload the Docker image to a container registry. -2. Upload the XML file to the NDIP platform. -3. Make sure that the API_KEY and GALAXY_URL are passed to the application as environmental variables. -4. Test your application via the web browser interface. +- Creating complex workflows that combine multiple tools +- Integrating with high-performance computing resources +- Developing specialized data analysis pipelines -After performing these steps, your application will be available to NDIP users. +These topics are beyond the scope of this introductory tutorial, but the NDIP team is available to help you explore these possibilities as your tools mature. ## Additional Resources @@ -90,10 +57,11 @@ After performing these steps, your application will be available to NDIP users. * **nova-mvvm documentation**: https://nova-application-development.readthedocs.io/projects/mvvm-lib/en/latest/ * **Calvera documentation**: https://calvera-test.ornl.gov/docs/ -By following the steps outlined in this section, you can deploy your own applications to the NDIP platform and make them available to the wider scientific community. +By following this tutorial, you've learned how to create and deploy a NOVA application to the NDIP platform. You can now build on this foundation to create more complex scientific applications that can be easily shared with the wider scientific community. :::::::::::::::::::::::::::::::::::::::: keypoints - Tools must be containerized to run on NDIP. -- NDIP requires tools to have an XML file which defines input, outputs, tool id, and the container location. +- NDIP requires tools to have an XML file which defines inputs, outputs, tool ID, and the container location. - Tool XML files must be added to the Galaxy Tools Repository. +- The development lifecycle involves continuous testing on the prototype branch before promoting to dev. :::::::::::::::::::::::::::::::::::::::::::::::::: diff --git a/learners/setup.md b/learners/setup.md index c988ad64514d17686fbec2f4727ab8833dcf2d5c..edd17a9969cac7ceace385334d1c6c4b2b4a8095 100755 --- a/learners/setup.md +++ b/learners/setup.md @@ -10,17 +10,32 @@ This section guides you through setting up your development environment to follo Before proceeding, ensure you have met the following prerequisites: * **Basic Python Knowledge:** A basic understanding of Python programming concepts is required. -* **Python Installation:** You must have Python 3.8 or higher installed on your system. Verify your Python version by running `python --version` or `python3 --version` in your terminal. +* **Python Installation:** You must have Python 3.10 or higher installed on your system. Verify your Python version by running `python --version` or `python3 --version` in your terminal. * **Python's `copier` Library:** We will be using this library to generate a starting application from a template. Install it using `pip install copier`. * **Poetry:** The code samples provided in this tutorial leverage Poetry for dependency management. If you don't have Poetry installed, follow the instructions on the official Poetry website: [https://python-poetry.org/docs/#installation](https://python-poetry.org/docs/#installation). * **NDIP Access:** You should have access to a working NDIP system. (Specific details about NDIP access will need to be provided by the instructor.) Ensure you have the necessary credentials (API key and NDIP URL) to connect to NDIP. These will be set as environment variables, as described later. * **NOVA Libraries:** While Poetry will manage these dependencies for you, it's helpful to be aware of the core NOVA libraries: `nova-galaxy`, `nova-trame`, and `nova-mvvm`. You can find documentation for these libraries on ReadTheDocs (links provided in the "References" section). * **A Text Editor or IDE:** You will need a text editor or IDE (such as VS Code, Sublime Text, or Atom) for writing code. +* **Git:** You must have git installed on your system. * **Familiarity with the Command Line:** You will need to be comfortable using the command line or terminal. +::::::::::::::::::::::::::::::::::::::::: callout + +You can use the analysis cluster for this tutorial. This is recommended if you use Windows or otherwise can\'t meet the above prerequisites on your laptop. By default, the `python` command on the cluster will use 3.9, so please explicitly reference `python3.11` where needed. + +You can create a virtual environment suitable for the tutorial on the cluster with: + +```bash +python3.11 -m venv .venv +source .venv/bin/activate +pip install copier poetry +``` + +:::::::::::::::::::::::::::::::::::::::::::::::: + ## 2. Getting your Galaxy API Key -In order to run the code examples in this tutorial, an API Key is required. An API key is obtained from the NDIP instance directly. +In order to run the code examples in this tutorial, an API Key is required. An API key is obtained from the NDIP instance directly. ::::::::::::::::::::::::::::::::::::::::: callout @@ -59,7 +74,25 @@ The NOVA framework requires you to set environment variables for your NDIP URL a **Important:** For security reasons, it is recommended to avoid hardcoding your API key directly in your code. Using environment variables is a more secure and flexible approach. -## 4. Verify Your Setup +## 4. Create a GitLab Personal Access Token + +::::::::::::::::::::::::::::::::::::::::: callout + +If you currently use an ssh key for gitlab, you will still need to create a personal access token for the tutorial. + +:::::::::::::::::::::::::::::::::::::::::::::::::: +To interact with repositories on code.ornl.gov, you'll need to create a personal access token: + +1. Navigate to [https://code.ornl.gov](https://code.ornl.gov) and log in with your credentials +2. In the left sidebar, select your avatar. +3. Select Edit Profile +4. On the left sidebar, select Access tokens +5. In Token name, enter a name for the token (such as Nova Tutorial) +6. Provide the desired scopes (at minimum, select "read repository", "write repository", and api) +7. Click Create personal access token. +8. **Important** Copy and save your token to your computer immediately. You will no longer have access to it after leaving the page. + +## 5. Verify Your Setup To ensure your setup is correct, run the following command in your terminal within the `nova_tutorial` directory: