Spaces:
Running
Running
Update services/report_data_handler.py
Browse files- services/report_data_handler.py +178 -1
services/report_data_handler.py
CHANGED
@@ -62,4 +62,181 @@ def save_report_results(
|
|
62 |
return success_ids['id']
|
63 |
else:
|
64 |
logger.error(f"Failed to save agentic analysis to Bubble. {success_ids}")
|
65 |
-
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
return success_ids['id']
|
63 |
else:
|
64 |
logger.error(f"Failed to save agentic analysis to Bubble. {success_ids}")
|
65 |
+
return False
|
66 |
+
|
67 |
+
|
68 |
+
# --- Data Saving Functions ---
|
69 |
+
|
70 |
+
def save_objectives(
|
71 |
+
org_urn: str,
|
72 |
+
report_id
|
73 |
+
objectives_data: List[Dict[str, Any]]
|
74 |
+
) -> Optional[List[str]]:
|
75 |
+
"""
|
76 |
+
Saves Objective records to Bubble.
|
77 |
+
|
78 |
+
Args:
|
79 |
+
org_urn: The URN of the organization to associate these objectives with.
|
80 |
+
objectives_data: A list of objective dictionaries from the main data structure.
|
81 |
+
|
82 |
+
Returns:
|
83 |
+
A list of the newly created Bubble record IDs for the objectives, or None on failure.
|
84 |
+
"""
|
85 |
+
if not objectives_data:
|
86 |
+
logger.info("No objectives to save.")
|
87 |
+
return []
|
88 |
+
|
89 |
+
payloads = []
|
90 |
+
for objective in objectives_data:
|
91 |
+
payloads.append({
|
92 |
+
#"organization_urn": org_urn,
|
93 |
+
"objective_description": objective.get("objective_description"),
|
94 |
+
"objective_timeline": objective.get("objective_timeline"),
|
95 |
+
"objective_owner": objective.get("objective_owner"),
|
96 |
+
"report": report_id
|
97 |
+
})
|
98 |
+
|
99 |
+
logger.info(f"Attempting to save {len(payloads)} objectives for org_urn: {org_urn}")
|
100 |
+
objective_ids = bulk_upload_to_bubble(payloads, BUBBLE_OBJECTIVES_TABLE_NAME)
|
101 |
+
|
102 |
+
if objective_ids is None:
|
103 |
+
logger.error("Failed to save objectives to Bubble.")
|
104 |
+
return None
|
105 |
+
|
106 |
+
logger.info(f"Successfully saved {len(objective_ids)} objectives.")
|
107 |
+
return objective_ids
|
108 |
+
|
109 |
+
|
110 |
+
def save_key_results(
|
111 |
+
org_urn: str,
|
112 |
+
objectives_with_ids: List[Tuple[Dict[str, Any], str]]
|
113 |
+
) -> Optional[List[Tuple[Dict[str, Any], str]]]:
|
114 |
+
"""
|
115 |
+
Saves Key Result records to Bubble, linking them to their parent objectives.
|
116 |
+
|
117 |
+
Args:
|
118 |
+
org_urn: The URN of the organization.
|
119 |
+
objectives_with_ids: A list of tuples, where each tuple contains an
|
120 |
+
original objective dictionary and its new Bubble ID.
|
121 |
+
Example: [(objective_dict, 'bubble_id_123'), ...]
|
122 |
+
|
123 |
+
Returns:
|
124 |
+
A list of tuples containing the original key result data and its new Bubble ID,
|
125 |
+
or None on failure. This is needed to save the tasks in the next step.
|
126 |
+
"""
|
127 |
+
key_result_payloads = []
|
128 |
+
# This list preserves the original KR data in the correct order to match the returned IDs
|
129 |
+
key_results_to_process = []
|
130 |
+
|
131 |
+
for objective_data, parent_objective_id in objectives_with_ids:
|
132 |
+
for kr in objective_data.get("key_results", []):
|
133 |
+
key_results_to_process.append(kr)
|
134 |
+
key_result_payloads.append({
|
135 |
+
#"organization_urn": org_urn,
|
136 |
+
"okr": parent_objective_id, # Link to parent
|
137 |
+
"description": kr.get("key_result_description"),
|
138 |
+
"target_metric": kr.get("target_metric"),
|
139 |
+
"target_value": kr.get("target_value"),
|
140 |
+
"kr_type": kr.get("key_result_type"),
|
141 |
+
"data_subject": kr.get("data_subject"),
|
142 |
+
})
|
143 |
+
|
144 |
+
if not key_result_payloads:
|
145 |
+
logger.info("No key results to save.")
|
146 |
+
return []
|
147 |
+
|
148 |
+
logger.info(f"Attempting to save {len(key_result_payloads)} key results for org_urn: {org_urn}")
|
149 |
+
key_result_ids = bulk_upload_to_bubble(key_result_payloads, BUBBLE_KEY_RESULTS_TABLE_NAME)
|
150 |
+
|
151 |
+
if key_result_ids is None:
|
152 |
+
logger.error("Failed to save key results to Bubble.")
|
153 |
+
return None
|
154 |
+
|
155 |
+
logger.info(f"Successfully saved {len(key_result_ids)} key results.")
|
156 |
+
# Combine the original KR data with their new IDs
|
157 |
+
return list(zip(key_results_to_process, key_result_ids))
|
158 |
+
|
159 |
+
|
160 |
+
def save_tasks(
|
161 |
+
org_urn: str,
|
162 |
+
key_results_with_ids: List[Tuple[Dict[str, Any], str]]
|
163 |
+
) -> Optional[List[str]]:
|
164 |
+
"""
|
165 |
+
Saves Task records to Bubble, linking them to their parent key results.
|
166 |
+
|
167 |
+
Args:
|
168 |
+
org_urn: The URN of the organization.
|
169 |
+
key_results_with_ids: A list of tuples from the save_key_results function.
|
170 |
+
Example: [(key_result_dict, 'bubble_id_456'), ...]
|
171 |
+
|
172 |
+
Returns:
|
173 |
+
A list of the newly created Bubble record IDs for the tasks, or None on failure.
|
174 |
+
"""
|
175 |
+
task_payloads = []
|
176 |
+
for key_result_data, parent_key_result_id in key_results_with_ids:
|
177 |
+
for task in key_result_data.get("tasks", []):
|
178 |
+
task_payloads.append({
|
179 |
+
#"organization_urn": org_urn,
|
180 |
+
"key_result": parent_key_result_id, # Link to parent
|
181 |
+
"description": task.get("task_description"),
|
182 |
+
"objective_deliverable": task.get("objective_deliverable"),
|
183 |
+
"category": task.get("task_category"),
|
184 |
+
#"task_type": task.get("task_type"),
|
185 |
+
"priority": task.get("priority"),
|
186 |
+
"priority_justification": task.get("priority_justification"),
|
187 |
+
"effort": task.get("effort"),
|
188 |
+
"timeline": task.get("timeline"),
|
189 |
+
#"data_subject": task.get("data_subject"),
|
190 |
+
"responsible_party": task.get("responsible_party"),
|
191 |
+
"success_criteria_metrics": task.get("success_criteria_metrics"),
|
192 |
+
"dependencies": task.get("dependencies_prerequisites"),
|
193 |
+
"why": task.get("why_proposed"),
|
194 |
+
})
|
195 |
+
|
196 |
+
if not task_payloads:
|
197 |
+
logger.info("No tasks to save.")
|
198 |
+
return []
|
199 |
+
|
200 |
+
logger.info(f"Attempting to save {len(task_payloads)} tasks for org_urn: {org_urn}")
|
201 |
+
task_ids = bulk_upload_to_bubble(task_payloads, BUBBLE_TASKS_TABLE_NAME)
|
202 |
+
|
203 |
+
if task_ids is None:
|
204 |
+
logger.error("Failed to save tasks to Bubble.")
|
205 |
+
return None
|
206 |
+
|
207 |
+
logger.info(f"Successfully saved {len(task_ids)} tasks.")
|
208 |
+
return task_ids
|
209 |
+
|
210 |
+
|
211 |
+
# --- Orchestrator Function ---
|
212 |
+
|
213 |
+
def save_actionable_okrs(org_urn: str, actionable_okrs: Dict[str, Any], report_id):
|
214 |
+
"""
|
215 |
+
Orchestrates the sequential saving of objectives, key results, and tasks.
|
216 |
+
|
217 |
+
This function shows how to correctly call the individual save functions
|
218 |
+
in the right order, passing the IDs from one step to the next.
|
219 |
+
"""
|
220 |
+
logger.info(f"--- Starting OKR save process for org_urn: {org_urn} ---")
|
221 |
+
objectives_data = actionable_okrs.get("okrs", [])
|
222 |
+
|
223 |
+
# Step 1: Save the top-level objectives
|
224 |
+
objective_ids = save_objectives(org_urn, objectives_data, report_id)
|
225 |
+
if objective_ids is None:
|
226 |
+
logger.error("OKR save process aborted due to failure in saving objectives.")
|
227 |
+
return
|
228 |
+
|
229 |
+
# Combine the original objective data with their new IDs for the next step
|
230 |
+
objectives_with_ids = list(zip(objectives_data, objective_ids))
|
231 |
+
|
232 |
+
# Step 2: Save the key results, linking them to the objectives
|
233 |
+
key_results_with_ids = save_key_results(org_urn, objectives_with_ids)
|
234 |
+
if key_results_with_ids is None:
|
235 |
+
logger.error("OKR save process aborted due to failure in saving key results.")
|
236 |
+
return
|
237 |
+
|
238 |
+
# Step 3: Save the tasks, linking them to the key results
|
239 |
+
save_tasks(org_urn, key_results_with_ids)
|
240 |
+
|
241 |
+
logger.info(f"--- OKR save process completed for org_urn: {org_urn} ---")
|
242 |
+
|