Skip to content
Open
47 changes: 26 additions & 21 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
### AL ###
#Template for AL projects for Dynamics 365 Business Central
#launch.json folder
__pycache__/
<<<<<<< HEAD
*.pyc
*.pyo
*.pyd
=======
*.py[cod]
*$py.class
>>>>>>> pr-3-Santix1234-ALP-Looping
.pytest_cache/
.coverage
htmlcov/
.env
<<<<<<< HEAD
=======
*.log
>>>>>>> pr-3-Santix1234-ALP-Looping
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
<<<<<<< HEAD
.idea/
*.log
=======
.idea/
>>>>>>> pr-3-Santix1234-ALP-Looping
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']
147 changes: 147 additions & 0 deletions src/alp/iteration_tracker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
from typing import Dict, Any, Optional
from enum import Enum, auto
import logging
from src.iteration_state import IterationStatus # Import unified enum

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, allowing iterations up to and including max_iterations
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))
185 changes: 185 additions & 0 deletions src/iteration_state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
from __future__ import annotations
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum, auto
from typing import Any, Dict, Optional, List
import json
import os
import logging


class IterationStatus(Enum):
"""
Comprehensive enumeration of iteration statuses across the system.

Combines statuses from different iteration tracking mechanisms.
"""
# Initial and Pending States
INITIALIZED = auto() # From tracker
PENDING = auto() # From state

# Active States
IN_PROGRESS = auto() # From tracker
RUNNING = auto() # From state

# Terminal States
COMPLETED = auto() # Common to both
TERMINATED = auto() # From tracker
INTERRUPTED = auto() # From state

# Error States
ERROR = auto() # From tracker
FAILED = auto() # From state


@dataclass
class IterationState:
"""
Represents the state of a single learning iteration.

Provides comprehensive tracking of iteration details, including
configuration, performance metrics, status, and context.
"""
iteration_id: str
status: IterationStatus = IterationStatus.PENDING
start_time: Optional[datetime] = None
end_time: Optional[datetime] = None
configuration: Dict[str, Any] = field(default_factory=dict)
metrics: Dict[str, Any] = field(default_factory=dict)
error_details: Optional[str] = None
context: Dict[str, Any] = field(default_factory=dict)

def mark_started(self) -> None:
"""
Mark the iteration as started and record the start time.
"""
self.status = IterationStatus.RUNNING
self.start_time = datetime.now()

def mark_completed(self) -> None:
"""
Mark the iteration as completed and record the end time.
"""
self.status = IterationStatus.COMPLETED
self.end_time = datetime.now()

def mark_failed(self, error_message: str) -> None:
"""
Mark the iteration as failed and store error details.

Args:
error_message: Description of the failure
"""
self.status = IterationStatus.FAILED
self.end_time = datetime.now()
self.error_details = error_message

def mark_interrupted(self) -> None:
"""
Mark the iteration as interrupted.
"""
self.status = IterationStatus.INTERRUPTED
self.end_time = datetime.now()

def to_dict(self) -> Dict[str, Any]:
"""
Convert the iteration state to a dictionary representation.

Returns:
Dict representation of the iteration state
"""
return {
'iteration_id': self.iteration_id,
'status': self.status.name,
'start_time': self.start_time.isoformat() if self.start_time else None,
'end_time': self.end_time.isoformat() if self.end_time else None,
'configuration': self.configuration,
'metrics': self.metrics,
'error_details': self.error_details,
'context': self.context
}

@classmethod
def from_dict(cls, data: Dict[str, Any]) -> IterationState:
"""
Create an IterationState instance from a dictionary.

Args:
data: Dictionary containing iteration state data

Returns:
Reconstructed IterationState
"""
state = cls(iteration_id=data['iteration_id'])
state.status = IterationStatus[data['status']]
state.start_time = datetime.fromisoformat(data['start_time']) if data['start_time'] else None
state.end_time = datetime.fromisoformat(data['end_time']) if data['end_time'] else None
state.configuration = data['configuration']
state.metrics = data['metrics']
state.error_details = data['error_details']
state.context = data['context']
return state


class IterationStateManager:
"""
Manages the storage, retrieval, and tracking of iteration states.
"""
def __init__(self, state_dir: str = 'iteration_states'):
"""
Initialize the IterationStateManager.

Args:
state_dir: Directory to store iteration states
"""
self.state_dir = state_dir
os.makedirs(state_dir, exist_ok=True)

def save_state(self, iteration_state: IterationState) -> None:
"""
Save an iteration state to a JSON file.

Args:
iteration_state: State to save
"""
state_file = os.path.join(self.state_dir, f'{iteration_state.iteration_id}.json')
with open(state_file, 'w') as f:
json.dump(iteration_state.to_dict(), f, indent=2)

def load_state(self, iteration_id: str) -> Optional[IterationState]:
"""
Load an iteration state from a file.

Args:
iteration_id: ID of the iteration to load

Returns:
Loaded IterationState or None if not found
"""
state_file = os.path.join(self.state_dir, f'{iteration_id}.json')
try:
with open(state_file, 'r') as f:
state_data = json.load(f)
return IterationState.from_dict(state_data)
except FileNotFoundError:
return None

def get_states_by_status(self, status: IterationStatus) -> List[IterationState]:
"""
Retrieve all iteration states with a specific status.

Args:
status: Status to filter by

Returns:
List of matching IterationState instances
"""
states = []
for filename in os.listdir(self.state_dir):
if filename.endswith('.json'):
state_path = os.path.join(self.state_dir, filename)
with open(state_path, 'r') as f:
state_data = json.load(f)
if IterationStatus[state_data['status']] == status:
states.append(IterationState.from_dict(state_data))
return states
Loading