Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 13 additions & 21 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
### AL ###
#Template for AL projects for Dynamics 365 Business Central
#launch.json folder
__pycache__/
*.pyc
*.pyo
*.pyd
.pytest_cache/
.coverage
htmlcov/
.env
dist/
build/
*.egg-info/
.vscode/
#Cache folder
.alcache/
#Symbols folder
.alpackages/
#Snapshots folder
.snapshots/
#Testing Output folder
.output/
#Extension App-file
*.app
#Rapid Application Development File
rad.json
#Translation Base-file
*.g.xlf
#License-file
*.flf
#Test results file
TestResults.xml
.idea/
*.log
3 changes: 3 additions & 0 deletions src/alp/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .iteration_tracker import IterationTracker, IterationStatus

__all__ = ['IterationTracker', 'IterationStatus']
155 changes: 155 additions & 0 deletions src/alp/iteration_tracker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
from typing import Dict, Any, Optional
from enum import Enum, auto
import logging

class IterationStatus(Enum):
"""Enumeration representing possible iteration statuses."""
INITIALIZED = auto()
IN_PROGRESS = auto()
COMPLETED = auto()
TERMINATED = auto()
ERROR = auto()

class IterationTracker:
"""
A comprehensive iteration tracking mechanism for the Adaptive Learning Process.

Manages iteration state, progression, and provides detailed tracking capabilities.

Attributes:
_current_iteration (int): Current iteration number
_max_iterations (Optional[int]): Maximum number of iterations allowed
_status (IterationStatus): Current status of the iteration process
_metadata (Dict[str, Any]): Additional metadata for tracking
"""

def __init__(self, max_iterations: Optional[int] = None):
"""
Initialize the IterationTracker.

Args:
max_iterations (Optional[int], optional): Maximum number of iterations allowed.
Defaults to None (unlimited iterations).
"""
self._current_iteration = 0
self._max_iterations = max_iterations
self._status = IterationStatus.INITIALIZED
self._metadata: Dict[str, Any] = {}
self._logger = logging.getLogger(self.__class__.__name__)

def start(self) -> None:
"""
Start the iteration process, setting the initial state.

Raises:
RuntimeError: If iterations have already been started.
"""
if self._status != IterationStatus.INITIALIZED:
raise RuntimeError("Iteration process has already been started.")

self._status = IterationStatus.IN_PROGRESS
self._logger.info("Iteration process started.")

def next_iteration(self) -> bool:
"""
Advance to the next iteration.

Returns:
bool: True if iterations can continue, False otherwise.

Raises:
RuntimeError: If iteration process is not in progress.
"""
if self._status != IterationStatus.IN_PROGRESS:
raise RuntimeError("Iteration process is not in progress.")

self._current_iteration += 1

# Check iteration limit
if (self._max_iterations is not None and
self._current_iteration >= self._max_iterations):
self.complete()
return False

self._logger.info(f"Starting iteration {self._current_iteration}")
return True

def complete(self) -> None:
"""
Mark the iteration process as completed."""
self._status = IterationStatus.COMPLETED
self._logger.info("Iteration process completed successfully.")

def terminate(self, reason: Optional[str] = None) -> None:
"""
Terminate the iteration process prematurely.

Args:
reason (Optional[str], optional): Reason for termination.
"""
self._status = IterationStatus.TERMINATED
self._logger.warning(f"Iteration process terminated. Reason: {reason}")

def error(self, error_details: Optional[str] = None) -> None:
"""
Mark the iteration process as encountering an error.

Args:
error_details (Optional[str], optional): Details of the error.
"""
self._status = IterationStatus.ERROR
self._logger.error(f"Iteration process encountered an error: {error_details}")

@property
def current_iteration(self) -> int:
"""
Get the current iteration number.

Returns:
int: Current iteration number.
"""
return self._current_iteration

@property
def status(self) -> IterationStatus:
"""
Get the current iteration status.

Returns:
IterationStatus: Current status of the iteration process.
"""
return self._status

def add_metadata(self, key: str, value: Any) -> None:
"""
Add metadata to the iteration tracking.

Args:
key (str): Metadata key
value (Any): Metadata value
"""
self._metadata[key] = value

def get_metadata(self, key: str, default: Optional[Any] = None) -> Optional[Any]:
"""
Retrieve metadata by key.

Args:
key (str): Metadata key
default (Optional[Any], optional): Default value if key is not found

Returns:
Optional[Any]: Metadata value or default
"""
return self._metadata.get(key, default)

def is_iteration_allowed(self) -> bool:
"""
Check if another iteration is allowed.

Returns:
bool: True if iteration can continue, False otherwise.
"""
return (self._status == IterationStatus.IN_PROGRESS and
(self._max_iterations is None or
self._current_iteration < self._max_iterations))
91 changes: 91 additions & 0 deletions tests/test_iteration_tracker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import pytest
from src.alp.iteration_tracker import IterationTracker, IterationStatus

def test_iteration_tracker_initialization():
"""Test basic initialization of IterationTracker."""
tracker = IterationTracker()
assert tracker.current_iteration == 0
assert tracker.status == IterationStatus.INITIALIZED

def test_iteration_tracker_start():
"""Test starting the iteration process."""
tracker = IterationTracker()
tracker.start()
assert tracker.status == IterationStatus.IN_PROGRESS

def test_iteration_tracker_next_iteration():
"""Test advancing to next iteration."""
tracker = IterationTracker()
tracker.start()
assert tracker.next_iteration() is True
assert tracker.current_iteration == 1

def test_iteration_tracker_max_iterations():
"""Test iteration limit."""
tracker = IterationTracker(max_iterations=3)
tracker.start()

# Simulate iterations
for _ in range(3):
assert tracker.next_iteration() is True

# Fourth iteration should return False
assert tracker.next_iteration() is False
assert tracker.status == IterationStatus.COMPLETED

def test_iteration_tracker_manual_complete():
"""Test manual completion of iterations."""
tracker = IterationTracker()
tracker.start()
tracker.complete()
assert tracker.status == IterationStatus.COMPLETED

def test_iteration_tracker_terminate():
"""Test termination of iterations."""
tracker = IterationTracker()
tracker.start()
tracker.terminate(reason="Test termination")
assert tracker.status == IterationStatus.TERMINATED

def test_iteration_tracker_error():
"""Test error handling in iterations."""
tracker = IterationTracker()
tracker.start()
tracker.error(error_details="Test error")
assert tracker.status == IterationStatus.ERROR

def test_iteration_tracker_metadata():
"""Test metadata management."""
tracker = IterationTracker()
tracker.add_metadata("test_key", "test_value")
assert tracker.get_metadata("test_key") == "test_value"
assert tracker.get_metadata("nonexistent_key") is None

def test_iteration_tracker_is_iteration_allowed():
"""Test iteration allowance checks."""
tracker = IterationTracker(max_iterations=2)
assert not tracker.is_iteration_allowed() # Not started

tracker.start()
assert tracker.is_iteration_allowed()

tracker.next_iteration()
assert tracker.is_iteration_allowed()

tracker.next_iteration()
assert not tracker.is_iteration_allowed() # Max iterations reached

def test_iteration_tracker_invalid_state_errors():
"""Test error handling for invalid state transitions."""
tracker = IterationTracker()

# Trying to get next iteration before start
with pytest.raises(RuntimeError):
tracker.next_iteration()

# Start the tracker
tracker.start()

# Trying to start again
with pytest.raises(RuntimeError):
tracker.start()