From dd46f0353ae74e1675ff973ac815b369d02e1c07 Mon Sep 17 00:00:00 2001 From: jamin9902 <48889500+jamin9902@users.noreply.github.com> Date: Sat, 16 Aug 2025 12:11:34 -0700 Subject: [PATCH 1/2] Documentation update and initial simulation manager --- docs/core-modules/client.md | 8 ++-- docs/core-modules/graph.md | 12 +++--- docs/core-modules/index.md | 43 +++++++++++++++++++ docs/core-modules/node.md | 7 +++ docs/core-modules/projects.md | 6 +-- docs/core-modules/run.md | 10 ++--- fluidize/core/modules/run/node/node_runner.py | 14 ++++++ .../core/types/file_models/file_model_base.py | 4 +- fluidize/managers/node.py | 14 +++--- fluidize/managers/simulations.py | 29 +++++++++++++ mkdocs.yml | 2 + 11 files changed, 117 insertions(+), 32 deletions(-) create mode 100644 docs/core-modules/index.md create mode 100644 docs/core-modules/node.md create mode 100644 fluidize/managers/simulations.py diff --git a/docs/core-modules/client.md b/docs/core-modules/client.md index 2c9bf16..b4411ae 100644 --- a/docs/core-modules/client.md +++ b/docs/core-modules/client.md @@ -1,4 +1,4 @@ -# Fluidize Client +# Client Module The Fluidize Client is the primary interface to create and edit projects. There are two interfaces for this, with more on the way. @@ -10,8 +10,7 @@ The Fluidize Client is the primary interface to create and edit projects. There options: show_source: false heading_level: 3 - extra: - show_root_heading: true + show_root_heading: true members: - mode - adapters @@ -22,8 +21,7 @@ The Fluidize Client is the primary interface to create and edit projects. There options: show_source: false heading_level: 3 - extra: - show_root_heading: true + show_root_heading: true members: - is_local_mode - is_api_mode diff --git a/docs/core-modules/graph.md b/docs/core-modules/graph.md index a7d26d0..0919c44 100644 --- a/docs/core-modules/graph.md +++ b/docs/core-modules/graph.md @@ -5,8 +5,7 @@ options: show_source: false heading_level: 3 - extra: - show_root_heading: true + show_root_heading: true members: - get - add_node @@ -20,30 +19,29 @@ options: show_source: false heading_level: 3 - extra: - show_root_heading: true + show_root_heading: true ## Graph Types ::: fluidize.core.types.graph.GraphData options: heading_level: 3 + show_root_heading: true extra: show_attributes: true - show_root_heading: true ::: fluidize.core.types.graph.GraphNode options: heading_level: 3 + show_root_heading: true extra: show_attributes: true - show_root_heading: true ::: fluidize.core.types.graph.GraphEdge options: heading_level: 3 + show_root_heading: true extra: show_attributes: true - show_root_heading: true diff --git a/docs/core-modules/index.md b/docs/core-modules/index.md new file mode 100644 index 0000000..f75ba97 --- /dev/null +++ b/docs/core-modules/index.md @@ -0,0 +1,43 @@ +# Core Modules + +The Fluidize library is composed of a set of core modules that provide a high-level interface for managing Fluidize resources. These modules are designed to be used together to build and execute scientific computing pipelines. + +## [Client](client.md) + +The **Fluidize Client** provides a unified, high-level interface for managing Fluidize resources in both local and cloud API modes. It serves as the primary entry point for creating and running pipelines across these environments. + +## [Projects](projects.md) + +The **Projects** module provides tools for managing project lifecycles: + +- [**Registry Manager**](projects.md#fluidize.managers.registry.RegistryManager): + Handles the user’s complete project registry, with functionality to create, edit, and delete projects. + +- [**Project Manager**](projects.md#fluidize.managers.project.ProjectManager): + Focuses on individual projects, managing the project graph, nodes, and runs, and supporting execution of project-specific workflows. + +## [Graph](graph.md) + +The **Graph** module provides tools for managing the project graph, which is a representation of the simulation pipeline. + +In a Fluidize project, pipelines are represented as a directed acyclic graph (DAG) where each node represents a module simulation and each edge represents the flow of data between nodes: + +- [**Graph Manager**](graph.md#fluidize.managers.graph.GraphManager): + Manages the project graph, and provides high level functionality to create, edit, and delete nodes and edges. + +- [**Graph Processor**](graph.md#fluidize.managers.graph.graph_processor.GraphProcessor): + Manages specific operations on the graph data structure within the local filesystem. + +## [Node](node.md) + +The **Node** module provides tools for managing the metadata, properties, and parameters of individual nodes within a project. + +## [Run](run.md) + +The **Run** module provides tools for managing simulation pipeline runs within a project: + +- [**Runs Manager**](run.md#fluidize.managers.run.RunsManager): + Manages the high level execution of runs and retrieving run status. + +- [**Project Runner**](run.md#fluidize.core.modules.run.project.ProjectRunner): + Manages the specific execution details of a project pipeline, including environment preparation and node execution order. diff --git a/docs/core-modules/node.md b/docs/core-modules/node.md new file mode 100644 index 0000000..88d11a1 --- /dev/null +++ b/docs/core-modules/node.md @@ -0,0 +1,7 @@ +# Node Module + +::: fluidize.managers.node.NodeManager + options: + show_source: false + heading_level: 3 + show_root_heading: true diff --git a/docs/core-modules/projects.md b/docs/core-modules/projects.md index e51384c..6e8c8ff 100644 --- a/docs/core-modules/projects.md +++ b/docs/core-modules/projects.md @@ -5,13 +5,11 @@ options: show_source: false heading_level: 3 - extra: - show_root_heading: true + show_root_heading: true ## Project ::: fluidize.managers.project.ProjectManager options: show_source: false heading_level: 3 - extra: - show_root_heading: true + show_root_heading: true diff --git a/docs/core-modules/run.md b/docs/core-modules/run.md index c21122f..dc6be19 100644 --- a/docs/core-modules/run.md +++ b/docs/core-modules/run.md @@ -6,8 +6,7 @@ options: show_source: false heading_level: 3 - extra: - show_root_heading: true + show_root_heading: true members: - run_flow - list @@ -19,12 +18,11 @@ options: show_source: false heading_level: 3 - extra: - show_root_heading: true + show_signature: false + show_root_heading: true ::: fluidize.core.modules.run.project.ProjectRunner options: show_source: false heading_level: 3 - extra: - show_root_heading: true + show_root_heading: true diff --git a/fluidize/core/modules/run/node/node_runner.py b/fluidize/core/modules/run/node/node_runner.py index a35668e..2684371 100644 --- a/fluidize/core/modules/run/node/node_runner.py +++ b/fluidize/core/modules/run/node/node_runner.py @@ -8,6 +8,10 @@ # RunJob now uses a strategy instance to dynamically choose behavior. class RunJob: + """ + A job that runs for a single node. + """ + def __init__( self, project: ProjectSummary, @@ -18,6 +22,16 @@ def __init__( run_id: Optional[str] = None, run_metadata: Optional[object] = None, # Add run metadata ): + """ + Args: + project: The project this node belongs to + strategyClass: The strategy class to use for execution + nodeProperties_simulation: The node properties to run + prev_nodeProperties_simulation: The previous node properties (optional) + mlflow_tracker: The MLflow tracker (optional) + run_id: The run ID (optional) + run_metadata: The run metadata (optional) + """ self.project = project self.nodeProperties_simulation = nodeProperties_simulation self.prev_nodeProperties_simulation = prev_nodeProperties_simulation diff --git a/fluidize/core/types/file_models/file_model_base.py b/fluidize/core/types/file_models/file_model_base.py index 3dc25f1..f037ad7 100644 --- a/fluidize/core/types/file_models/file_model_base.py +++ b/fluidize/core/types/file_models/file_model_base.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, TypeVar, Union +from typing import Any, Optional, TypeVar, Union from pydantic import BaseModel, ConfigDict, PrivateAttr, ValidationError from upath import UPath @@ -49,7 +49,7 @@ def from_file(cls: type[T], directory: Union[str, UPath]) -> T: return instance @classmethod - def from_dict_and_path(cls: type[T], data: dict, path: UPath) -> T: + def from_dict_and_path(cls: type[T], data: Any, path: Optional[UPath]) -> T: """Creates a model instance from a dictionary and a path, without reading the file again.""" if not data: raise ValueError() diff --git a/fluidize/managers/node.py b/fluidize/managers/node.py index cef596e..41619d5 100644 --- a/fluidize/managers/node.py +++ b/fluidize/managers/node.py @@ -24,8 +24,6 @@ class NodeManager: def __init__(self, adapter: Any, project: ProjectSummary, node_id: str) -> None: """ - Initialize node-scoped manager. - Args: adapter: adapter adapter (FluidizeSDK or Localadapter) project: The project this node belongs to @@ -268,13 +266,13 @@ def update_parameter(self, parameter: Parameter) -> Parameter: def set_parameters(self, parameters: list[Parameter]) -> list[Parameter]: """ - Replace all parameters with the provided list. + Replace all parameters with the provided list. + + Args: + parameters: List of parameters to set - Args: - parameters: List of parameters to set - W - Returns: - The list of parameters that were set + Returns: + The list of parameters that were set """ parameters_model = self.get_parameters_model() parameters_model.parameters = parameters diff --git a/fluidize/managers/simulations.py b/fluidize/managers/simulations.py new file mode 100644 index 0000000..3c05141 --- /dev/null +++ b/fluidize/managers/simulations.py @@ -0,0 +1,29 @@ +from typing import Any + +from fluidize_sdk import FluidizeSDK + +from fluidize.core.types.node import nodeMetadata_simulation + + +class SimulationsManager: + """ + Simulations manager that provides access to the Fluidize simulation library. + """ + + def __init__(self, adapter: Any) -> None: + """ + Args: + adapter: adapter (FluidizeSDK or LocalAdapter) + """ + self._adapter = adapter + self.fluidize_sdk = FluidizeSDK() + + def list_simulations(self) -> list[Any]: + """ + List all simulations available in the Fluidize simulation library. + + Returns: + List of simulation metadata + """ + simulations = self.fluidize_sdk.graph.list_simulations(sim_global=True) + return [nodeMetadata_simulation.from_dict_and_path(data=simulation, path=None) for simulation in simulations] diff --git a/mkdocs.yml b/mkdocs.yml index ba159d6..b561f73 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -13,9 +13,11 @@ nav: - Quickstart: getting-started/quickstart.md - Examples: getting-started/examples.md - Core Modules: + - core-modules/index.md - Client: core-modules/client.md - Projects: core-modules/projects.md - Graph: core-modules/graph.md + - Node: core-modules/node.md - Run: core-modules/run.md plugins: - search From a5146358e62f20a000c897f3c6d34473272e2497 Mon Sep 17 00:00:00 2001 From: jamin9902 <48889500+jamin9902@users.noreply.github.com> Date: Sat, 16 Aug 2025 16:50:51 -0700 Subject: [PATCH 2/2] Working simulation manager and testing --- fluidize/managers/simulations.py | 10 ++- pyproject.toml | 2 +- tests/integration/test_simulations_manager.py | 40 +++++++++ tests/unit/managers/test_simulation.py | 87 +++++++++++++++++++ uv.lock | 8 +- 5 files changed, 139 insertions(+), 8 deletions(-) create mode 100644 tests/integration/test_simulations_manager.py create mode 100644 tests/unit/managers/test_simulation.py diff --git a/fluidize/managers/simulations.py b/fluidize/managers/simulations.py index 3c05141..e6f3ed5 100644 --- a/fluidize/managers/simulations.py +++ b/fluidize/managers/simulations.py @@ -16,7 +16,8 @@ def __init__(self, adapter: Any) -> None: adapter: adapter (FluidizeSDK or LocalAdapter) """ self._adapter = adapter - self.fluidize_sdk = FluidizeSDK() + # TODO: Fix hardcoding of api_token and remove type ignore + self.fluidize_sdk = FluidizeSDK(api_token="placeholder") # noqa: S106 def list_simulations(self) -> list[Any]: """ @@ -25,5 +26,8 @@ def list_simulations(self) -> list[Any]: Returns: List of simulation metadata """ - simulations = self.fluidize_sdk.graph.list_simulations(sim_global=True) - return [nodeMetadata_simulation.from_dict_and_path(data=simulation, path=None) for simulation in simulations] + simulations = self.fluidize_sdk.simulation.list_simulations(sim_global=True) + return [ + nodeMetadata_simulation.from_dict_and_path(data=simulation.model_dump(), path=None) + for simulation in simulations + ] diff --git a/pyproject.toml b/pyproject.toml index f6b43ce..f0844da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ classifiers = [ dependencies = [ "asciitree>=0.3.3", "docker>=7.1.0", - "fluidize-sdk>=0.4.0", + "fluidize-sdk>=0.6.0", "jinja2>=3.1.6", "mlflow>=3.1.4", "networkx>=3.2.1", diff --git a/tests/integration/test_simulations_manager.py b/tests/integration/test_simulations_manager.py new file mode 100644 index 0000000..43ef738 --- /dev/null +++ b/tests/integration/test_simulations_manager.py @@ -0,0 +1,40 @@ +"""Integration tests for SimulationsManager - tests real API connectivity.""" + +import pytest + +from fluidize.managers.simulations import SimulationsManager + + +class TestSimulationsManagerIntegration: + """Integration test suite for SimulationsManager class.""" + + @pytest.fixture + def mock_adapter(self): + """Create a mock adapter for testing.""" + from unittest.mock import Mock + + adapter = Mock() + return adapter + + def test_list_simulations_integration(self, mock_adapter): + """Integration test that actually calls the API and prints output.""" + + # Create manager without mocking SDK + manager = SimulationsManager(mock_adapter) + + # Act - make real API call + result = manager.list_simulations() + + # Assert basic functionality + assert isinstance(result, list) + + # Print results for manual verification + print("\n=== Integration Test Results ===") + print(f"Number of simulations found: {len(result)}") + for sim in result: + print("Simulation details:") + print(f" Name: {sim.name}") + print(f" ID: {sim.id}") + print(f" Description: {sim.description}") + print(f" Version: {sim.version}") + print("\n") diff --git a/tests/unit/managers/test_simulation.py b/tests/unit/managers/test_simulation.py new file mode 100644 index 0000000..7137e98 --- /dev/null +++ b/tests/unit/managers/test_simulation.py @@ -0,0 +1,87 @@ +"""Unit tests for Simulations Manager - high-level simulation library interface.""" + +from unittest.mock import Mock, patch + +import pytest + +from fluidize.managers.simulations import SimulationsManager + + +class TestSimulationsManager: + """Test suite for SimulationsManager class.""" + + @pytest.fixture + def mock_adapter(self): + """Create a mock adapter for testing.""" + adapter = Mock() + return adapter + + @pytest.fixture + def simulations_manager(self, mock_adapter): + """Create a SimulationsManager instance for testing.""" + with patch("fluidize.managers.simulations.FluidizeSDK"): + return SimulationsManager(mock_adapter) + + @patch("fluidize.managers.simulations.FluidizeSDK") + def test_init(self, mock_sdk_class, mock_adapter): + """Test SimulationsManager initialization.""" + manager = SimulationsManager(mock_adapter) + + assert manager._adapter is mock_adapter + assert manager.fluidize_sdk is not None + mock_sdk_class.assert_called_once() + + @patch("fluidize.managers.simulations.FluidizeSDK") + def test_list_simulations_returns_list(self, mock_sdk_class, simulations_manager): + """Test that list_simulations returns a list.""" + # Arrange + mock_sdk_instance = mock_sdk_class.return_value + simulations_manager.fluidize_sdk = mock_sdk_instance + # Create a mock simulation object with model_dump method + mock_simulation = Mock() + mock_simulation.model_dump.return_value = { + "name": "Test Simulation", + "id": "sim_001", + "description": "A test simulation", + "date": "2024-01-01", + "version": "1.0.0", + "authors": [], + "tags": [], + } + mock_sdk_instance.simulation.list_simulations.return_value = [mock_simulation] + + # Act + result = simulations_manager.list_simulations() + + # Assert + assert isinstance(result, list) + mock_sdk_instance.simulation.list_simulations.assert_called_once_with(sim_global=True) + + @patch("fluidize.managers.simulations.FluidizeSDK") + def test_list_simulations_empty_list(self, mock_sdk_class, simulations_manager): + """Test that list_simulations handles empty results.""" + # Arrange + mock_sdk_instance = mock_sdk_class.return_value + simulations_manager.fluidize_sdk = mock_sdk_instance + mock_sdk_instance.simulation.list_simulations.return_value = [] + + # Act + result = simulations_manager.list_simulations() + + # Assert + assert result == [] + mock_sdk_instance.simulation.list_simulations.assert_called_once_with(sim_global=True) + + @patch("fluidize.managers.simulations.FluidizeSDK") + def test_list_simulations_sdk_delegation(self, mock_sdk_class, simulations_manager): + """Test that list_simulations properly delegates to SDK.""" + # Arrange + mock_sdk_instance = mock_sdk_class.return_value + simulations_manager.fluidize_sdk = mock_sdk_instance + mock_sdk_instance.simulation.list_simulations.return_value = [] + + # Act + simulations_manager.list_simulations() + + # Assert + mock_sdk_instance.simulation.list_simulations.assert_called_once_with(sim_global=True) diff --git a/uv.lock b/uv.lock index b5e98e6..e1f8dcf 100644 --- a/uv.lock +++ b/uv.lock @@ -759,7 +759,7 @@ dev = [ requires-dist = [ { name = "asciitree", specifier = ">=0.3.3" }, { name = "docker", specifier = ">=7.1.0" }, - { name = "fluidize-sdk", specifier = ">=0.4.0" }, + { name = "fluidize-sdk", specifier = ">=0.6.0" }, { name = "jinja2", specifier = ">=3.1.6" }, { name = "mlflow", specifier = ">=3.1.4" }, { name = "networkx", specifier = ">=3.2.1" }, @@ -786,7 +786,7 @@ dev = [ [[package]] name = "fluidize-sdk" -version = "0.4.0" +version = "0.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -796,9 +796,9 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/ef/167f581b140afec073ee4961b18fe347c090477b3263a5276656539cf9af/fluidize_sdk-0.4.0.tar.gz", hash = "sha256:0b80ff0a561fadf01cf7da6a1c0b425bfec287af9d31a8596d6e3cd15e476381", size = 118050, upload-time = "2025-08-10T10:16:28.617Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/6d/41a511df1e9f2cfb6526a349162ea7032ef2ef3d4ced52b8ff81a036ca84/fluidize_sdk-0.6.0.tar.gz", hash = "sha256:cb4548e3ebac5c949b177a6f352edc91be5ffd82900dd0267159027ffb895633", size = 119144, upload-time = "2025-08-16T09:45:34.48Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/f4/8d1b1f630058a951bd5702f29069ea6f1bf1396d3649663a3752f7f4b699/fluidize_sdk-0.4.0-py3-none-any.whl", hash = "sha256:501e7093f998cecc171e95e57d4f4e9d207820859f13bdb8a0a45e1c72d9f21b", size = 120117, upload-time = "2025-08-10T10:16:27.245Z" }, + { url = "https://files.pythonhosted.org/packages/e9/6e/25a81bd993b6dec0733e7f4f13f199a40e3500222907bfab54fc5e946eea/fluidize_sdk-0.6.0-py3-none-any.whl", hash = "sha256:22db9101b88bbf0adea113057f96ce9436985ce3b9e635fedcdb421fff399ab2", size = 121524, upload-time = "2025-08-16T09:45:32.874Z" }, ] [[package]]