tallyfy 1.0.4__py3-none-any.whl → 1.0.6__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.
Potentially problematic release.
This version of tallyfy might be problematic. Click here for more details.
- tallyfy/__init__.py +8 -4
- tallyfy/core.py +11 -8
- tallyfy/form_fields_management/__init__.py +70 -0
- tallyfy/form_fields_management/base.py +109 -0
- tallyfy/form_fields_management/crud_operations.py +234 -0
- tallyfy/form_fields_management/options_management.py +222 -0
- tallyfy/form_fields_management/suggestions.py +411 -0
- tallyfy/models.py +3 -1
- tallyfy/task_management/__init__.py +81 -0
- tallyfy/task_management/base.py +125 -0
- tallyfy/task_management/creation.py +221 -0
- tallyfy/task_management/retrieval.py +211 -0
- tallyfy/task_management/search.py +196 -0
- tallyfy/template_management/__init__.py +85 -0
- tallyfy/template_management/analysis.py +1093 -0
- tallyfy/template_management/automation.py +469 -0
- tallyfy/template_management/base.py +56 -0
- tallyfy/template_management/basic_operations.py +477 -0
- tallyfy/template_management/health_assessment.py +763 -0
- tallyfy/user_management/__init__.py +69 -0
- tallyfy/user_management/base.py +146 -0
- tallyfy/user_management/invitation.py +286 -0
- tallyfy/user_management/retrieval.py +339 -0
- {tallyfy-1.0.4.dist-info → tallyfy-1.0.6.dist-info}/METADATA +120 -56
- tallyfy-1.0.6.dist-info/RECORD +28 -0
- tallyfy/BUILD.md +0 -5
- tallyfy/form_fields_management.py +0 -582
- tallyfy/task_management.py +0 -356
- tallyfy/template_management.py +0 -2607
- tallyfy/user_management.py +0 -235
- tallyfy-1.0.4.dist-info/RECORD +0 -13
- {tallyfy-1.0.4.dist-info → tallyfy-1.0.6.dist-info}/WHEEL +0 -0
- {tallyfy-1.0.4.dist-info → tallyfy-1.0.6.dist-info}/licenses/LICENSE +0 -0
- {tallyfy-1.0.4.dist-info → tallyfy-1.0.6.dist-info}/top_level.txt +0 -0
tallyfy/__init__.py
CHANGED
|
@@ -4,17 +4,21 @@ Tallyfy SDK - A modular Python SDK for Tallyfy API
|
|
|
4
4
|
|
|
5
5
|
from .core import TallyfySDK, TallyfyError
|
|
6
6
|
from .models import *
|
|
7
|
-
from .user_management import UserManagement
|
|
8
|
-
from .task_management import TaskManagement
|
|
9
|
-
from .template_management import TemplateManagement
|
|
10
|
-
from .form_fields_management import FormFieldManagement
|
|
7
|
+
from .user_management import UserManager, UserManagement
|
|
8
|
+
from .task_management import TaskManager, TaskManagement
|
|
9
|
+
from .template_management import TemplateManager, TemplateManagement
|
|
10
|
+
from .form_fields_management import FormFieldManager, FormFieldManagement
|
|
11
11
|
|
|
12
12
|
__version__ = "1.0.0"
|
|
13
13
|
__all__ = [
|
|
14
14
|
"TallyfySDK",
|
|
15
15
|
"TallyfyError",
|
|
16
|
+
"UserManager",
|
|
16
17
|
"UserManagement",
|
|
18
|
+
"TaskManager",
|
|
17
19
|
"TaskManagement",
|
|
20
|
+
"TemplateManager",
|
|
18
21
|
"TemplateManagement",
|
|
22
|
+
"FormFieldManager",
|
|
19
23
|
"FormFieldManagement"
|
|
20
24
|
]
|
tallyfy/core.py
CHANGED
|
@@ -148,16 +148,19 @@ class TallyfySDK(BaseSDK):
|
|
|
148
148
|
super().__init__(api_key, base_url, timeout, max_retries, retry_delay)
|
|
149
149
|
|
|
150
150
|
# Initialize management modules
|
|
151
|
-
from .user_management import
|
|
152
|
-
from .task_management import
|
|
153
|
-
from .template_management import
|
|
154
|
-
from .form_fields_management import
|
|
151
|
+
from .user_management import UserManager
|
|
152
|
+
from .task_management import TaskManager
|
|
153
|
+
from .template_management import TemplateManager
|
|
154
|
+
from .form_fields_management import FormFieldManager
|
|
155
155
|
|
|
156
|
-
self.users =
|
|
157
|
-
self.tasks =
|
|
158
|
-
self.templates =
|
|
159
|
-
self.form_fields =
|
|
156
|
+
self.users = UserManager(self)
|
|
157
|
+
self.tasks = TaskManager(self)
|
|
158
|
+
self.templates = TemplateManager(self)
|
|
159
|
+
self.form_fields = FormFieldManager(self)
|
|
160
160
|
|
|
161
|
+
def get_current_user_info(self, org_id: str):
|
|
162
|
+
"""Get current user information."""
|
|
163
|
+
return self.users.get_current_user_info(org_id)
|
|
161
164
|
# Backward compatibility methods - delegate to management modules
|
|
162
165
|
def get_organization_users(self, org_id: str, with_groups: bool = False):
|
|
163
166
|
"""Get all organization members with full profile data."""
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Form Fields Management Package
|
|
3
|
+
|
|
4
|
+
This package provides a refactored, modular approach to form field management
|
|
5
|
+
functionality, breaking down the monolithic FormFieldManagement class into
|
|
6
|
+
specialized components for better maintainability and separation of concerns.
|
|
7
|
+
|
|
8
|
+
Classes:
|
|
9
|
+
FormFieldCRUD: Basic CRUD operations for form fields
|
|
10
|
+
FormFieldOptions: Dropdown options management
|
|
11
|
+
FormFieldSuggestions: AI-powered form field suggestions
|
|
12
|
+
FormFieldManager: Unified interface combining all functionality
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from .base import FormFieldManagerBase
|
|
16
|
+
from .crud_operations import FormFieldCRUD
|
|
17
|
+
from .options_management import FormFieldOptions
|
|
18
|
+
from .suggestions import FormFieldSuggestions
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class FormFieldManager:
|
|
22
|
+
"""
|
|
23
|
+
Unified interface for form field management functionality.
|
|
24
|
+
|
|
25
|
+
This class provides access to all form field management capabilities
|
|
26
|
+
through a single interface while maintaining the modular structure
|
|
27
|
+
underneath.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, sdk):
|
|
31
|
+
"""
|
|
32
|
+
Initialize form field manager with SDK instance.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
sdk: Main SDK instance
|
|
36
|
+
"""
|
|
37
|
+
self.crud = FormFieldCRUD(sdk)
|
|
38
|
+
self.options = FormFieldOptions(sdk)
|
|
39
|
+
self.suggestions = FormFieldSuggestions(sdk)
|
|
40
|
+
|
|
41
|
+
# For backward compatibility, expose common methods at the top level
|
|
42
|
+
self.add_form_field_to_step = self.crud.add_form_field_to_step
|
|
43
|
+
self.update_form_field = self.crud.update_form_field
|
|
44
|
+
self.move_form_field = self.crud.move_form_field
|
|
45
|
+
self.delete_form_field = self.crud.delete_form_field
|
|
46
|
+
self.get_step = self.crud.get_step
|
|
47
|
+
|
|
48
|
+
# Options management methods
|
|
49
|
+
self.get_dropdown_options = self.options.get_dropdown_options
|
|
50
|
+
self.update_dropdown_options = self.options.update_dropdown_options
|
|
51
|
+
self.add_dropdown_option = self.options.add_dropdown_option
|
|
52
|
+
self.remove_dropdown_option = self.options.remove_dropdown_option
|
|
53
|
+
self.reorder_dropdown_options = self.options.reorder_dropdown_options
|
|
54
|
+
|
|
55
|
+
# Suggestions methods
|
|
56
|
+
self.suggest_form_fields_for_step = self.suggestions.suggest_form_fields_for_step
|
|
57
|
+
self.suggest_field_improvements = self.suggestions.suggest_field_improvements
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# For backward compatibility, create an alias
|
|
61
|
+
FormFieldManagement = FormFieldManager
|
|
62
|
+
|
|
63
|
+
__all__ = [
|
|
64
|
+
'FormFieldManagerBase',
|
|
65
|
+
'FormFieldCRUD',
|
|
66
|
+
'FormFieldOptions',
|
|
67
|
+
'FormFieldSuggestions',
|
|
68
|
+
'FormFieldManager',
|
|
69
|
+
'FormFieldManagement' # Backward compatibility alias
|
|
70
|
+
]
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base class for form field management operations
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
from ..models import TallyfyError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FormFieldManagerBase:
|
|
10
|
+
"""Base class providing common functionality for form field management"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, sdk):
|
|
13
|
+
"""
|
|
14
|
+
Initialize base form field manager.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
sdk: Main SDK instance
|
|
18
|
+
"""
|
|
19
|
+
self.sdk = sdk
|
|
20
|
+
|
|
21
|
+
def _validate_org_id(self, org_id: str) -> None:
|
|
22
|
+
"""
|
|
23
|
+
Validate organization ID parameter.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
org_id: Organization ID to validate
|
|
27
|
+
|
|
28
|
+
Raises:
|
|
29
|
+
ValueError: If org_id is invalid
|
|
30
|
+
"""
|
|
31
|
+
if not org_id or not isinstance(org_id, str):
|
|
32
|
+
raise ValueError("Organization ID must be a non-empty string")
|
|
33
|
+
|
|
34
|
+
def _validate_template_id(self, template_id: str) -> None:
|
|
35
|
+
"""
|
|
36
|
+
Validate template ID parameter.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
template_id: Template ID to validate
|
|
40
|
+
|
|
41
|
+
Raises:
|
|
42
|
+
ValueError: If template_id is invalid
|
|
43
|
+
"""
|
|
44
|
+
if not template_id or not isinstance(template_id, str):
|
|
45
|
+
raise ValueError("Template ID must be a non-empty string")
|
|
46
|
+
|
|
47
|
+
def _validate_step_id(self, step_id: str) -> None:
|
|
48
|
+
"""
|
|
49
|
+
Validate step ID parameter.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
step_id: Step ID to validate
|
|
53
|
+
|
|
54
|
+
Raises:
|
|
55
|
+
ValueError: If step_id is invalid
|
|
56
|
+
"""
|
|
57
|
+
if not step_id or not isinstance(step_id, str):
|
|
58
|
+
raise ValueError("Step ID must be a non-empty string")
|
|
59
|
+
|
|
60
|
+
def _validate_field_id(self, field_id: str) -> None:
|
|
61
|
+
"""
|
|
62
|
+
Validate field ID parameter.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
field_id: Field ID to validate
|
|
66
|
+
|
|
67
|
+
Raises:
|
|
68
|
+
ValueError: If field_id is invalid
|
|
69
|
+
"""
|
|
70
|
+
if not field_id or not isinstance(field_id, str):
|
|
71
|
+
raise ValueError("Field ID must be a non-empty string")
|
|
72
|
+
|
|
73
|
+
def _extract_data(self, response_data) -> Optional[dict]:
|
|
74
|
+
"""
|
|
75
|
+
Extract data from API response.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
response_data: Raw response from API
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Extracted data dictionary or None
|
|
82
|
+
"""
|
|
83
|
+
if isinstance(response_data, dict):
|
|
84
|
+
if 'data' in response_data:
|
|
85
|
+
return response_data['data']
|
|
86
|
+
return response_data
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
def _handle_api_error(self, error: Exception, operation: str, **context) -> None:
|
|
90
|
+
"""
|
|
91
|
+
Handle API errors with context.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
error: The exception that occurred
|
|
95
|
+
operation: Description of the operation that failed
|
|
96
|
+
**context: Additional context for error logging
|
|
97
|
+
"""
|
|
98
|
+
context_str = ", ".join([f"{k}={v}" for k, v in context.items()])
|
|
99
|
+
error_msg = f"Failed to {operation}"
|
|
100
|
+
if context_str:
|
|
101
|
+
error_msg += f" ({context_str})"
|
|
102
|
+
error_msg += f": {error}"
|
|
103
|
+
|
|
104
|
+
self.sdk.logger.error(error_msg)
|
|
105
|
+
|
|
106
|
+
if isinstance(error, TallyfyError):
|
|
107
|
+
raise error
|
|
108
|
+
else:
|
|
109
|
+
raise TallyfyError(error_msg)
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CRUD operations for form field management
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict, Any, Optional
|
|
6
|
+
from .base import FormFieldManagerBase
|
|
7
|
+
from ..models import Capture, Step, TallyfyError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FormFieldCRUD(FormFieldManagerBase):
|
|
11
|
+
"""Handles basic CRUD operations for form fields"""
|
|
12
|
+
|
|
13
|
+
def add_form_field_to_step(self, org_id: str, template_id: str, step_id: str, field_data: Dict[str, Any]) -> Optional[Capture]:
|
|
14
|
+
"""
|
|
15
|
+
Add form fields (text, dropdown, date, etc.) to a step.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
org_id: Organization ID
|
|
19
|
+
template_id: Template ID
|
|
20
|
+
step_id: Step ID
|
|
21
|
+
field_data: Form field creation data including field_type, label, required, etc.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Created Capture object or None if creation failed
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
TallyfyError: If the request fails
|
|
28
|
+
"""
|
|
29
|
+
self._validate_org_id(org_id)
|
|
30
|
+
self._validate_template_id(template_id)
|
|
31
|
+
self._validate_step_id(step_id)
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
endpoint = f"organizations/{org_id}/checklists/{template_id}/steps/{step_id}/captures"
|
|
35
|
+
|
|
36
|
+
# Validate required fields
|
|
37
|
+
required_fields = ['field_type', 'label']
|
|
38
|
+
for field in required_fields:
|
|
39
|
+
if field not in field_data:
|
|
40
|
+
raise ValueError(f"Missing required field: {field}")
|
|
41
|
+
|
|
42
|
+
# Set defaults for optional fields
|
|
43
|
+
capture_data = {
|
|
44
|
+
'field_type': field_data['field_type'],
|
|
45
|
+
'label': field_data['label'],
|
|
46
|
+
'required': field_data.get('required', True),
|
|
47
|
+
'position': field_data.get('position', 1)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Add optional fields if provided
|
|
51
|
+
optional_fields = ['guidance', 'options', 'default_value', 'default_value_enabled']
|
|
52
|
+
for field in optional_fields:
|
|
53
|
+
if field in field_data:
|
|
54
|
+
capture_data[field] = field_data[field]
|
|
55
|
+
|
|
56
|
+
response_data = self.sdk._make_request('POST', endpoint, data=capture_data)
|
|
57
|
+
|
|
58
|
+
extracted_data = self._extract_data(response_data)
|
|
59
|
+
if extracted_data:
|
|
60
|
+
return Capture.from_dict(extracted_data)
|
|
61
|
+
else:
|
|
62
|
+
self.sdk.logger.warning("Unexpected response format for form field creation")
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
except TallyfyError:
|
|
66
|
+
raise
|
|
67
|
+
except Exception as e:
|
|
68
|
+
self._handle_api_error(e, "add form field to step", org_id=org_id, template_id=template_id, step_id=step_id)
|
|
69
|
+
|
|
70
|
+
def update_form_field(self, org_id: str, template_id: str, step_id: str, field_id: str, **kwargs) -> Optional[Capture]:
|
|
71
|
+
"""
|
|
72
|
+
Update form field properties, validation, options.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
org_id: Organization ID
|
|
76
|
+
template_id: Template ID
|
|
77
|
+
step_id: Step ID
|
|
78
|
+
field_id: Form field ID
|
|
79
|
+
**kwargs: Form field properties to update
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Updated Capture object or None if update failed
|
|
83
|
+
|
|
84
|
+
Raises:
|
|
85
|
+
TallyfyError: If the request fails
|
|
86
|
+
"""
|
|
87
|
+
self._validate_org_id(org_id)
|
|
88
|
+
self._validate_template_id(template_id)
|
|
89
|
+
self._validate_step_id(step_id)
|
|
90
|
+
self._validate_field_id(field_id)
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
endpoint = f"organizations/{org_id}/checklists/{template_id}/steps/{step_id}/captures/{field_id}"
|
|
94
|
+
|
|
95
|
+
# Build update data from kwargs
|
|
96
|
+
update_data = {}
|
|
97
|
+
allowed_fields = [
|
|
98
|
+
'field_type', 'label', 'guidance', 'position', 'required',
|
|
99
|
+
'options', 'default_value', 'default_value_enabled'
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
for field, value in kwargs.items():
|
|
103
|
+
if field in allowed_fields:
|
|
104
|
+
update_data[field] = value
|
|
105
|
+
else:
|
|
106
|
+
self.sdk.logger.warning(f"Ignoring unknown form field: {field}")
|
|
107
|
+
|
|
108
|
+
if not update_data:
|
|
109
|
+
raise ValueError("No valid form field properties provided for update")
|
|
110
|
+
|
|
111
|
+
response_data = self.sdk._make_request('PUT', endpoint, data=update_data)
|
|
112
|
+
|
|
113
|
+
extracted_data = self._extract_data(response_data)
|
|
114
|
+
if extracted_data:
|
|
115
|
+
return Capture.from_dict(extracted_data)
|
|
116
|
+
else:
|
|
117
|
+
self.sdk.logger.warning("Unexpected response format for form field update")
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
except TallyfyError:
|
|
121
|
+
raise
|
|
122
|
+
except Exception as e:
|
|
123
|
+
self._handle_api_error(e, "update form field", org_id=org_id, template_id=template_id, step_id=step_id, field_id=field_id)
|
|
124
|
+
|
|
125
|
+
def move_form_field(self, org_id: str, template_id: str, from_step: str, field_id: str, to_step: str, position: int = 1) -> bool:
|
|
126
|
+
"""
|
|
127
|
+
Move form field between steps.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
org_id: Organization ID
|
|
131
|
+
template_id: Template ID
|
|
132
|
+
from_step: Source step ID
|
|
133
|
+
field_id: Form field ID to move
|
|
134
|
+
to_step: Target step ID
|
|
135
|
+
position: Position in target step (default: 1)
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
True if move was successful
|
|
139
|
+
|
|
140
|
+
Raises:
|
|
141
|
+
TallyfyError: If the request fails
|
|
142
|
+
"""
|
|
143
|
+
self._validate_org_id(org_id)
|
|
144
|
+
self._validate_template_id(template_id)
|
|
145
|
+
self._validate_step_id(from_step)
|
|
146
|
+
self._validate_field_id(field_id)
|
|
147
|
+
self._validate_step_id(to_step)
|
|
148
|
+
|
|
149
|
+
try:
|
|
150
|
+
endpoint = f"organizations/{org_id}/checklists/{template_id}/steps/{from_step}/captures/{field_id}/move"
|
|
151
|
+
|
|
152
|
+
move_data = {
|
|
153
|
+
'to_step_id': to_step,
|
|
154
|
+
'position': position
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
response_data = self.sdk._make_request('POST', endpoint, data=move_data)
|
|
158
|
+
|
|
159
|
+
# Check if move was successful
|
|
160
|
+
return isinstance(response_data, dict) and response_data.get('success', False)
|
|
161
|
+
|
|
162
|
+
except TallyfyError:
|
|
163
|
+
raise
|
|
164
|
+
except Exception as e:
|
|
165
|
+
self._handle_api_error(e, "move form field", org_id=org_id, template_id=template_id, from_step=from_step, field_id=field_id, to_step=to_step)
|
|
166
|
+
|
|
167
|
+
def delete_form_field(self, org_id: str, template_id: str, step_id: str, field_id: str) -> bool:
|
|
168
|
+
"""
|
|
169
|
+
Delete a form field from a step.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
org_id: Organization ID
|
|
173
|
+
template_id: Template ID
|
|
174
|
+
step_id: Step ID
|
|
175
|
+
field_id: Form field ID
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
True if deletion was successful
|
|
179
|
+
|
|
180
|
+
Raises:
|
|
181
|
+
TallyfyError: If the request fails
|
|
182
|
+
"""
|
|
183
|
+
self._validate_org_id(org_id)
|
|
184
|
+
self._validate_template_id(template_id)
|
|
185
|
+
self._validate_step_id(step_id)
|
|
186
|
+
self._validate_field_id(field_id)
|
|
187
|
+
|
|
188
|
+
try:
|
|
189
|
+
endpoint = f"organizations/{org_id}/checklists/{template_id}/steps/{step_id}/captures/{field_id}"
|
|
190
|
+
|
|
191
|
+
response_data = self.sdk._make_request('DELETE', endpoint)
|
|
192
|
+
|
|
193
|
+
# Check if deletion was successful
|
|
194
|
+
return isinstance(response_data, dict) and response_data.get('success', False)
|
|
195
|
+
|
|
196
|
+
except TallyfyError:
|
|
197
|
+
raise
|
|
198
|
+
except Exception as e:
|
|
199
|
+
self._handle_api_error(e, "delete form field", org_id=org_id, template_id=template_id, step_id=step_id, field_id=field_id)
|
|
200
|
+
|
|
201
|
+
def get_step(self, org_id: str, template_id: str, step_id: str) -> Optional[Step]:
|
|
202
|
+
"""
|
|
203
|
+
Get a specific step with its details.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
org_id: Organization ID
|
|
207
|
+
template_id: Template ID
|
|
208
|
+
step_id: Step ID
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
Step object or None if not found
|
|
212
|
+
|
|
213
|
+
Raises:
|
|
214
|
+
TallyfyError: If the request fails
|
|
215
|
+
"""
|
|
216
|
+
self._validate_org_id(org_id)
|
|
217
|
+
self._validate_template_id(template_id)
|
|
218
|
+
self._validate_step_id(step_id)
|
|
219
|
+
|
|
220
|
+
try:
|
|
221
|
+
endpoint = f"organizations/{org_id}/checklists/{template_id}/steps/{step_id}"
|
|
222
|
+
response_data = self.sdk._make_request('GET', endpoint)
|
|
223
|
+
|
|
224
|
+
extracted_data = self._extract_data(response_data)
|
|
225
|
+
if extracted_data:
|
|
226
|
+
return Step.from_dict(extracted_data)
|
|
227
|
+
else:
|
|
228
|
+
self.sdk.logger.warning("Unexpected response format for step")
|
|
229
|
+
return None
|
|
230
|
+
|
|
231
|
+
except TallyfyError:
|
|
232
|
+
raise
|
|
233
|
+
except Exception as e:
|
|
234
|
+
self._handle_api_error(e, "get step", org_id=org_id, template_id=template_id, step_id=step_id)
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Dropdown options management for form fields
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import List, Dict, Any, Optional
|
|
6
|
+
from .base import FormFieldManagerBase
|
|
7
|
+
from ..models import TallyfyError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FormFieldOptions(FormFieldManagerBase):
|
|
11
|
+
"""Handles dropdown options management for form fields"""
|
|
12
|
+
|
|
13
|
+
def get_dropdown_options(self, org_id: str, template_id: str, step_id: str, field_id: str) -> List[str]:
|
|
14
|
+
"""
|
|
15
|
+
Get current dropdown options for analysis.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
org_id: Organization ID
|
|
19
|
+
template_id: Template ID
|
|
20
|
+
step_id: Step ID
|
|
21
|
+
field_id: Form field ID
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
List of dropdown option strings
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
TallyfyError: If the request fails
|
|
28
|
+
"""
|
|
29
|
+
self._validate_org_id(org_id)
|
|
30
|
+
self._validate_template_id(template_id)
|
|
31
|
+
self._validate_step_id(step_id)
|
|
32
|
+
self._validate_field_id(field_id)
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
endpoint = f"organizations/{org_id}/checklists/{template_id}/steps/{step_id}/captures/{field_id}"
|
|
36
|
+
|
|
37
|
+
response_data = self.sdk._make_request('GET', endpoint)
|
|
38
|
+
|
|
39
|
+
extracted_data = self._extract_data(response_data)
|
|
40
|
+
if extracted_data:
|
|
41
|
+
options = extracted_data.get('options', [])
|
|
42
|
+
|
|
43
|
+
# Extract option values/labels
|
|
44
|
+
if isinstance(options, list):
|
|
45
|
+
return [
|
|
46
|
+
opt.get('label', opt.get('value', str(opt))) if isinstance(opt, dict) else str(opt)
|
|
47
|
+
for opt in options
|
|
48
|
+
]
|
|
49
|
+
else:
|
|
50
|
+
return []
|
|
51
|
+
else:
|
|
52
|
+
self.sdk.logger.warning("Unexpected response format for form field options")
|
|
53
|
+
return []
|
|
54
|
+
|
|
55
|
+
except TallyfyError:
|
|
56
|
+
raise
|
|
57
|
+
except Exception as e:
|
|
58
|
+
self._handle_api_error(e, "get dropdown options", org_id=org_id, template_id=template_id, step_id=step_id, field_id=field_id)
|
|
59
|
+
|
|
60
|
+
def update_dropdown_options(self, org_id: str, template_id: str, step_id: str, field_id: str, options: List[str]) -> bool:
|
|
61
|
+
"""
|
|
62
|
+
Update dropdown options (for external data integration).
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
org_id: Organization ID
|
|
66
|
+
template_id: Template ID
|
|
67
|
+
step_id: Step ID
|
|
68
|
+
field_id: Form field ID
|
|
69
|
+
options: List of new option strings
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
True if update was successful
|
|
73
|
+
|
|
74
|
+
Raises:
|
|
75
|
+
TallyfyError: If the request fails
|
|
76
|
+
"""
|
|
77
|
+
self._validate_org_id(org_id)
|
|
78
|
+
self._validate_template_id(template_id)
|
|
79
|
+
self._validate_step_id(step_id)
|
|
80
|
+
self._validate_field_id(field_id)
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
# Format options for API
|
|
84
|
+
formatted_options = []
|
|
85
|
+
for i, option in enumerate(options):
|
|
86
|
+
if isinstance(option, str):
|
|
87
|
+
formatted_options.append({
|
|
88
|
+
'value': option.lower().replace(' ', '_'),
|
|
89
|
+
'label': option,
|
|
90
|
+
'position': i + 1
|
|
91
|
+
})
|
|
92
|
+
elif isinstance(option, dict):
|
|
93
|
+
formatted_options.append(option)
|
|
94
|
+
else:
|
|
95
|
+
formatted_options.append({
|
|
96
|
+
'value': str(option),
|
|
97
|
+
'label': str(option),
|
|
98
|
+
'position': i + 1
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
# Update the field with new options using CRUD operations
|
|
102
|
+
# We need to import FormFieldCRUD here to avoid circular imports
|
|
103
|
+
from .crud_operations import FormFieldCRUD
|
|
104
|
+
crud = FormFieldCRUD(self.sdk)
|
|
105
|
+
|
|
106
|
+
updated_capture = crud.update_form_field(
|
|
107
|
+
org_id, template_id, step_id, field_id,
|
|
108
|
+
options=formatted_options
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
return updated_capture is not None
|
|
112
|
+
|
|
113
|
+
except TallyfyError:
|
|
114
|
+
raise
|
|
115
|
+
except Exception as e:
|
|
116
|
+
self._handle_api_error(e, "update dropdown options", org_id=org_id, template_id=template_id, step_id=step_id, field_id=field_id)
|
|
117
|
+
|
|
118
|
+
def add_dropdown_option(self, org_id: str, template_id: str, step_id: str, field_id: str, new_option: str, position: Optional[int] = None) -> bool:
|
|
119
|
+
"""
|
|
120
|
+
Add a single new option to an existing dropdown field.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
org_id: Organization ID
|
|
124
|
+
template_id: Template ID
|
|
125
|
+
step_id: Step ID
|
|
126
|
+
field_id: Form field ID
|
|
127
|
+
new_option: New option to add
|
|
128
|
+
position: Position to insert the option (None = append to end)
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
True if addition was successful
|
|
132
|
+
|
|
133
|
+
Raises:
|
|
134
|
+
TallyfyError: If the request fails
|
|
135
|
+
"""
|
|
136
|
+
try:
|
|
137
|
+
# Get current options
|
|
138
|
+
current_options = self.get_dropdown_options(org_id, template_id, step_id, field_id)
|
|
139
|
+
|
|
140
|
+
# Add new option at specified position or end
|
|
141
|
+
if position is not None and 0 <= position <= len(current_options):
|
|
142
|
+
current_options.insert(position, new_option)
|
|
143
|
+
else:
|
|
144
|
+
current_options.append(new_option)
|
|
145
|
+
|
|
146
|
+
# Update with new options list
|
|
147
|
+
return self.update_dropdown_options(org_id, template_id, step_id, field_id, current_options)
|
|
148
|
+
|
|
149
|
+
except TallyfyError:
|
|
150
|
+
raise
|
|
151
|
+
except Exception as e:
|
|
152
|
+
self._handle_api_error(e, "add dropdown option", org_id=org_id, template_id=template_id, step_id=step_id, field_id=field_id)
|
|
153
|
+
|
|
154
|
+
def remove_dropdown_option(self, org_id: str, template_id: str, step_id: str, field_id: str, option_to_remove: str) -> bool:
|
|
155
|
+
"""
|
|
156
|
+
Remove a specific option from a dropdown field.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
org_id: Organization ID
|
|
160
|
+
template_id: Template ID
|
|
161
|
+
step_id: Step ID
|
|
162
|
+
field_id: Form field ID
|
|
163
|
+
option_to_remove: Option to remove
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
True if removal was successful
|
|
167
|
+
|
|
168
|
+
Raises:
|
|
169
|
+
TallyfyError: If the request fails
|
|
170
|
+
"""
|
|
171
|
+
try:
|
|
172
|
+
# Get current options
|
|
173
|
+
current_options = self.get_dropdown_options(org_id, template_id, step_id, field_id)
|
|
174
|
+
|
|
175
|
+
# Remove the specified option if it exists
|
|
176
|
+
if option_to_remove in current_options:
|
|
177
|
+
current_options.remove(option_to_remove)
|
|
178
|
+
|
|
179
|
+
# Update with modified options list
|
|
180
|
+
return self.update_dropdown_options(org_id, template_id, step_id, field_id, current_options)
|
|
181
|
+
else:
|
|
182
|
+
self.sdk.logger.warning(f"Option '{option_to_remove}' not found in dropdown field {field_id}")
|
|
183
|
+
return False
|
|
184
|
+
|
|
185
|
+
except TallyfyError:
|
|
186
|
+
raise
|
|
187
|
+
except Exception as e:
|
|
188
|
+
self._handle_api_error(e, "remove dropdown option", org_id=org_id, template_id=template_id, step_id=step_id, field_id=field_id)
|
|
189
|
+
|
|
190
|
+
def reorder_dropdown_options(self, org_id: str, template_id: str, step_id: str, field_id: str, ordered_options: List[str]) -> bool:
|
|
191
|
+
"""
|
|
192
|
+
Reorder dropdown options to match the provided list.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
org_id: Organization ID
|
|
196
|
+
template_id: Template ID
|
|
197
|
+
step_id: Step ID
|
|
198
|
+
field_id: Form field ID
|
|
199
|
+
ordered_options: List of options in desired order
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
True if reordering was successful
|
|
203
|
+
|
|
204
|
+
Raises:
|
|
205
|
+
TallyfyError: If the request fails
|
|
206
|
+
"""
|
|
207
|
+
try:
|
|
208
|
+
# Get current options to validate
|
|
209
|
+
current_options = self.get_dropdown_options(org_id, template_id, step_id, field_id)
|
|
210
|
+
|
|
211
|
+
# Validate that all provided options exist in current options
|
|
212
|
+
missing_options = set(ordered_options) - set(current_options)
|
|
213
|
+
if missing_options:
|
|
214
|
+
raise ValueError(f"Cannot reorder: options not found in field: {missing_options}")
|
|
215
|
+
|
|
216
|
+
# Update with reordered options
|
|
217
|
+
return self.update_dropdown_options(org_id, template_id, step_id, field_id, ordered_options)
|
|
218
|
+
|
|
219
|
+
except TallyfyError:
|
|
220
|
+
raise
|
|
221
|
+
except Exception as e:
|
|
222
|
+
self._handle_api_error(e, "reorder dropdown options", org_id=org_id, template_id=template_id, step_id=step_id, field_id=field_id)
|