mona / utils /automation /templates.py
mrradix's picture
Update utils/automation/templates.py
75090bf verified
import json
import uuid
from typing import Dict, List, Any, Optional, Callable, Union
from datetime import datetime
import re
from utils.logging import setup_logger
from utils.error_handling import handle_exceptions, AutomationError
from utils.storage import load_data, save_data
# Initialize logger
logger = setup_logger(__name__)
class Template:
"""Template for auto-applying patterns"""
def __init__(self, name: str, template_type: str, content: Dict[str, Any],
description: Optional[str] = None):
"""Initialize a template
Args:
name: Template name
template_type: Type of template (task, note, goal, project, etc.)
content: Template content
description: Template description (optional)
"""
self.id = str(uuid.uuid4())
self.name = name
self.description = description or ""
self.template_type = template_type
self.content = content
self.created_at = datetime.now().isoformat()
self.updated_at = self.created_at
self.usage_count = 0
self.last_used = None
self.is_default = False
self.tags = []
self.variables = self._extract_variables()
def _extract_variables(self) -> List[str]:
"""Extract variables from template content
Returns:
List of variable names
"""
variables = []
# Convert content to string for searching
content_str = json.dumps(self.content)
# Find all {{variable}} patterns
pattern = r'\{\{([^\}]+)\}\}'
matches = re.findall(pattern, content_str)
# Add unique variables
for match in matches:
var_name = match.strip()
if var_name and var_name not in variables:
variables.append(var_name)
return variables
@handle_exceptions
def apply(self, variables: Dict[str, Any] = None) -> Dict[str, Any]:
"""Apply template with variables
Args:
variables: Dictionary of variable values (optional)
Returns:
Applied template content
"""
variables = variables or {}
result = self._replace_variables(self.content, variables)
# Update usage stats
self.usage_count += 1
self.last_used = datetime.now().isoformat()
self.updated_at = self.last_used
return result
def _replace_variables(self, content: Any, variables: Dict[str, Any]) -> Any:
"""Replace variables in content recursively
Args:
content: Content to process
variables: Dictionary of variable values
Returns:
Processed content
"""
if isinstance(content, str):
# Replace variables in string
result = content
for var_name, var_value in variables.items():
pattern = r"\{\{\s*" + var_name + r"\s*\}\}"
result = re.sub(pattern, str(var_value), result)
return result
elif isinstance(content, dict):
# Process dictionary recursively
result = {}
for key, value in content.items():
# Process both key and value
new_key = self._replace_variables(key, variables)
new_value = self._replace_variables(value, variables)
result[new_key] = new_value
return result
elif isinstance(content, list):
# Process list recursively
result = []
for item in content:
result.append(self._replace_variables(item, variables))
return result
else:
# Return other types unchanged
return content
@handle_exceptions
def set_as_default(self, is_default: bool = True) -> None:
"""Set template as default
Args:
is_default: Whether template is default
"""
self.is_default = is_default
self.updated_at = datetime.now().isoformat()
@handle_exceptions
def add_tag(self, tag: str) -> None:
"""Add tag to template
Args:
tag: Tag to add
"""
if tag not in self.tags:
self.tags.append(tag)
self.updated_at = datetime.now().isoformat()
@handle_exceptions
def remove_tag(self, tag: str) -> None:
"""Remove tag from template
Args:
tag: Tag to remove
"""
if tag in self.tags:
self.tags.remove(tag)
self.updated_at = datetime.now().isoformat()
@handle_exceptions
def to_dict(self) -> Dict[str, Any]:
"""Convert template to dictionary
Returns:
Template as dictionary
"""
return {
"id": self.id,
"name": self.name,
"description": self.description,
"template_type": self.template_type,
"content": self.content,
"created_at": self.created_at,
"updated_at": self.updated_at,
"usage_count": self.usage_count,
"last_used": self.last_used,
"is_default": self.is_default,
"tags": self.tags,
"variables": self.variables
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'Template':
"""Create template from dictionary
Args:
data: Template data
Returns:
Template instance
"""
template = cls(
data["name"],
data["template_type"],
data["content"],
data.get("description", "")
)
template.id = data["id"]
template.created_at = data["created_at"]
template.updated_at = data["updated_at"]
template.usage_count = data.get("usage_count", 0)
template.last_used = data.get("last_used")
template.is_default = data.get("is_default", False)
template.tags = data.get("tags", [])
template.variables = data.get("variables", [])
return template
class TemplateManager:
"""Manager for templates"""
def __init__(self):
"""Initialize template manager"""
self.templates = {}
self.load_templates()
self._ensure_default_templates()
@handle_exceptions
def load_templates(self) -> None:
"""Load templates from storage"""
try:
templates_data = load_data("templates", default=[])
for template_data in templates_data:
template = Template.from_dict(template_data)
self.templates[template.id] = template
logger.info(f"Loaded {len(self.templates)} templates")
except Exception as e:
logger.error(f"Failed to load templates: {str(e)}")
@handle_exceptions
def save_templates(self) -> None:
"""Save templates to storage"""
try:
templates_data = [template.to_dict() for template in self.templates.values()]
save_data("templates", templates_data)
logger.info(f"Saved {len(self.templates)} templates")
except Exception as e:
logger.error(f"Failed to save templates: {str(e)}")
def _ensure_default_templates(self) -> None:
"""Ensure default templates exist"""
# Check if we need to create default templates
if not self.get_templates_by_type("task"):
self._create_default_task_template()
if not self.get_templates_by_type("note"):
self._create_default_note_template()
if not self.get_templates_by_type("goal"):
self._create_default_goal_template()
if not self.get_templates_by_type("project"):
self._create_default_project_template()
def _create_default_task_template(self) -> None:
"""Create default task template"""
content = {
"title": "{{title}}",
"description": "{{description}}",
"status": "pending",
"priority": "{{priority|normal}}",
"due_date": "{{due_date}}",
"tags": ["{{tag}}"],
"category": "{{category|general}}"
}
template = Template("Default Task", "task", content, "Default template for tasks")
template.set_as_default(True)
template.add_tag("default")
self.templates[template.id] = template
self.save_templates()
def _create_default_note_template(self) -> None:
"""Create default note template"""
content = {
"title": "{{title}}",
"content": "{{content}}",
"tags": ["{{tag}}"],
"category": "{{category|general}}"
}
template = Template("Default Note", "note", content, "Default template for notes")
template.set_as_default(True)
template.add_tag("default")
self.templates[template.id] = template
self.save_templates()
def _create_default_goal_template(self) -> None:
"""Create default goal template"""
content = {
"title": "{{title}}",
"description": "{{description}}",
"target_date": "{{target_date}}",
"status": "in_progress",
"progress": 0,
"tags": ["{{tag}}"],
"category": "{{category|personal}}"
}
template = Template("Default Goal", "goal", content, "Default template for goals")
template.set_as_default(True)
template.add_tag("default")
self.templates[template.id] = template
self.save_templates()
def _create_default_project_template(self) -> None:
"""Create default project template"""
content = {
"title": "{{title}}",
"description": "{{description}}",
"start_date": "{{start_date}}",
"end_date": "{{end_date}}",
"status": "planning",
"progress": 0,
"tags": ["{{tag}}"],
"category": "{{category|work}}",
"tasks": []
}
template = Template("Default Project", "project", content, "Default template for projects")
template.set_as_default(True)
template.add_tag("default")
self.templates[template.id] = template
self.save_templates()
@handle_exceptions
def create_template(self, name: str, template_type: str, content: Dict[str, Any],
description: Optional[str] = None) -> Template:
"""Create a new template
Args:
name: Template name
template_type: Type of template
content: Template content
description: Template description (optional)
Returns:
Created template
"""
template = Template(name, template_type, content, description)
self.templates[template.id] = template
self.save_templates()
return template
@handle_exceptions
def get_template(self, template_id: str) -> Optional[Template]:
"""Get template by ID
Args:
template_id: Template ID
Returns:
Template if found, None otherwise
"""
return self.templates.get(template_id)
@handle_exceptions
def update_template(self, template: Template) -> None:
"""Update template
Args:
template: Template to update
"""
if template.id in self.templates:
template.updated_at = datetime.now().isoformat()
self.templates[template.id] = template
self.save_templates()
else:
raise AutomationError(f"Template not found: {template.id}")
@handle_exceptions
def delete_template(self, template_id: str) -> None:
"""Delete template
Args:
template_id: Template ID
"""
if template_id in self.templates:
del self.templates[template_id]
self.save_templates()
else:
raise AutomationError(f"Template not found: {template_id}")
@handle_exceptions
def get_all_templates(self) -> List[Template]:
"""Get all templates
Returns:
List of all templates
"""
return list(self.templates.values())
@handle_exceptions
def get_templates_by_type(self, template_type: str) -> List[Template]:
"""Get templates by type
Args:
template_type: Template type
Returns:
List of templates of the specified type
"""
return [
template for template in self.templates.values()
if template.template_type == template_type
]
@handle_exceptions
def get_templates_by_tag(self, tag: str) -> List[Template]:
"""Get templates by tag
Args:
tag: Tag to filter by
Returns:
List of templates with the specified tag
"""
return [
template for template in self.templates.values()
if tag in template.tags
]
@handle_exceptions
def get_default_template(self, template_type: str) -> Optional[Template]:
"""Get default template for a type
Args:
template_type: Template type
Returns:
Default template if found, None otherwise
"""
templates = self.get_templates_by_type(template_type)
for template in templates:
if template.is_default:
return template
# If no default template found, return the first one
return templates[0] if templates else None
@handle_exceptions
def set_default_template(self, template_id: str) -> None:
"""Set a template as default for its type
Args:
template_id: Template ID
"""
template = self.get_template(template_id)
if not template:
raise AutomationError(f"Template not found: {template_id}")
# Clear default flag for all templates of this type
for t in self.get_templates_by_type(template.template_type):
if t.is_default:
t.set_as_default(False)
self.update_template(t)
# Set this template as default
template.set_as_default(True)
self.update_template(template)
@handle_exceptions
def apply_template(self, template_id: str, variables: Dict[str, Any] = None) -> Dict[str, Any]:
"""Apply a template with variables
Args:
template_id: Template ID
variables: Dictionary of variable values (optional)
Returns:
Applied template content
"""
template = self.get_template(template_id)
if not template:
raise AutomationError(f"Template not found: {template_id}")
result = template.apply(variables)
self.update_template(template) # Update usage stats
return result
@handle_exceptions
def apply_default_template(self, template_type: str, variables: Dict[str, Any] = None) -> Dict[str, Any]:
"""Apply the default template for a type
Args:
template_type: Template type
variables: Dictionary of variable values (optional)
Returns:
Applied template content
"""
template = self.get_default_template(template_type)
if not template:
raise AutomationError(f"No template found for type: {template_type}")
return self.apply_template(template.id, variables)
@handle_exceptions
def duplicate_template(self, template_id: str, new_name: Optional[str] = None) -> Template:
"""Duplicate a template
Args:
template_id: Template ID
new_name: New template name (optional)
Returns:
Duplicated template
"""
template = self.get_template(template_id)
if not template:
raise AutomationError(f"Template not found: {template_id}")
# Create new name if not provided
if not new_name:
new_name = f"Copy of {template.name}"
# Create new template
new_template = Template(
new_name,
template.template_type,
template.content.copy(),
template.description
)
# Copy tags
for tag in template.tags:
new_template.add_tag(tag)
# Add to templates
self.templates[new_template.id] = new_template
self.save_templates()
return new_template
@handle_exceptions
def import_templates(self, templates_data: List[Dict[str, Any]],
overwrite_existing: bool = False) -> int:
"""Import templates from data
Args:
templates_data: List of template data
overwrite_existing: Whether to overwrite existing templates
Returns:
Number of templates imported
"""
imported_count = 0
for template_data in templates_data:
try:
template = Template.from_dict(template_data)
# Check if template with same ID exists
if template.id in self.templates:
if overwrite_existing:
self.templates[template.id] = template
imported_count += 1
else:
self.templates[template.id] = template
imported_count += 1
except Exception as e:
logger.error(f"Failed to import template: {str(e)}")
if imported_count > 0:
self.save_templates()
return imported_count
@handle_exceptions
def export_templates(self, template_ids: Optional[List[str]] = None) -> List[Dict[str, Any]]:
"""Export templates to data
Args:
template_ids: List of template IDs to export (optional, exports all if None)
Returns:
List of template data
"""
if template_ids:
# Export specific templates
templates = []
for template_id in template_ids:
template = self.get_template(template_id)
if template:
templates.append(template)
else:
# Export all templates
templates = self.get_all_templates()
return [template.to_dict() for template in templates]
# Create a global instance of the template manager
template_manager = TemplateManager()