""" Template Data Models Contains data models for workflow templates and template parameters. """ from dataclasses import dataclass, field from datetime import datetime from enum import Enum from typing import Any, Dict, List, Optional from uuid import uuid4 from .visual_workflow import VisualWorkflow, ParameterType class TemplateDifficulty(Enum): """Difficulty levels for templates""" BEGINNER = 'beginner' INTERMEDIATE = 'intermediate' ADVANCED = 'advanced' @dataclass class TemplateParameter: """Configurable parameter for a template""" name: str type: ParameterType description: str default_value: Optional[Any] = None # Mapping to workflow nodes node_id: str = "" parameter_name: str = "" # UI hints label: str = "" placeholder: Optional[str] = None required: bool = True def to_dict(self) -> Dict[str, Any]: return { 'name': self.name, 'type': self.type.value, 'description': self.description, 'default_value': self.default_value, 'node_id': self.node_id, 'parameter_name': self.parameter_name, 'label': self.label, 'placeholder': self.placeholder, 'required': self.required } @classmethod def from_dict(cls, data: Dict[str, Any]) -> 'TemplateParameter': return cls( name=data['name'], type=ParameterType(data['type']), description=data['description'], default_value=data.get('default_value'), node_id=data.get('node_id', ''), parameter_name=data.get('parameter_name', ''), label=data.get('label', ''), placeholder=data.get('placeholder'), required=data.get('required', True) ) @dataclass class WorkflowTemplate: """Template for creating workflows""" id: str name: str description: str category: str # Template workflow structure workflow: VisualWorkflow # Configurable parameters parameters: List[TemplateParameter] = field(default_factory=list) # Metadata tags: List[str] = field(default_factory=list) difficulty: TemplateDifficulty = TemplateDifficulty.BEGINNER estimated_time: int = 5 # minutes # Usage statistics usage_count: int = 0 rating: float = 0.0 # Timestamps created_at: datetime = field(default_factory=datetime.now) updated_at: datetime = field(default_factory=datetime.now) created_by: str = "system" def to_dict(self) -> Dict[str, Any]: return { 'id': self.id, 'name': self.name, 'description': self.description, 'category': self.category, 'workflow': self.workflow.to_dict(), 'parameters': [p.to_dict() for p in self.parameters], 'tags': self.tags, 'difficulty': self.difficulty.value, 'estimated_time': self.estimated_time, 'usage_count': self.usage_count, 'rating': self.rating, 'created_at': self.created_at.isoformat(), 'updated_at': self.updated_at.isoformat(), 'created_by': self.created_by } @classmethod def from_dict(cls, data: Dict[str, Any]) -> 'WorkflowTemplate': return cls( id=data['id'], name=data['name'], description=data['description'], category=data['category'], workflow=VisualWorkflow.from_dict(data['workflow']), parameters=[TemplateParameter.from_dict(p) for p in data.get('parameters', [])], tags=data.get('tags', []), difficulty=TemplateDifficulty(data.get('difficulty', 'beginner')), estimated_time=data.get('estimated_time', 5), usage_count=data.get('usage_count', 0), rating=data.get('rating', 0.0), created_at=datetime.fromisoformat(data.get('created_at', datetime.now().isoformat())), updated_at=datetime.fromisoformat(data.get('updated_at', datetime.now().isoformat())), created_by=data.get('created_by', 'system') ) def instantiate(self, parameters: Dict[str, Any], workflow_name: str, created_by: str = "user") -> VisualWorkflow: """Create a new workflow instance from this template""" # Create a copy of the template workflow workflow_data = self.workflow.to_dict() # Generate new IDs workflow_data['id'] = str(uuid4()) workflow_data['name'] = workflow_name workflow_data['created_at'] = datetime.now().isoformat() workflow_data['updated_at'] = datetime.now().isoformat() workflow_data['created_by'] = created_by workflow_data['is_template'] = False # Apply parameter substitutions for param in self.parameters: if param.name in parameters: value = parameters[param.name] # Find the target node and update its parameter for node_data in workflow_data['nodes']: if node_data['id'] == param.node_id: node_data['parameters'][param.parameter_name] = value break # Generate new node and edge IDs to avoid conflicts node_id_mapping = {} for i, node_data in enumerate(workflow_data['nodes']): old_id = node_data['id'] new_id = f"{workflow_data['id']}_node_{i}" node_id_mapping[old_id] = new_id node_data['id'] = new_id # Update edge references for edge_data in workflow_data['edges']: edge_data['id'] = str(uuid4()) edge_data['source'] = node_id_mapping.get(edge_data['source'], edge_data['source']) edge_data['target'] = node_id_mapping.get(edge_data['target'], edge_data['target']) return VisualWorkflow.from_dict(workflow_data) def validate(self) -> List[str]: """Validate template structure""" errors = [] # Basic validation if not self.id: errors.append("Template ID is required") if not self.name: errors.append("Template name is required") if not self.category: errors.append("Template category is required") # Validate workflow workflow_errors = self.workflow.validate() errors.extend([f"Workflow: {err}" for err in workflow_errors]) # Validate parameters node_ids = {node.id for node in self.workflow.nodes} for param in self.parameters: if param.node_id and param.node_id not in node_ids: errors.append(f"Parameter {param.name} references non-existent node {param.node_id}") return errors def generate_template_id() -> str: """Generate a unique template ID""" return f"template_{str(uuid4())[:8]}"