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.
- tallyfy/__init__.py +27 -0
- tallyfy/__pycache__/__init__.cpython-310.pyc +0 -0
- tallyfy/__pycache__/core.cpython-310.pyc +0 -0
- tallyfy/__pycache__/form_fields_management.cpython-310.pyc +0 -0
- tallyfy/__pycache__/models.cpython-310.pyc +0 -0
- tallyfy/__pycache__/task_management.cpython-310.pyc +0 -0
- tallyfy/__pycache__/template_management.cpython-310.pyc +0 -0
- tallyfy/__pycache__/user_management.cpython-310.pyc +0 -0
- tallyfy/core.py +361 -0
- tallyfy/form_fields_management/__init__.py +70 -0
- tallyfy/form_fields_management/__pycache__/__init__.cpython-310.pyc +0 -0
- tallyfy/form_fields_management/__pycache__/base.cpython-310.pyc +0 -0
- tallyfy/form_fields_management/__pycache__/crud_operations.cpython-310.pyc +0 -0
- tallyfy/form_fields_management/__pycache__/options_management.cpython-310.pyc +0 -0
- tallyfy/form_fields_management/__pycache__/suggestions.cpython-310.pyc +0 -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 +1464 -0
- tallyfy/organization_management/__init__.py +26 -0
- tallyfy/organization_management/base.py +76 -0
- tallyfy/organization_management/retrieval.py +39 -0
- tallyfy/task_management/__init__.py +81 -0
- tallyfy/task_management/__pycache__/__init__.cpython-310.pyc +0 -0
- tallyfy/task_management/__pycache__/base.cpython-310.pyc +0 -0
- tallyfy/task_management/__pycache__/creation.cpython-310.pyc +0 -0
- tallyfy/task_management/__pycache__/retrieval.cpython-310.pyc +0 -0
- tallyfy/task_management/__pycache__/search.cpython-310.pyc +0 -0
- tallyfy/task_management/base.py +125 -0
- tallyfy/task_management/creation.py +221 -0
- tallyfy/task_management/retrieval.py +252 -0
- tallyfy/task_management/search.py +198 -0
- tallyfy/template_management/__init__.py +85 -0
- tallyfy/template_management/analysis.py +1099 -0
- tallyfy/template_management/automation.py +469 -0
- tallyfy/template_management/base.py +56 -0
- tallyfy/template_management/basic_operations.py +479 -0
- tallyfy/template_management/health_assessment.py +793 -0
- tallyfy/user_management/__init__.py +70 -0
- tallyfy/user_management/__pycache__/__init__.cpython-310.pyc +0 -0
- tallyfy/user_management/__pycache__/base.cpython-310.pyc +0 -0
- tallyfy/user_management/__pycache__/invitation.cpython-310.pyc +0 -0
- tallyfy/user_management/__pycache__/retrieval.cpython-310.pyc +0 -0
- tallyfy/user_management/base.py +146 -0
- tallyfy/user_management/invitation.py +286 -0
- tallyfy/user_management/retrieval.py +381 -0
- tallyfy-1.0.16.dist-info/METADATA +742 -0
- tallyfy-1.0.16.dist-info/RECORD +52 -0
- tallyfy-1.0.16.dist-info/WHEEL +5 -0
- tallyfy-1.0.16.dist-info/licenses/LICENSE +21 -0
- tallyfy-1.0.16.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AI-powered form field suggestions
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import List, Dict, Any, Optional
|
|
6
|
+
from .base import FormFieldManagerBase
|
|
7
|
+
from ..models import Step, TallyfyError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FormFieldSuggestions(FormFieldManagerBase):
|
|
11
|
+
"""Handles AI-powered form field suggestions based on step content"""
|
|
12
|
+
|
|
13
|
+
def suggest_form_fields_for_step(self, org_id: str, template_id: str, step_id: str) -> List[Dict[str, Any]]:
|
|
14
|
+
"""
|
|
15
|
+
AI-powered suggestions for relevant form fields based on step content.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
org_id: Organization ID
|
|
19
|
+
template_id: Template ID
|
|
20
|
+
step_id: Step ID to analyze
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
List of suggested form field configurations
|
|
24
|
+
|
|
25
|
+
Raises:
|
|
26
|
+
TallyfyError: If the request fails
|
|
27
|
+
"""
|
|
28
|
+
self._validate_org_id(org_id)
|
|
29
|
+
self._validate_template_id(template_id)
|
|
30
|
+
self._validate_step_id(step_id)
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
# Get the step details to analyze
|
|
34
|
+
step = self._get_step_details(org_id, template_id, step_id)
|
|
35
|
+
if not step:
|
|
36
|
+
raise TallyfyError(f"Step {step_id} not found")
|
|
37
|
+
|
|
38
|
+
# Get template context for better suggestions
|
|
39
|
+
template = self.sdk.get_template(org_id, template_id)
|
|
40
|
+
if not template:
|
|
41
|
+
raise TallyfyError(f"Template {template_id} not found")
|
|
42
|
+
|
|
43
|
+
# Analyze step content for intelligent suggestions
|
|
44
|
+
step_title = step.title.lower() if step.title else ''
|
|
45
|
+
step_summary = step.summary.lower() if step.summary else ''
|
|
46
|
+
step_type = step.step_type or 'task'
|
|
47
|
+
existing_fields = step.captures or []
|
|
48
|
+
|
|
49
|
+
# Combined text for analysis
|
|
50
|
+
step_text = f"{step_title} {step_summary}".strip()
|
|
51
|
+
|
|
52
|
+
suggestions = []
|
|
53
|
+
|
|
54
|
+
# Get field patterns for analysis
|
|
55
|
+
field_patterns = self._get_field_patterns()
|
|
56
|
+
|
|
57
|
+
# Check existing field types to avoid duplicates
|
|
58
|
+
existing_field_types = set()
|
|
59
|
+
existing_field_labels = set()
|
|
60
|
+
for field in existing_fields:
|
|
61
|
+
if hasattr(field, 'field_type'):
|
|
62
|
+
existing_field_types.add(field.field_type)
|
|
63
|
+
if hasattr(field, 'label'):
|
|
64
|
+
existing_field_labels.add(field.label.lower())
|
|
65
|
+
|
|
66
|
+
# Analyze step content against patterns
|
|
67
|
+
matched_patterns = []
|
|
68
|
+
for pattern_name, pattern_data in field_patterns.items():
|
|
69
|
+
keyword_matches = sum(1 for keyword in pattern_data['keywords'] if keyword in step_text)
|
|
70
|
+
if keyword_matches > 0:
|
|
71
|
+
matched_patterns.append((pattern_name, keyword_matches, pattern_data))
|
|
72
|
+
|
|
73
|
+
# Sort by relevance (number of keyword matches)
|
|
74
|
+
matched_patterns.sort(key=lambda x: x[1], reverse=True)
|
|
75
|
+
|
|
76
|
+
# Generate suggestions from matched patterns
|
|
77
|
+
suggested_count = 0
|
|
78
|
+
max_suggestions = 5
|
|
79
|
+
|
|
80
|
+
for pattern_name, matches, pattern_data in matched_patterns:
|
|
81
|
+
if suggested_count >= max_suggestions:
|
|
82
|
+
break
|
|
83
|
+
|
|
84
|
+
for field_config in pattern_data['fields']:
|
|
85
|
+
if suggested_count >= max_suggestions:
|
|
86
|
+
break
|
|
87
|
+
|
|
88
|
+
# Skip if similar field already exists
|
|
89
|
+
field_label_lower = field_config['label'].lower()
|
|
90
|
+
if field_label_lower in existing_field_labels:
|
|
91
|
+
continue
|
|
92
|
+
|
|
93
|
+
# Add suggestion with metadata
|
|
94
|
+
suggestion = {
|
|
95
|
+
'field_config': field_config.copy(),
|
|
96
|
+
'confidence': min(0.9, 0.3 + (matches * 0.2)), # Confidence based on keyword matches
|
|
97
|
+
'pattern_matched': pattern_name,
|
|
98
|
+
'keyword_matches': matches,
|
|
99
|
+
'priority': 'high' if matches >= 2 else 'medium' if matches >= 1 else 'low'
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# Add position suggestion
|
|
103
|
+
suggestion['field_config']['position'] = len(existing_fields) + suggested_count + 1
|
|
104
|
+
|
|
105
|
+
suggestions.append(suggestion)
|
|
106
|
+
suggested_count += 1
|
|
107
|
+
|
|
108
|
+
# If no specific patterns matched, provide generic useful fields
|
|
109
|
+
if not suggestions:
|
|
110
|
+
suggestions = self._get_generic_suggestions(existing_fields)
|
|
111
|
+
|
|
112
|
+
# Add implementation guidance
|
|
113
|
+
for suggestion in suggestions:
|
|
114
|
+
suggestion['implementation'] = {
|
|
115
|
+
'method': 'add_form_field_to_step',
|
|
116
|
+
'parameters': {
|
|
117
|
+
'org_id': org_id,
|
|
118
|
+
'template_id': template_id,
|
|
119
|
+
'step_id': step_id,
|
|
120
|
+
'field_data': suggestion['field_config']
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return suggestions
|
|
125
|
+
|
|
126
|
+
except TallyfyError:
|
|
127
|
+
raise
|
|
128
|
+
except Exception as e:
|
|
129
|
+
self._handle_api_error(e, "suggest form fields for step", org_id=org_id, template_id=template_id, step_id=step_id)
|
|
130
|
+
|
|
131
|
+
def _get_step_details(self, org_id: str, template_id: str, step_id: str) -> Optional[Step]:
|
|
132
|
+
"""
|
|
133
|
+
Get step details using CRUD operations to avoid circular imports.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
org_id: Organization ID
|
|
137
|
+
template_id: Template ID
|
|
138
|
+
step_id: Step ID
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
Step object or None if not found
|
|
142
|
+
"""
|
|
143
|
+
from .crud_operations import FormFieldCRUD
|
|
144
|
+
crud = FormFieldCRUD(self.sdk)
|
|
145
|
+
return crud.get_step(org_id, template_id, step_id)
|
|
146
|
+
|
|
147
|
+
def _get_field_patterns(self) -> Dict[str, Dict[str, Any]]:
|
|
148
|
+
"""
|
|
149
|
+
Get predefined field patterns for content analysis.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Dictionary of field patterns with keywords and suggested fields
|
|
153
|
+
"""
|
|
154
|
+
return {
|
|
155
|
+
# Approval and Review patterns
|
|
156
|
+
'approval': {
|
|
157
|
+
'keywords': ['approve', 'review', 'sign off', 'accept', 'reject', 'confirm'],
|
|
158
|
+
'fields': [
|
|
159
|
+
{
|
|
160
|
+
'field_type': 'dropdown',
|
|
161
|
+
'label': 'Decision',
|
|
162
|
+
'options': [
|
|
163
|
+
{'value': 'approved', 'label': 'Approved'},
|
|
164
|
+
{'value': 'rejected', 'label': 'Rejected'},
|
|
165
|
+
{'value': 'needs_revision', 'label': 'Needs Revision'}
|
|
166
|
+
],
|
|
167
|
+
'required': True,
|
|
168
|
+
'reasoning': 'Approval steps typically need a decision field'
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
'field_type': 'textarea',
|
|
172
|
+
'label': 'Comments',
|
|
173
|
+
'required': False,
|
|
174
|
+
'reasoning': 'Comments are useful for providing feedback'
|
|
175
|
+
}
|
|
176
|
+
]
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
# Contact and Communication patterns
|
|
180
|
+
'contact': {
|
|
181
|
+
'keywords': ['contact', 'call', 'email', 'phone', 'reach out', 'communicate'],
|
|
182
|
+
'fields': [
|
|
183
|
+
{
|
|
184
|
+
'field_type': 'text',
|
|
185
|
+
'label': 'Contact Method',
|
|
186
|
+
'required': True,
|
|
187
|
+
'reasoning': 'Track how contact was made'
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
'field_type': 'date',
|
|
191
|
+
'label': 'Contact Date',
|
|
192
|
+
'required': True,
|
|
193
|
+
'reasoning': 'Record when contact was made'
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
'field_type': 'textarea',
|
|
197
|
+
'label': 'Contact Notes',
|
|
198
|
+
'required': False,
|
|
199
|
+
'reasoning': 'Document the conversation or interaction'
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
# Document and File patterns
|
|
205
|
+
'document': {
|
|
206
|
+
'keywords': ['document', 'file', 'upload', 'attach', 'report', 'contract', 'agreement'],
|
|
207
|
+
'fields': [
|
|
208
|
+
{
|
|
209
|
+
'field_type': 'file',
|
|
210
|
+
'label': 'Document Upload',
|
|
211
|
+
'required': True,
|
|
212
|
+
'reasoning': 'File upload for document-related steps'
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
'field_type': 'text',
|
|
216
|
+
'label': 'Document Title',
|
|
217
|
+
'required': False,
|
|
218
|
+
'reasoning': 'Name or title of the document'
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
'field_type': 'textarea',
|
|
222
|
+
'label': 'Document Description',
|
|
223
|
+
'required': False,
|
|
224
|
+
'reasoning': 'Brief description of the document'
|
|
225
|
+
}
|
|
226
|
+
]
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
# Payment and Financial patterns
|
|
230
|
+
'payment': {
|
|
231
|
+
'keywords': ['payment', 'invoice', 'cost', 'price', 'amount', 'bill', 'expense'],
|
|
232
|
+
'fields': [
|
|
233
|
+
{
|
|
234
|
+
'field_type': 'number',
|
|
235
|
+
'label': 'Amount',
|
|
236
|
+
'required': True,
|
|
237
|
+
'reasoning': 'Financial steps need amount tracking'
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
'field_type': 'dropdown',
|
|
241
|
+
'label': 'Currency',
|
|
242
|
+
'options': [
|
|
243
|
+
{'value': 'USD', 'label': 'USD'},
|
|
244
|
+
{'value': 'EUR', 'label': 'EUR'},
|
|
245
|
+
{'value': 'GBP', 'label': 'GBP'}
|
|
246
|
+
],
|
|
247
|
+
'required': True,
|
|
248
|
+
'reasoning': 'Specify currency for financial transactions'
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
'field_type': 'date',
|
|
252
|
+
'label': 'Payment Date',
|
|
253
|
+
'required': False,
|
|
254
|
+
'reasoning': 'Track when payment was made'
|
|
255
|
+
}
|
|
256
|
+
]
|
|
257
|
+
},
|
|
258
|
+
|
|
259
|
+
# Quality and Testing patterns
|
|
260
|
+
'quality': {
|
|
261
|
+
'keywords': ['test', 'quality', 'check', 'verify', 'validate', 'inspect'],
|
|
262
|
+
'fields': [
|
|
263
|
+
{
|
|
264
|
+
'field_type': 'dropdown',
|
|
265
|
+
'label': 'Test Result',
|
|
266
|
+
'options': [
|
|
267
|
+
{'value': 'pass', 'label': 'Pass'},
|
|
268
|
+
{'value': 'fail', 'label': 'Fail'},
|
|
269
|
+
{'value': 'partial', 'label': 'Partial Pass'}
|
|
270
|
+
],
|
|
271
|
+
'required': True,
|
|
272
|
+
'reasoning': 'Quality steps need result tracking'
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
'field_type': 'textarea',
|
|
276
|
+
'label': 'Test Notes',
|
|
277
|
+
'required': False,
|
|
278
|
+
'reasoning': 'Document test findings and issues'
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
'field_type': 'number',
|
|
282
|
+
'label': 'Score',
|
|
283
|
+
'required': False,
|
|
284
|
+
'reasoning': 'Numerical rating for quality assessment'
|
|
285
|
+
}
|
|
286
|
+
]
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
# Schedule and Time patterns
|
|
290
|
+
'schedule': {
|
|
291
|
+
'keywords': ['schedule', 'meeting', 'appointment', 'deadline', 'due', 'time'],
|
|
292
|
+
'fields': [
|
|
293
|
+
{
|
|
294
|
+
'field_type': 'datetime',
|
|
295
|
+
'label': 'Scheduled Time',
|
|
296
|
+
'required': True,
|
|
297
|
+
'reasoning': 'Scheduling steps need date and time'
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
'field_type': 'text',
|
|
301
|
+
'label': 'Location',
|
|
302
|
+
'required': False,
|
|
303
|
+
'reasoning': 'Meeting location or venue'
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
'field_type': 'textarea',
|
|
307
|
+
'label': 'Agenda',
|
|
308
|
+
'required': False,
|
|
309
|
+
'reasoning': 'Meeting agenda or notes'
|
|
310
|
+
}
|
|
311
|
+
]
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
def _get_generic_suggestions(self, existing_fields: List) -> List[Dict[str, Any]]:
|
|
316
|
+
"""
|
|
317
|
+
Get generic form field suggestions when no specific patterns match.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
existing_fields: List of existing form fields on the step
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
List of generic field suggestions
|
|
324
|
+
"""
|
|
325
|
+
return [
|
|
326
|
+
{
|
|
327
|
+
'field_config': {
|
|
328
|
+
'field_type': 'textarea',
|
|
329
|
+
'label': 'Notes',
|
|
330
|
+
'required': False,
|
|
331
|
+
'position': len(existing_fields) + 1
|
|
332
|
+
},
|
|
333
|
+
'confidence': 0.6,
|
|
334
|
+
'pattern_matched': 'generic',
|
|
335
|
+
'keyword_matches': 0,
|
|
336
|
+
'priority': 'medium',
|
|
337
|
+
'reasoning': 'Notes field is useful for most steps to capture additional information'
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
'field_config': {
|
|
341
|
+
'field_type': 'dropdown',
|
|
342
|
+
'label': 'Status',
|
|
343
|
+
'options': [
|
|
344
|
+
{'value': 'completed', 'label': 'Completed'},
|
|
345
|
+
{'value': 'in_progress', 'label': 'In Progress'},
|
|
346
|
+
{'value': 'blocked', 'label': 'Blocked'}
|
|
347
|
+
],
|
|
348
|
+
'required': False,
|
|
349
|
+
'position': len(existing_fields) + 2
|
|
350
|
+
},
|
|
351
|
+
'confidence': 0.5,
|
|
352
|
+
'pattern_matched': 'generic',
|
|
353
|
+
'keyword_matches': 0,
|
|
354
|
+
'priority': 'low',
|
|
355
|
+
'reasoning': 'Status tracking can be helpful for workflow management'
|
|
356
|
+
}
|
|
357
|
+
]
|
|
358
|
+
|
|
359
|
+
def suggest_field_improvements(self, org_id: str, template_id: str, step_id: str, field_id: str) -> List[Dict[str, Any]]:
|
|
360
|
+
"""
|
|
361
|
+
Suggest improvements for an existing form field.
|
|
362
|
+
|
|
363
|
+
Args:
|
|
364
|
+
org_id: Organization ID
|
|
365
|
+
template_id: Template ID
|
|
366
|
+
step_id: Step ID
|
|
367
|
+
field_id: Form field ID to analyze
|
|
368
|
+
|
|
369
|
+
Returns:
|
|
370
|
+
List of improvement suggestions
|
|
371
|
+
|
|
372
|
+
Raises:
|
|
373
|
+
TallyfyError: If the request fails
|
|
374
|
+
"""
|
|
375
|
+
try:
|
|
376
|
+
# Get current field options using options management
|
|
377
|
+
from .options_management import FormFieldOptions
|
|
378
|
+
options_manager = FormFieldOptions(self.sdk)
|
|
379
|
+
|
|
380
|
+
current_options = options_manager.get_dropdown_options(org_id, template_id, step_id, field_id)
|
|
381
|
+
|
|
382
|
+
improvements = []
|
|
383
|
+
|
|
384
|
+
# Analyze dropdown options if applicable
|
|
385
|
+
if current_options:
|
|
386
|
+
# Check for common improvements
|
|
387
|
+
if len(current_options) > 10:
|
|
388
|
+
improvements.append({
|
|
389
|
+
'type': 'reduce_options',
|
|
390
|
+
'priority': 'medium',
|
|
391
|
+
'description': 'Consider reducing the number of dropdown options for better usability',
|
|
392
|
+
'recommendation': 'Group similar options or use a search-enabled dropdown',
|
|
393
|
+
'current_count': len(current_options)
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
# Check for inconsistent naming
|
|
397
|
+
option_lengths = [len(opt) for opt in current_options]
|
|
398
|
+
if max(option_lengths) - min(option_lengths) > 20:
|
|
399
|
+
improvements.append({
|
|
400
|
+
'type': 'normalize_options',
|
|
401
|
+
'priority': 'low',
|
|
402
|
+
'description': 'Option labels have inconsistent lengths',
|
|
403
|
+
'recommendation': 'Standardize option label lengths for better visual consistency'
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
return improvements
|
|
407
|
+
|
|
408
|
+
except TallyfyError:
|
|
409
|
+
raise
|
|
410
|
+
except Exception as e:
|
|
411
|
+
self._handle_api_error(e, "suggest field improvements", org_id=org_id, template_id=template_id, step_id=step_id, field_id=field_id)
|