Add All Folders
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
from core.contracts import RequirementContract, DataContract
|
||||
|
||||
|
||||
def check_compatibility(
|
||||
provider: DataContract,
|
||||
requirement: RequirementContract
|
||||
):
|
||||
# Central compatibility entry point
|
||||
try:
|
||||
requirement.is_satisfied_by(provider)
|
||||
return True, "Compatible"
|
||||
except ValueError as e:
|
||||
return False, str(e)
|
||||
@@ -0,0 +1,14 @@
|
||||
from enum import Enum
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class CompatibilityLevel(str, Enum):
|
||||
OK = "OK"
|
||||
WARNING = "WARNING"
|
||||
ERROR = "ERROR"
|
||||
|
||||
|
||||
class CompatibilityResult(BaseModel):
|
||||
level: CompatibilityLevel
|
||||
message: str
|
||||
suggestion: str | None = None
|
||||
@@ -0,0 +1,36 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Literal, Set
|
||||
|
||||
|
||||
class DataContract(BaseModel):
|
||||
# Describes what a node produces
|
||||
data_type: str
|
||||
provides: Set[str] = set()
|
||||
|
||||
|
||||
class ImageDataContract(DataContract):
|
||||
data_type: Literal["image"] = "image"
|
||||
modality: Literal["CT", "MR"]
|
||||
dim: Literal["2D", "3D"]
|
||||
has_mask: bool = False
|
||||
provides: Set[str] = {"image"}
|
||||
|
||||
|
||||
class MaskDataContract(DataContract):
|
||||
data_type: Literal["mask"] = "mask"
|
||||
modality: Literal["CT", "MR"]
|
||||
dim: Literal["2D", "3D"]
|
||||
provides: Set[str] = {"mask"}
|
||||
|
||||
|
||||
class FeatureTableContract(DataContract):
|
||||
data_type: Literal["table"] = "table"
|
||||
provides: Set[str] = {"features"}
|
||||
|
||||
|
||||
class RequirementContract(BaseModel):
|
||||
# Describes what a node requires
|
||||
data_type: str
|
||||
|
||||
def is_satisfied_by(self, provided: DataContract):
|
||||
raise NotImplementedError
|
||||
@@ -0,0 +1,21 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Literal, Set, Optional
|
||||
|
||||
|
||||
class ImageDataContract(BaseModel):
|
||||
data_type: Literal["image"] = "image"
|
||||
modality: Literal["CT", "PET", "MR"]
|
||||
dim: Literal["2D", "3D", "2_5D"]
|
||||
geometry_id: Optional[str] = None
|
||||
has_mask: bool = False
|
||||
|
||||
|
||||
class MaskDataContract(BaseModel):
|
||||
data_type: Literal["mask"] = "mask"
|
||||
modality: Literal["CT", "PET", "MR"]
|
||||
dim: Literal["2D", "3D", "2_5D"]
|
||||
geometry_id: Optional[str] = None
|
||||
|
||||
|
||||
class RadiomicsFeatureTableContract(BaseModel):
|
||||
data_type: Literal["table"] = "table"
|
||||
@@ -0,0 +1,15 @@
|
||||
from typing import Dict
|
||||
from core.contracts import DataContract, RequirementContract
|
||||
|
||||
|
||||
class Node:
|
||||
# Base class for all processing nodes
|
||||
|
||||
name: str
|
||||
inputs: Dict[str, RequirementContract]
|
||||
outputs: Dict[str, DataContract]
|
||||
|
||||
def __init__(self, name, inputs=None, outputs=None):
|
||||
self.name = name
|
||||
self.inputs = inputs or {}
|
||||
self.outputs = outputs or {}
|
||||
@@ -0,0 +1,12 @@
|
||||
class Workflow:
|
||||
def __init__(self):
|
||||
self.connections = []
|
||||
|
||||
def connect(self, out_node, out_port, in_node, in_port):
|
||||
self.connections.append((out_node, out_port, in_node, in_port))
|
||||
|
||||
def get_connection(self, in_node, in_port):
|
||||
for o_node, o_port, i_node, i_port in self.connections:
|
||||
if i_node == in_node and i_port == in_port:
|
||||
return o_node, o_port
|
||||
raise RuntimeError("Missing connection")
|
||||
@@ -0,0 +1,34 @@
|
||||
class WorkflowExecutor:
|
||||
def __init__(self, graph):
|
||||
self.graph = graph
|
||||
self.cache = {}
|
||||
|
||||
def execute_from_writer(self, writer_node):
|
||||
# Execute dependencies first
|
||||
inputs = {}
|
||||
|
||||
for port_name, requirement in writer_node.inputs.items():
|
||||
src_node, src_port = self.graph.get_connection(writer_node, port_name)
|
||||
inputs[port_name] = self._execute_node(src_node)
|
||||
|
||||
writer_node.run(**inputs)
|
||||
|
||||
def _execute_node(self, node):
|
||||
if node in self.cache:
|
||||
return self.cache[node]
|
||||
|
||||
# Reader
|
||||
if hasattr(node, "load_data"):
|
||||
result = node.load_data()
|
||||
self.cache[node] = result
|
||||
return result
|
||||
|
||||
# Processor
|
||||
inputs = {}
|
||||
for port_name in node.inputs:
|
||||
src_node, _ = self.graph.get_connection(node, port_name)
|
||||
inputs[port_name] = self._execute_node(src_node)
|
||||
|
||||
result = node.run(**inputs)
|
||||
self.cache[node] = result
|
||||
return result
|
||||
Reference in New Issue
Block a user