Add All Folders

This commit is contained in:
soorena62
2026-02-08 04:38:10 +03:30
commit 9bb57e4799
191 changed files with 8469 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
Description:
# In this project, using Pytest, the API layer codes of the Radioma Desktop Ver 2.0 project were tested with 20 tests.
Dependencies:
# pip install pytest
Running:
# pytest test rnd/run_rnd_workflow
+14
View File
@@ -0,0 +1,14 @@
class Asset:
"""
Represents the output of a Task.
"""
def __init__(self, data, type_name: str):
self.data = data
self.type_name = type_name
self.preserved = True
def dismiss(self):
self.preserved = False
def __repr__(self):
return f"<Asset type={self.type_name}>"
@@ -0,0 +1,53 @@
from typing import Dict, Any
from radiuma_api.asset import Asset
from radiuma_api.io_port import InPort
from radiuma_api.io_port import OutPort
# Write Codes Here:
class ExecutionContext:
"""
Holds runtime execution data for a Workflow run.
"""
def __init__(self, execution_id: str | None = None):
self.execution_id = execution_id
self._data: Dict[OutPort, Asset] = {}
self._metadata: Dict[str, Any] = {}
# Output Data Recording
def put(self, output_port: OutPort, asset: Asset):
"""Register an asset produced by an OutputPort."""
self._data[output_port] = asset
# Getting Data For InputPort
def get(self, input_port: InPort):
"""Retrieve the asset connected to this InputPort."""
if input_port.connected_output is None:
return None
return self._data.get(input_port.connected_output)
# Check Data Availability
def has_data(self, input_port: InPort):
return self.get(input_port) is not None
# Metadata management
def set_meta(self, key: str, value: Any):
self._metadata[key] = value
def get_meta(self, key: str, default=None):
return self._metadata.get(key, default)
# Status View
def status_view(self):
"""Return a lightweight snapshot of current execution state."""
return {
"execution_id": self.execution_id,
"assets": {
f"{port.parent_task.name}.{port.name}": {
"contract": repr(port.contract),
"asset_type": asset.type_name
}
for port, asset in self._data.items()
},
"metadata": self._metadata.copy(),
}
+200
View File
@@ -0,0 +1,200 @@
from abc import ABC, abstractmethod
from typing import Optional, Dict, Any, List
# Codes Go Below:
# Errors:
class CompatibilityException(Exception):
def __init__(self, reason: str):
super().__init__(reason)
self.reason = reason
# DType (Semantic Contract):
class DType(ABC):
def __init__(self, metadata: Optional[Dict[str, Any]] = None):
self.metadata = metadata or {}
@abstractmethod
def can_connect_to(self, other: "DType", conditions: Optional[Dict[str, Any]] = None):
pass
def __repr__(self):
return f"{self.__class__.__name__}(metadata={self.metadata})"
# COMPOSITE TYPE:
class CompositeType(DType):
"""
Represents a semantic bundle of multiple named DTypes.
Example:
CompositeType({
"sheet1": TableType(...),
"sheet2": TextType(...),
"image": ImageType(...)
})
"""
def __init__(self, parts: Dict[str, DType], metadata=None):
super().__init__(metadata)
self.parts = parts # Dict[str, DType]
def can_connect_to(self, other: "DType", conditions=None):
if not isinstance(other, CompositeType):
raise CompatibilityException("Expected CompositeType")
if self.parts.keys() != other.parts.keys():
raise CompatibilityException("CompositeType Keys Mismatch")
for key in self.parts:
self.parts[key].can_connect_to(other.parts[key], conditions)
def __repr__(self):
return f"CompositeType({self.parts})"
# Image Types:
class ImageType(DType):
def __init__(self, modality: Optional[str] = None, metadata=None):
super().__init__(metadata)
self.modality = modality
def can_connect_to(self, other: DType, conditions=None):
if not isinstance(other, ImageType):
raise CompatibilityException("Target is not an ImageType")
if self.modality and other.modality and self.modality != other.modality:
raise CompatibilityException(
f"Image modality mismatch: {self.modality} vs {other.modality}"
)
class NIFTIImageType(ImageType):
def __init__(self, metadata=None):
super().__init__(modality="nifti", metadata=metadata)
def can_connect_to(self, other: DType, conditions=None):
super().can_connect_to(other, conditions)
# NIFTI-specific rules here
class DICOMImageType(ImageType):
def __init__(self, metadata=None):
super().__init__(modality="dicom", metadata=metadata)
def can_connect_to(self, other: DType, conditions=None):
super().can_connect_to(other, conditions)
# DICOM-specific rules here
# Table Types:
class TableType(DType):
def __init__(self, columns: List[str], metadata=None):
super().__init__(metadata)
self.columns = columns
def can_connect_to(self, other: DType, conditions=None):
if not isinstance(other, TableType):
raise CompatibilityException("Target is not a TableType")
if self.columns != other.columns:
raise CompatibilityException(
f"Table schema mismatch: {self.columns} vs {other.columns}"
)
class CSVTableType(TableType):
def __init__(
self,
columns: List[str],
delimiter: str = ",",
encoding: str = "utf-8",
metadata=None,
):
super().__init__(columns, metadata)
self.delimiter = delimiter
self.encoding = encoding
def can_connect_to(self, other: DType, conditions=None):
if not isinstance(other, CSVTableType):
raise CompatibilityException("CSV tables can only connect to CSV tables")
super().can_connect_to(other, conditions)
if self.delimiter != other.delimiter:
raise CompatibilityException(
f"CSV delimiter mismatch: {self.delimiter} vs {other.delimiter}"
)
# Text Type:
class TextType(DType):
def __init__(
self,
language: Optional[str] = None,
encoding: Optional[str] = None,
metadata=None,
):
super().__init__(metadata)
self.language = language
self.encoding = encoding
def can_connect_to(self, other: DType, conditions=None):
if not isinstance(other, TextType):
raise CompatibilityException("Target is not TextType")
if self.language and other.language and self.language != other.language:
raise CompatibilityException(
f"Language mismatch: {self.language} vs {other.language}"
)
if self.encoding and other.encoding and self.encoding != other.encoding:
raise CompatibilityException(
f"Encoding mismatch: {self.encoding} vs {other.encoding}"
)
# Ports:
class Port(ABC):
def __init__(self, name: str, dtype: DType):
self.name = name
self.dtype = dtype
self.parent_task = None
def __repr__(self):
return f"{self.__class__.__name__}({self.name}, {self.dtype})"
class OutPort(Port):
def __init__(self, name: str, dtype: DType):
super().__init__(name, dtype)
self._connections: List["InPort"] = []
@property
def connections(self):
return list(self._connections)
def connect(self, in_port: "InPort", conditions: Optional[Dict[str, Any]] = None):
if len(self._connections):
raise CompatibilityException("OutPort max connections reached")
in_port.can_connect_to(self, conditions)
self._connections.append(in_port)
in_port._connected_output = self
class InPort(Port):
def __init__(self, name: str, dtype: DType, required: bool = True):
super().__init__(name, dtype)
self.required = required
self._connected_output: Optional[OutPort] = None
@property
def connected_output(self):
return self._connected_output
def is_ready(self):
return self._connected_output is not None
def can_connect_to(self, out_port: OutPort, conditions: Optional[Dict[str, Any]] = None):
self.dtype.can_connect_to(out_port.dtype, conditions)
+34
View File
@@ -0,0 +1,34 @@
from radiuma_api.task import Task, Status, TaskEvent
from radiuma_api.asset import Asset
from radiuma_api.execution_context import ExecutionContext
from radiuma_api.io_port import CompatibilityException
# Write Codes Here:
class Module(Task):
"""Leaf node: atomic executable unit."""
def run(self, context: ExecutionContext):
if not self.check_inputs_ready(context):
self._set_status(Status.PENDING)
return
self._set_status(Status.RUNNING)
try:
consumed_data = [
port.connected_output.produced_asset.data
for port in self.inputs
if port.is_ready()
]
for output_port in self.outputs:
asset = Asset(
data=f"{self.name} processed {consumed_data}",
type_name=output_port.type_spec
)
context.put(output_port, asset)
self._set_status(Status.COMPLETED)
except CompatibilityException as e:
self._set_status(Status.FAILED)
self._emit(TaskEvent.ON_ERROR, {"error": e.reason})
@@ -0,0 +1,85 @@
from typing import Set
from radiuma_api.task import Task, Status
from radiuma_api.execution_context import ExecutionContext
# Write Codes Here:
class Scheduler:
"""
DAG-based execution scheduler.
Decides WHEN a Task can run based on port readiness and orchestrates execution.
"""
def __init__(self):
self._completed: Set[Task] = set()
self._failed: Set[Task] = set()
def run(self, root: Task, context: ExecutionContext):
"""
Entry point: execute a Workflow or Task graph.
"""
self._execute_task(root, context)
def _execute_task(self, task: Task, context: ExecutionContext):
"""
Execute a task respecting DAG dependencies.
If 'task' is a Workflow (has children), delegate to _run_workflow.
Otherwise, run the leaf task when inputs are ready.
"""
# Skip finished tasks
if task.status in (Status.COMPLETED, Status.FAILED, Status.STOPPED):
return
# Workflow (composite)
if hasattr(task, "children"):
self._run_workflow(task, context)
return
# Leaf Task: must have inputs ready
if not task.check_inputs_ready(context):
# Optional: diagnostic log
print(f"[Scheduler] Task '{task.name}' not ready, skipping for now.")
return
try:
task._set_status(Status.RUNNING)
task.run(context)
task._set_status(Status.COMPLETED)
self._completed.add(task)
except Exception as e:
task._set_status(Status.FAILED)
self._failed.add(task)
# Optional: diagnostic log
print(f"[Scheduler] Task '{task.name}' failed: {e}")
# Re-raise to surface error if caller needs to handle
raise
def _run_workflow(self, workflow: Task, context: ExecutionContext):
"""
Execute children respecting dependencies.
Progress until all children are executed or a deadlock is detected.
"""
workflow._set_status(Status.RUNNING)
remaining = set(workflow.children)
while remaining:
progress_made = False
for child in list(remaining):
if child.check_inputs_ready(context):
self._execute_task(child, context)
remaining.remove(child)
progress_made = True
if not progress_made:
# Deadlock: dependencies not resolvable with current data
raise RuntimeError(
f"Workflow '{workflow.name}' deadlock: unresolved dependencies"
)
# Final workflow status based on children results
if any(c.status == Status.FAILED for c in workflow.children):
workflow._set_status(Status.FAILED)
else:
workflow._set_status(Status.COMPLETED)
+83
View File
@@ -0,0 +1,83 @@
from abc import ABC, abstractmethod
from enum import Enum
from typing import List, Any, Optional
from radiuma_api.io_port import InPort
from radiuma_api.io_port import OutPort
from radiuma_api.execution_context import ExecutionContext
# Write Codes Here:
class Status(Enum):
PENDING = "pending"
READY = "ready"
RUNNING = "running"
PAUSED = "paused"
STOPPED = "stopped"
COMPLETED = "completed"
FAILED = "failed"
class TaskEvent(Enum):
BEFORE_RUN = "before_run"
AFTER_RUN = "after_run"
ON_ERROR = "on_error"
STATUS_CHANGED = "status_changed"
class TaskEventListener:
def handle(self, event: TaskEvent, task: "Task", payload: Optional[Any] = None):
pass
class Task(ABC):
def __init__(self, name: str):
self.name = name
self.status = Status.PENDING
self.inputs: List[InPort] = []
self.outputs: List[OutPort] = []
self._listeners: List[TaskEventListener] = []
# Events
def add_listener(self, listener: TaskEventListener):
self._listeners.append(listener)
def _emit(self, event: TaskEvent, payload: Optional[Any] = None):
for listener in self._listeners:
listener.handle(event, self, payload)
def _set_status(self, new_status: Status):
old_status = self.status
self.status = new_status
if old_status != new_status:
self._emit(TaskEvent.STATUS_CHANGED, {"from": old_status, "to": new_status})
# Lifecycle
def pause(self):
if self.status == Status.RUNNING:
self._set_status(Status.PAUSED)
def resume(self):
if self.status == Status.PAUSED:
self._set_status(Status.READY)
def stop(self):
if self.status in (Status.RUNNING, Status.PAUSED, Status.READY):
self._set_status(Status.STOPPED)
# Ports
def _add_input_port(self, port: InPort):
port.parent_task = self
self.inputs.append(port)
def _add_output_port(self, port: OutPort):
port.parent_task = self
self.outputs.append(port)
def check_inputs_ready(self, context: ExecutionContext) -> bool:
"""Check if all required inputs have data in ExecutionContext."""
for in_port in self.inputs:
if in_port.required and not context.has_data(in_port):
return False
return True
# Runnable
@abstractmethod
def run(self, context: ExecutionContext):
pass
@@ -0,0 +1,44 @@
from typing import List
from radiuma_api.task import Task, Status, TaskEvent
from radiuma_api.execution_context import ExecutionContext
from radiuma_api.scheduler import Scheduler
from radiuma_api.io_port import CompatibilityException
# Write Codes Here:
class Workflow(Task):
"""Composite node: aggregates Tasks (Modules or other Workflows)."""
def __init__(self, name: str):
super().__init__(name)
self.children: List[Task] = []
def add_child(self, task: Task):
task.parent_task = self
self.children.append(task)
def run(self, context: ExecutionContext):
"""Delegate execution of children to Scheduler."""
self._set_status(Status.RUNNING)
scheduler = Scheduler()
for child in self.children:
if self.status == Status.STOPPED:
break
try:
if child.check_inputs_ready(context):
scheduler.run(child, context)
else:
print(f"Task {child.name} not ready, skipping.")
except CompatibilityException as e:
self._emit(TaskEvent.ON_ERROR, {"error": e.reason, "task": child.name})
child._set_status(Status.FAILED)
# Final status evaluation
if all(c.status == Status.COMPLETED for c in self.children):
self._set_status(Status.COMPLETED)
elif any(c.status == Status.FAILED for c in self.children):
self._set_status(Status.FAILED)
else:
self._set_status(Status.STOPPED)
@@ -0,0 +1,69 @@
from radiuma_api.workflow import Workflow
from radiuma_api.execution_context import ExecutionContext
from radiuma_api.io_port import InPort, OutPort, ImageType, TextType
from radiuma_api.module import Module
from radiuma_api.task import Status
from radiuma_api.asset import Asset
# Codes Go Below:
class Reader(Module):
def __init__(self):
super().__init__("Reader")
self._add_output_port(OutPort("images", ImageType()))
def run(self, context):
print("[Reader] Loading images...")
context.put(self.outputs[0], Asset(["img1", "img2"], "images"))
self._set_status(Status.COMPLETED)
class Extractor(Module):
def __init__(self):
super().__init__("Extractor")
self._add_input_port(InPort("images", ImageType()))
self._add_output_port(OutPort("features", TextType()))
def run(self, context):
imgs = context.get(self.inputs[0]).data
print(f"[Extractor] Extracting features from: {imgs}")
feats = [{"img": img, "feat": f"feature_of_{img}"} for img in imgs]
context.put(self.outputs[0], Asset(feats, "features"))
self._set_status(Status.COMPLETED)
class Writer(Module):
def __init__(self):
super().__init__("Writer")
self._add_input_port(InPort("features", TextType()))
def run(self, context):
feats = context.get(self.inputs[0]).data
print("[Writer] Saving features:")
for f in feats:
print(" ", f)
self._set_status(Status.COMPLETED)
def main():
ctx = ExecutionContext()
wf = Workflow("RND_Workflow")
r = Reader()
e = Extractor()
w = Writer()
r.outputs[0].connect(e.inputs[0])
e.outputs[0].connect(w.inputs[0])
wf.add_child(r)
wf.add_child(e)
wf.add_child(w)
print("=== Running Workflow ===")
wf.run(ctx)
print("=== Workflow Completed ===")
if __name__ == "__main__":
main()
@@ -0,0 +1,43 @@
from radiuma_api.io_port import (
CompositeType,
ImageType,
TextType,
TableType,
CompatibilityException
)
def test_composite_type_success():
t1 = CompositeType({
"img": ImageType(),
"meta": TextType(language="en")
})
t2 = CompositeType({
"img": ImageType(),
"meta": TextType(language="en")
})
# Must succeed
t1.can_connect_to(t2)
def test_composite_type_key_mismatch():
t1 = CompositeType({"img": ImageType()})
t2 = CompositeType({"image": ImageType()})
try:
t1.can_connect_to(t2)
assert False, "Expected CompositeType key mismatch"
except CompatibilityException:
assert True
def test_composite_type_internal_type_mismatch():
t1 = CompositeType({"img": ImageType(modality="nifti")})
t2 = CompositeType({"img": ImageType(modality="dicom")})
try:
t1.can_connect_to(t2)
assert False, "Expected internal DType mismatch"
except CompatibilityException:
assert True
@@ -0,0 +1,17 @@
from radiuma_api.execution_context import ExecutionContext
from radiuma_api.io_port import OutPort, InPort, ImageType
from radiuma_api.asset import Asset
# Codes Go Below:
def test_context_put_get():
ctx = ExecutionContext()
outp = OutPort("o", ImageType())
inp = InPort("i", ImageType())
outp.connect(inp)
asset = Asset(["img1"], "images")
ctx.put(outp, asset)
assert ctx.get(inp).data == ["img1"]
+39
View File
@@ -0,0 +1,39 @@
from radiuma_api.io_port import InPort, OutPort, ImageType, TextType, CompatibilityException
# Codes Go Below:
def test_compatible_ports():
"""Ports with the same DType must connect successfully."""
outp = OutPort("o", ImageType())
inp = InPort("i", ImageType())
outp.connect(inp)
assert inp.connected_output == outp
def test_incompatible_ports():
"""Connecting mismatched DTypes must raise CompatibilityException."""
outp = OutPort("o", ImageType())
inp = InPort("i", TextType())
try:
outp.connect(inp)
assert False, "Expected CompatibilityException"
except CompatibilityException:
assert True
def test_outport_single_connection_limit():
"""OutPort must not allow more than one connection."""
outp = OutPort("o", ImageType())
inp1 = InPort("i1", ImageType())
inp2 = InPort("i2", ImageType())
outp.connect(inp1)
try:
outp.connect(inp2)
assert False, "Expected OutPort max connections error"
except CompatibilityException:
assert True
@@ -0,0 +1,43 @@
from radiuma_api.scheduler import Scheduler
from radiuma_api.workflow import Workflow
from radiuma_api.module import Module
from radiuma_api.io_port import InPort, OutPort, ImageType
from radiuma_api.execution_context import ExecutionContext
from radiuma_api.task import Status
# Codes Go below:
class Reader(Module):
def __init__(self):
super().__init__("Reader")
self._add_output_port(OutPort("images", ImageType()))
def run(self, context):
context.put(self.outputs[0], "DATA")
self._set_status(Status.COMPLETED)
class Extractor(Module):
def __init__(self):
super().__init__("Extractor")
self._add_input_port(InPort("images", ImageType()))
def run(self, context):
assert context.get(self.inputs[0]) == "DATA"
self._set_status(Status.COMPLETED)
def test_scheduler_runs_in_order():
ctx = ExecutionContext()
wf = Workflow("WF")
r = Reader()
e = Extractor()
r.outputs[0].connect(e.inputs[0])
wf.add_child(r)
wf.add_child(e)
wf.run(ctx)
assert r.status == Status.COMPLETED
assert e.status == Status.COMPLETED
+24
View File
@@ -0,0 +1,24 @@
from radiuma_api.task import Status
from radiuma_api.module import Module
from radiuma_api.execution_context import ExecutionContext
from radiuma_api.io_port import OutPort, ImageType
# Codes Go below:
class Dummy(Module):
def __init__(self):
super().__init__("Dummy")
self._add_output_port(OutPort("o", ImageType()))
def run(self, context):
self._set_status(Status.RUNNING)
context.put(self.outputs[0], None)
self._set_status(Status.COMPLETED)
def test_task_lifecycle():
ctx = ExecutionContext()
t = Dummy()
assert t.status == Status.PENDING
t.run(ctx)
assert t.status == Status.COMPLETED
@@ -0,0 +1,57 @@
from radiuma_api.workflow import Workflow
from radiuma_api.execution_context import ExecutionContext
from radiuma_api.io_port import InPort, OutPort, ImageType, TextType
from radiuma_api.module import Module
from radiuma_api.task import Status
from radiuma_api.asset import Asset
# Codes Go below:
class Reader(Module):
def __init__(self):
super().__init__("Reader")
self._add_output_port(OutPort("images", ImageType()))
def run(self, context):
context.put(self.outputs[0], Asset(["img1"], "images"))
self._set_status(Status.COMPLETED)
class Extractor(Module):
def __init__(self):
super().__init__("Extractor")
self._add_input_port(InPort("images", ImageType()))
self._add_output_port(OutPort("features", TextType()))
def run(self, context):
imgs = context.get(self.inputs[0]).data
context.put(self.outputs[0], Asset([{"img": imgs[0]}], "features"))
self._set_status(Status.COMPLETED)
class Writer(Module):
def __init__(self):
super().__init__("Writer")
self._add_input_port(InPort("features", TextType()))
def run(self, context):
feats = context.get(self.inputs[0]).data
assert feats[0]["img"] == "img1"
self._set_status(Status.COMPLETED)
def test_end_to_end():
ctx = ExecutionContext()
wf = Workflow("WF")
r = Reader()
e = Extractor()
w = Writer()
r.outputs[0].connect(e.inputs[0])
e.outputs[0].connect(w.inputs[0])
wf.add_child(r)
wf.add_child(e)
wf.add_child(w)
wf.run(ctx)
assert w.status == Status.COMPLETED