tallyfy 1.0.16__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. tallyfy/__init__.py +27 -0
  2. tallyfy/__pycache__/__init__.cpython-310.pyc +0 -0
  3. tallyfy/__pycache__/core.cpython-310.pyc +0 -0
  4. tallyfy/__pycache__/form_fields_management.cpython-310.pyc +0 -0
  5. tallyfy/__pycache__/models.cpython-310.pyc +0 -0
  6. tallyfy/__pycache__/task_management.cpython-310.pyc +0 -0
  7. tallyfy/__pycache__/template_management.cpython-310.pyc +0 -0
  8. tallyfy/__pycache__/user_management.cpython-310.pyc +0 -0
  9. tallyfy/core.py +361 -0
  10. tallyfy/form_fields_management/__init__.py +70 -0
  11. tallyfy/form_fields_management/__pycache__/__init__.cpython-310.pyc +0 -0
  12. tallyfy/form_fields_management/__pycache__/base.cpython-310.pyc +0 -0
  13. tallyfy/form_fields_management/__pycache__/crud_operations.cpython-310.pyc +0 -0
  14. tallyfy/form_fields_management/__pycache__/options_management.cpython-310.pyc +0 -0
  15. tallyfy/form_fields_management/__pycache__/suggestions.cpython-310.pyc +0 -0
  16. tallyfy/form_fields_management/base.py +109 -0
  17. tallyfy/form_fields_management/crud_operations.py +234 -0
  18. tallyfy/form_fields_management/options_management.py +222 -0
  19. tallyfy/form_fields_management/suggestions.py +411 -0
  20. tallyfy/models.py +1464 -0
  21. tallyfy/organization_management/__init__.py +26 -0
  22. tallyfy/organization_management/base.py +76 -0
  23. tallyfy/organization_management/retrieval.py +39 -0
  24. tallyfy/task_management/__init__.py +81 -0
  25. tallyfy/task_management/__pycache__/__init__.cpython-310.pyc +0 -0
  26. tallyfy/task_management/__pycache__/base.cpython-310.pyc +0 -0
  27. tallyfy/task_management/__pycache__/creation.cpython-310.pyc +0 -0
  28. tallyfy/task_management/__pycache__/retrieval.cpython-310.pyc +0 -0
  29. tallyfy/task_management/__pycache__/search.cpython-310.pyc +0 -0
  30. tallyfy/task_management/base.py +125 -0
  31. tallyfy/task_management/creation.py +221 -0
  32. tallyfy/task_management/retrieval.py +252 -0
  33. tallyfy/task_management/search.py +198 -0
  34. tallyfy/template_management/__init__.py +85 -0
  35. tallyfy/template_management/analysis.py +1099 -0
  36. tallyfy/template_management/automation.py +469 -0
  37. tallyfy/template_management/base.py +56 -0
  38. tallyfy/template_management/basic_operations.py +479 -0
  39. tallyfy/template_management/health_assessment.py +793 -0
  40. tallyfy/user_management/__init__.py +70 -0
  41. tallyfy/user_management/__pycache__/__init__.cpython-310.pyc +0 -0
  42. tallyfy/user_management/__pycache__/base.cpython-310.pyc +0 -0
  43. tallyfy/user_management/__pycache__/invitation.cpython-310.pyc +0 -0
  44. tallyfy/user_management/__pycache__/retrieval.cpython-310.pyc +0 -0
  45. tallyfy/user_management/base.py +146 -0
  46. tallyfy/user_management/invitation.py +286 -0
  47. tallyfy/user_management/retrieval.py +381 -0
  48. tallyfy-1.0.16.dist-info/METADATA +742 -0
  49. tallyfy-1.0.16.dist-info/RECORD +52 -0
  50. tallyfy-1.0.16.dist-info/WHEEL +5 -0
  51. tallyfy-1.0.16.dist-info/licenses/LICENSE +21 -0
  52. tallyfy-1.0.16.dist-info/top_level.txt +1 -0
@@ -0,0 +1,252 @@
1
+ """
2
+ Task and process retrieval operations
3
+ """
4
+
5
+ from typing import List, Optional
6
+ from .base import TaskManagerBase
7
+ from ..models import Task, Run, TasksList, RunsList, TallyfyError
8
+
9
+
10
+ class TaskRetrieval(TaskManagerBase):
11
+ """Handles task and process retrieval operations"""
12
+
13
+ def get_my_tasks(self, org_id: str, page: int = 1, per_page: int = 100) -> TasksList:
14
+ """
15
+ Get all tasks assigned to the current user in the organization.
16
+
17
+ Args:
18
+ org_id: Organization ID
19
+ page: Page number (default: 1)
20
+ per_page: Number of results per page (default: 100)
21
+
22
+ Returns:
23
+ TasksList object containing tasks and pagination metadata (total count, etc.)
24
+
25
+ Raises:
26
+ TallyfyError: If the request fails
27
+ """
28
+ self._validate_org_id(org_id)
29
+
30
+ try:
31
+ endpoint = f"organizations/{org_id}/me/tasks"
32
+ params = self._build_query_params(page=page, per_page=per_page)
33
+ response_data = self.sdk._make_request('GET', endpoint, params=params)
34
+
35
+ # Return the structured response with pagination
36
+ return TasksList.from_dict(response_data)
37
+
38
+ except TallyfyError:
39
+ raise
40
+ except Exception as e:
41
+ self._handle_api_error(e, "get my tasks", org_id=org_id)
42
+
43
+ def get_user_tasks(self, org_id: str, user_id: int, page: int = 1, per_page: int = 100,
44
+ sort_by: str = 'newest', status: str = 'all') -> TasksList:
45
+ """
46
+ Get all tasks assigned to the given user in the organization.
47
+
48
+ Args:
49
+ org_id: Organization ID
50
+ user_id: User ID
51
+ page: Page number (default: 1)
52
+ per_page: Number of results per page (default: 100)
53
+ sort_by: Sort order (default: 'newest')
54
+ status: Task status filter (default: 'all')
55
+
56
+ Returns:
57
+ TasksList object containing tasks and pagination metadata (total count, etc.)
58
+
59
+ Raises:
60
+ TallyfyError: If the request fails
61
+ """
62
+ self._validate_org_id(org_id)
63
+ self._validate_user_id(user_id)
64
+
65
+ try:
66
+ endpoint = f"organizations/{org_id}/users/{user_id}/tasks"
67
+ params = {
68
+ 'page': str(page),
69
+ 'per_page': str(per_page),
70
+ 'sort_by': sort_by,
71
+ 'status': status,
72
+ 'with': 'run,threads_count,step,tags,folders,member_watchers.watcher'
73
+ }
74
+
75
+ response_data = self.sdk._make_request('GET', endpoint, params=params)
76
+
77
+ # Return the structured response with pagination
78
+ return TasksList.from_dict(response_data)
79
+
80
+ except TallyfyError:
81
+ raise
82
+ except Exception as e:
83
+ self._handle_api_error(e, "get user tasks", org_id=org_id, user_id=user_id)
84
+
85
+ def get_tasks_for_process(self, org_id: str, process_id: Optional[str] = None, process_name: Optional[str] = None,
86
+ status: Optional[str] = None, sort: Optional[str] = None, with_: Optional[str] = None,
87
+ owners: Optional[str] = None, groups: Optional[str] = None, guests: Optional[str] = None,
88
+ page: int = 1, per_page: int = 100, current_task: Optional[str] = None,
89
+ replace_page: Optional[int] = None, without_pagination: str = "false",
90
+ deadline_start_range: Optional[str] = None, deadline_end_range: Optional[str] = None,
91
+ unassigned: Optional[bool] = None) -> TasksList:
92
+ """
93
+ Get all tasks for a given process (run).
94
+
95
+ Args:
96
+ org_id: Organization ID
97
+ process_id: Process (run) ID to get tasks for
98
+ process_name: Process (run) name to get tasks for (alternative to process_id)
99
+ status: Filter by task status (complete, hasproblem, overdue, due_soon, active, active_visible, incomplete, inprogress, not-started)
100
+ sort: Sort by position or deadline (position, deadline, -position, -deadline)
101
+ with_: Additional data to retrieve (activities, run, run.checklist, step, form_fields, threads, comments, assets, summary)
102
+ owners: Search tasks assigned to specific members (comma-separated member IDs)
103
+ groups: Filter by Group ID
104
+ guests: Search tasks assigned to specific group (comma-separated group IDs)
105
+ page: Results page to retrieve (default: 1)
106
+ per_page: Tasks per page (default: 100)
107
+ current_task: Task ID
108
+ replace_page: Replace page
109
+ without_pagination: Results without pagination (true/false, default: false)
110
+ deadline_start_range: Deadline range starting date
111
+ deadline_end_range: Deadline range ending date
112
+ unassigned: Filter tasks with nobody assigned
113
+
114
+ Returns:
115
+ TasksList object containing tasks and pagination metadata (total count, etc.)
116
+
117
+ Raises:
118
+ TallyfyError: If the request fails
119
+ ValueError: If neither process_id nor process_name is provided
120
+ """
121
+ self._validate_org_id(org_id)
122
+
123
+ if not process_id and not process_name:
124
+ raise ValueError("Either process_id or process_name must be provided")
125
+
126
+ try:
127
+ # If process_name is provided but not process_id, search for the process first
128
+ if process_name and not process_id:
129
+ # We need to import TaskSearch here to avoid circular imports
130
+ from .search import TaskSearch
131
+ search = TaskSearch(self.sdk)
132
+ process_id = search.search_processes_by_name(org_id, process_name)
133
+
134
+ self._validate_process_id(process_id)
135
+
136
+ endpoint = f"organizations/{org_id}/runs/{process_id}/tasks"
137
+
138
+ # Build parameters using base class helper
139
+ params = self._build_query_params(
140
+ status=status,
141
+ sort=sort,
142
+ owners=owners,
143
+ groups=groups,
144
+ guests=guests,
145
+ page=page,
146
+ per_page=per_page,
147
+ currentTask=current_task,
148
+ replace_page=replace_page,
149
+ without_pagination=without_pagination,
150
+ deadline_start_range=deadline_start_range,
151
+ deadline_end_range=deadline_end_range,
152
+ unassigned=unassigned
153
+ )
154
+
155
+ # Handle the 'with' parameter specially due to Python keyword conflict
156
+ if with_:
157
+ params['with'] = with_
158
+
159
+ response_data = self.sdk._make_request('GET', endpoint, params=params)
160
+
161
+ # Return the structured response with pagination
162
+ return TasksList.from_dict(response_data)
163
+
164
+ except TallyfyError:
165
+ raise
166
+ except Exception as e:
167
+ self._handle_api_error(e, "get tasks for process", org_id=org_id, process_id=process_id, process_name=process_name)
168
+
169
+ def get_organization_runs(self, org_id: str, per_page: int = 100, page: int = 1, with_data: Optional[str] = None,
170
+ form_fields_values: Optional[bool] = None,
171
+ owners: Optional[str] = None, task_status: Optional[str] = None,
172
+ groups: Optional[str] = None, status: Optional[str] = None,
173
+ folder: Optional[str] = None, checklist_id: Optional[str] = None,
174
+ starred: Optional[bool] = None, run_type: Optional[str] = None,
175
+ tag: Optional[str] = None, sort: Optional[str] = None) -> RunsList:
176
+ """
177
+ Get all processes (runs) in the organization.
178
+
179
+ Args:
180
+ org_id: Organization ID
181
+ per_page: Results per page (default: 100)
182
+ page: Results page to retrieve (default: 1)
183
+ with_data: Comma-separated data to include (e.g., 'checklist,tasks,assets,tags')
184
+ form_fields_values: Include form field values
185
+ owners: Filter by specific member IDs
186
+ task_status: Filter by task status ('all', 'in-progress', 'completed')
187
+ groups: Filter by group IDs
188
+ status: Filter by process status ('active', 'problem', 'delayed', 'complete', 'archived')
189
+ folder: Filter by folder ID
190
+ checklist_id: Filter by template ID
191
+ starred: Filter by starred status
192
+ run_type: Filter by type ('procedure', 'form', 'document')
193
+ tag: Filter by tag ID
194
+ sort: Sort by creation date ('created_at', '-created_at')
195
+
196
+ Returns:
197
+ RunsList object containing runs and pagination metadata (total count, etc.)
198
+
199
+ Raises:
200
+ TallyfyError: If the request fails
201
+ """
202
+ self._validate_org_id(org_id)
203
+
204
+ try:
205
+ endpoint = f"organizations/{org_id}/runs"
206
+
207
+ # Build parameters using base class helper
208
+ params = self._build_query_params(
209
+ with_=with_data, # Use with_ to avoid Python keyword conflict
210
+ form_fields_values=form_fields_values,
211
+ owners=owners,
212
+ task_status=task_status,
213
+ groups=groups,
214
+ status=status,
215
+ folder=folder,
216
+ checklist_id=checklist_id,
217
+ starred=starred,
218
+ type=run_type, # API expects 'type' parameter
219
+ tag=tag,
220
+ sort=sort,
221
+ per_page=per_page,
222
+ page=page
223
+ )
224
+
225
+ # Handle the 'with' parameter specially due to Python keyword conflict
226
+ if with_data:
227
+ params['with'] = with_data
228
+ if 'with_' in params:
229
+ del params['with_']
230
+
231
+ response_data = self.sdk._make_request('GET', endpoint, params=params)
232
+
233
+ # Return the structured response with pagination
234
+ return RunsList.from_dict(response_data)
235
+
236
+ except TallyfyError:
237
+ raise
238
+ except Exception as e:
239
+ self._handle_api_error(e, "get organization runs", org_id=org_id)
240
+
241
+ def get_organization_processes(self, org_id: str, **kwargs) -> RunsList:
242
+ """
243
+ Alias for get_organization_runs for better naming consistency.
244
+
245
+ Args:
246
+ org_id: Organization ID
247
+ **kwargs: Same parameters as get_organization_runs
248
+
249
+ Returns:
250
+ RunsList object containing runs and pagination metadata (total count, etc.)
251
+ """
252
+ return self.get_organization_runs(org_id, **kwargs)
@@ -0,0 +1,198 @@
1
+ """
2
+ Task and process search operations
3
+ """
4
+
5
+ from typing import List
6
+ from .base import TaskManagerBase
7
+ from ..models import SearchResult, SearchResultsList, TallyfyError
8
+
9
+
10
+ class TaskSearch(TaskManagerBase):
11
+ """Handles task and process search operations"""
12
+
13
+ def search_processes_by_name(self, org_id: str, process_name: str) -> str:
14
+ """
15
+ Search for processes by name using the search endpoint.
16
+
17
+ Args:
18
+ org_id: Organization ID
19
+ process_name: Name or partial name of the process to search for
20
+
21
+ Returns:
22
+ Process ID of the found process
23
+
24
+ Raises:
25
+ TallyfyError: If no process found, multiple matches, or search fails
26
+ """
27
+ self._validate_org_id(org_id)
28
+
29
+ if not process_name or not isinstance(process_name, str):
30
+ raise ValueError("Process name must be a non-empty string")
31
+
32
+ try:
33
+ search_endpoint = f"organizations/{org_id}/search"
34
+ search_params = {
35
+ 'on': 'process',
36
+ 'per_page': '20',
37
+ 'search': process_name
38
+ }
39
+
40
+ search_response = self.sdk._make_request('GET', search_endpoint, params=search_params)
41
+
42
+ if isinstance(search_response, dict) and 'process' in search_response:
43
+ process_data = search_response['process']
44
+ if 'data' in process_data and process_data['data']:
45
+ processes = process_data['data']
46
+
47
+ # First try exact match (case-insensitive)
48
+ exact_matches = [p for p in processes if p['name'].lower() == process_name.lower()]
49
+ if exact_matches:
50
+ return exact_matches[0]['id']
51
+ elif len(processes) == 1:
52
+ # Single search result, use it
53
+ return processes[0]['id']
54
+ else:
55
+ # Multiple matches found, provide helpful error with options
56
+ match_names = [f"'{p['name']}'" for p in processes[:5]] # Show max 5
57
+ raise TallyfyError(f"Multiple processes found matching '{process_name}': {', '.join(match_names)}. Please be more specific.")
58
+ else:
59
+ raise TallyfyError(f"No process found matching name: {process_name}")
60
+ else:
61
+ raise TallyfyError(f"Search failed for process name: {process_name}")
62
+
63
+ except TallyfyError:
64
+ raise
65
+ except Exception as e:
66
+ self._handle_api_error(e, "search processes by name", org_id=org_id, process_name=process_name)
67
+
68
+ def search(self, org_id: str, search_query: str, search_type: str = "process",
69
+ page: int = 1, per_page: int = 20) -> SearchResultsList:
70
+ """
71
+ Search for processes, templates, or tasks in the organization.
72
+
73
+ Args:
74
+ org_id: Organization ID
75
+ search_query: Text to search for
76
+ search_type: Type of search - 'process', 'blueprint', or 'task' (default: 'process'). blueprint equals template
77
+ page: Page number (default: 1)
78
+ per_page: Number of results per page (default: 20)
79
+
80
+ Returns:
81
+ SearchResultsList object containing results and pagination metadata (total count, etc.)
82
+
83
+ Raises:
84
+ TallyfyError: If the request fails
85
+ ValueError: If search_type is not valid
86
+ """
87
+ self._validate_org_id(org_id)
88
+
89
+ if not search_query or not isinstance(search_query, str):
90
+ raise ValueError("Search query must be a non-empty string")
91
+
92
+ # Validate search type
93
+ valid_types = ["process", "blueprint", "task"]
94
+ if search_type not in valid_types:
95
+ raise ValueError(f"Search type must be one of: {', '.join(valid_types)}")
96
+
97
+ if per_page <= 0 or per_page > 100:
98
+ raise ValueError("per_page must be between 1 and 100")
99
+
100
+ try:
101
+ endpoint = f"organizations/{org_id}/search"
102
+ params = {
103
+ 'on': search_type,
104
+ 'page': str(page),
105
+ 'per_page': str(per_page),
106
+ 'search': search_query
107
+ }
108
+
109
+ response_data = self.sdk._make_request('GET', endpoint, params=params)
110
+
111
+ # Return the structured response with pagination
112
+ return SearchResultsList.from_dict(response_data, search_type)
113
+
114
+ except TallyfyError:
115
+ raise
116
+ except Exception as e:
117
+ self._handle_api_error(e, "search", org_id=org_id, search_query=search_query, search_type=search_type)
118
+
119
+ def search_processes(self, org_id: str, search_query: str, page: int = 1, per_page: int = 20) -> SearchResultsList:
120
+ """
121
+ Search for processes in the organization.
122
+
123
+ Args:
124
+ org_id: Organization ID
125
+ search_query: Text to search for
126
+ page: Page number (default: 1)
127
+ per_page: Number of results per page (default: 20)
128
+
129
+ Returns:
130
+ SearchResultsList object containing results and pagination metadata (total count, etc.)
131
+
132
+ Raises:
133
+ TallyfyError: If the request fails
134
+ """
135
+ return self.search(org_id, search_query, "process", page, per_page)
136
+
137
+ def search_templates(self, org_id: str, search_query: str, page: int = 1, per_page: int = 20) -> SearchResultsList:
138
+ """
139
+ Search for templates (blueprints) in the organization.
140
+
141
+ Args:
142
+ org_id: Organization ID
143
+ search_query: Text to search for
144
+ page: Page number (default: 1)
145
+ per_page: Number of results per page (default: 20)
146
+
147
+ Returns:
148
+ SearchResultsList object containing results and pagination metadata (total count, etc.)
149
+
150
+ Raises:
151
+ TallyfyError: If the request fails
152
+ """
153
+ return self.search(org_id, search_query, "blueprint", page, per_page)
154
+
155
+ def search_tasks(self, org_id: str, search_query: str, page: int = 1, per_page: int = 20) -> SearchResultsList:
156
+ """
157
+ Search for tasks in the organization.
158
+
159
+ Args:
160
+ org_id: Organization ID
161
+ search_query: Text to search for
162
+ page: Page number (default: 1)
163
+ per_page: Number of results per page (default: 20)
164
+
165
+ Returns:
166
+ SearchResultsList object containing results and pagination metadata (total count, etc.)
167
+
168
+ Raises:
169
+ TallyfyError: If the request fails
170
+ """
171
+ return self.search(org_id, search_query, "task", page, per_page)
172
+
173
+ def find_process_by_name(self, org_id: str, process_name: str, exact_match: bool = True) -> List[SearchResult]:
174
+ """
175
+ Find processes by name with flexible matching options.
176
+
177
+ Args:
178
+ org_id: Organization ID
179
+ process_name: Name of the process to search for
180
+ exact_match: If True, only return exact matches (case-insensitive)
181
+
182
+ Returns:
183
+ List of SearchResult objects matching the criteria
184
+
185
+ Raises:
186
+ TallyfyError: If the request fails
187
+ """
188
+ results_list = self.search_processes(org_id, process_name)
189
+
190
+ if exact_match:
191
+ # Filter for exact matches (case-insensitive)
192
+ exact_results = []
193
+ for result in results_list.data:
194
+ if hasattr(result, 'name') and result.name.lower() == process_name.lower():
195
+ exact_results.append(result)
196
+ return exact_results
197
+
198
+ return results_list.data
@@ -0,0 +1,85 @@
1
+ """
2
+ Template Management Package
3
+
4
+ This package provides a refactored, modular approach to template management
5
+ functionality, breaking down the monolithic TemplateManagement class into
6
+ specialized components for better maintainability and separation of concerns.
7
+
8
+ Classes:
9
+ TemplateBasicOperations: CRUD operations for templates
10
+ TemplateAnalysis: Analysis and insights for templates
11
+ TemplateAutomation: Automation rule management
12
+ TemplateHealthAssessment: Comprehensive template health checks
13
+ TemplateManager: Unified interface combining all functionality
14
+ """
15
+
16
+ from .base import TemplateManagerBase
17
+ from .basic_operations import TemplateBasicOperations
18
+ from .analysis import TemplateAnalysis
19
+ from .automation import TemplateAutomation
20
+ from .health_assessment import TemplateHealthAssessment
21
+
22
+
23
+ class TemplateManager:
24
+ """
25
+ Unified interface for template management functionality.
26
+
27
+ This class provides access to all template management capabilities
28
+ through a single interface while maintaining the modular structure
29
+ underneath.
30
+ """
31
+
32
+ def __init__(self, sdk):
33
+ """
34
+ Initialize template manager with SDK instance.
35
+
36
+ Args:
37
+ sdk: Main SDK instance
38
+ """
39
+ self.basic_operations = TemplateBasicOperations(sdk)
40
+ self.analysis = TemplateAnalysis(sdk)
41
+ self.automation = TemplateAutomation(sdk)
42
+ self.health_assessment = TemplateHealthAssessment(sdk)
43
+
44
+ # For backward compatibility, expose common methods at the top level
45
+ self.search_templates_by_name = self.basic_operations.search_templates_by_name
46
+ self.get_template = self.basic_operations.get_template
47
+ self.get_all_templates = self.basic_operations.get_all_templates
48
+ self.update_template_metadata = self.basic_operations.update_template_metadata
49
+ self.get_template_with_steps = self.basic_operations.get_template_with_steps
50
+ self.duplicate_template = self.basic_operations.duplicate_template
51
+ self.get_template_steps = self.basic_operations.get_template_steps
52
+ self.edit_description_on_step = self.basic_operations.edit_description_on_step
53
+ self.add_step_to_template = self.basic_operations.add_step_to_template
54
+
55
+ # Analysis methods
56
+ self.get_step_dependencies = self.analysis.get_step_dependencies
57
+ self.suggest_step_deadline = self.analysis.suggest_step_deadline
58
+ self.get_step_visibility_conditions = self.analysis.get_step_visibility_conditions
59
+ self.suggest_kickoff_fields = self.analysis.suggest_kickoff_fields
60
+ self.suggest_automation_consolidation = self.analysis.suggest_automation_consolidation
61
+
62
+ # Automation methods
63
+ self.create_automation_rule = self.automation.create_automation_rule
64
+ self.update_automation_rule = self.automation.update_automation_rule
65
+ self.delete_automation_rule = self.automation.delete_automation_rule
66
+ self.consolidate_automation_rules = self.automation.consolidate_automation_rules
67
+ self.add_assignees_to_step = self.automation.add_assignees_to_step
68
+ self.analyze_template_automations = self.automation.analyze_template_automations
69
+
70
+ # Health assessment method
71
+ self.assess_template_health = self.health_assessment.assess_template_health
72
+
73
+
74
+ # For backward compatibility, create an alias
75
+ TemplateManagement = TemplateManager
76
+
77
+ __all__ = [
78
+ 'TemplateManagerBase',
79
+ 'TemplateBasicOperations',
80
+ 'TemplateAnalysis',
81
+ 'TemplateAutomation',
82
+ 'TemplateHealthAssessment',
83
+ 'TemplateManager',
84
+ 'TemplateManagement' # Backward compatibility alias
85
+ ]