Spaces:
Running
Running
Update ui/insights_ui_generator.py
Browse files- ui/insights_ui_generator.py +4 -186
ui/insights_ui_generator.py
CHANGED
@@ -71,189 +71,7 @@ def format_report_for_display(report_data: Optional[pd.Series]) -> Dict[str, str
|
|
71 |
|
72 |
return {'header_html': header_html, 'body_markdown': body_markdown}
|
73 |
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
Given the new structure where 'format_report_for_display' handles the split,
|
79 |
-
this function might become redundant or repurposed.
|
80 |
-
Keeping it for now but noting its potential redundancy depending on upstream calls.
|
81 |
-
"""
|
82 |
-
if not report_string or not report_string.strip():
|
83 |
-
return "## Comprehensive Analysis Report\n\n*No analysis report was generated, or an error occurred during its generation.*"
|
84 |
-
|
85 |
-
# Simple formatting for now. Could be enhanced (e.g., looking for patterns like "Section X:" to make them H3)
|
86 |
-
formatted_report = f"## Comprehensive Analysis Report\n\n{report_string.strip()}"
|
87 |
-
return formatted_report
|
88 |
-
|
89 |
-
def extract_key_results_for_selection(
|
90 |
-
actionable_okrs_and_tasks_dict: Optional[Dict[str, Any]]
|
91 |
-
) -> List[Dict[str, Any]]:
|
92 |
-
"""
|
93 |
-
Extracts Key Results from the OKR structure for UI selection in Gradio.
|
94 |
-
Each Key Result is given a unique ID for state management in the Gradio app.
|
95 |
-
|
96 |
-
Args:
|
97 |
-
actionable_okrs_and_tasks_dict: The dictionary representation of TaskExtractionOutput,
|
98 |
-
typically `orchestration_results["actionable_okrs_and_tasks"]`.
|
99 |
-
Expected structure: {'okrs': List[OKR_dict], ...}
|
100 |
-
|
101 |
-
Returns:
|
102 |
-
A list of dictionaries, where each dictionary represents a Key Result:
|
103 |
-
{'okr_index': int, 'kr_index': int, 'okr_objective': str,
|
104 |
-
'kr_description': str, 'unique_kr_id': str}
|
105 |
-
"""
|
106 |
-
key_results_for_ui: List[Dict[str, Any]] = []
|
107 |
-
|
108 |
-
if not actionable_okrs_and_tasks_dict or not isinstance(actionable_okrs_and_tasks_dict.get('okrs'), list):
|
109 |
-
logger.warning("No 'okrs' list found or it's not a list in the provided task extraction output.")
|
110 |
-
return key_results_for_ui
|
111 |
-
|
112 |
-
okrs_list = actionable_okrs_and_tasks_dict['okrs']
|
113 |
-
|
114 |
-
for okr_idx, okr_data in enumerate(okrs_list):
|
115 |
-
if not isinstance(okr_data, dict):
|
116 |
-
logger.warning(f"OKR item at index {okr_idx} is not a dictionary, skipping.")
|
117 |
-
continue
|
118 |
-
|
119 |
-
okr_objective = okr_data.get('objective_description', f"Objective {okr_idx + 1} (Unnamed)")
|
120 |
-
key_results_list = okr_data.get('key_results', [])
|
121 |
-
|
122 |
-
if not isinstance(key_results_list, list):
|
123 |
-
logger.warning(f"Expected 'key_results' in OKR '{okr_objective}' (index {okr_idx}) to be a list, got {type(key_results_list)}.")
|
124 |
-
continue
|
125 |
-
|
126 |
-
for kr_idx, kr_data in enumerate(key_results_list):
|
127 |
-
if not isinstance(kr_data, dict):
|
128 |
-
logger.warning(f"Key Result item for OKR '{okr_objective}' at KR index {kr_idx} is not a dictionary, skipping.")
|
129 |
-
continue
|
130 |
-
|
131 |
-
kr_description = kr_data.get('key_result_description') or kr_data.get('description') or f"Key Result {kr_idx+1} (No description)"
|
132 |
-
key_results_for_ui.append({
|
133 |
-
'okr_index': okr_idx, # Index of the parent OKR in the original list
|
134 |
-
'kr_index': kr_idx, # Index of this KR within its parent OKR
|
135 |
-
'okr_objective': okr_objective,
|
136 |
-
'kr_description': kr_description,
|
137 |
-
'unique_kr_id': f"okr{okr_idx}_kr{kr_idx}" # Unique ID for Gradio component linking
|
138 |
-
})
|
139 |
-
|
140 |
-
if not key_results_for_ui:
|
141 |
-
logger.info("No Key Results were extracted for selection from the OKR data.")
|
142 |
-
|
143 |
-
return key_results_for_ui
|
144 |
-
|
145 |
-
def format_single_okr_for_display(
|
146 |
-
okr_data: Dict[str, Any],
|
147 |
-
accepted_kr_indices: Optional[List[int]] = None,
|
148 |
-
okr_main_index: Optional[int] = None # For titling if needed
|
149 |
-
) -> str:
|
150 |
-
"""
|
151 |
-
Formats a single complete OKR object (with its Key Results and Tasks) into a
|
152 |
-
detailed Markdown string for display. Optionally filters to show only accepted Key Results.
|
153 |
-
|
154 |
-
Args:
|
155 |
-
okr_data: A dictionary representing a single OKR from the TaskExtractionOutput.
|
156 |
-
accepted_kr_indices: Optional list of indices of Key Results within this OKR
|
157 |
-
that were accepted by the user. If None, all KRs are displayed.
|
158 |
-
okr_main_index: Optional index of this OKR in the main list, for titling.
|
159 |
-
|
160 |
-
|
161 |
-
Returns:
|
162 |
-
A Markdown formatted string representing the OKR.
|
163 |
-
"""
|
164 |
-
if not okr_data or not isinstance(okr_data, dict):
|
165 |
-
return "*Invalid OKR data provided for display.*\n"
|
166 |
-
|
167 |
-
md_parts = []
|
168 |
-
|
169 |
-
objective_title_num = f" {okr_main_index + 1}" if okr_main_index is not None else ""
|
170 |
-
objective = okr_data.get('objective_description') or okr_data.get('description') or f"Unnamed Objective{objective_title_num}"
|
171 |
-
logger.info(f"OKR data desccr {objective}")
|
172 |
-
objective_timeline = okr_data.get('objective_timeline', '')
|
173 |
-
objective_owner = okr_data.get('objective_owner', 'N/A')
|
174 |
-
|
175 |
-
md_parts.append(f"### Objective{objective_title_num}: {objective}")
|
176 |
-
if objective_timeline:
|
177 |
-
md_parts.append(f"**Overall Timeline:** {objective_timeline}")
|
178 |
-
if objective_owner and objective_owner != 'N/A':
|
179 |
-
md_parts.append(f"**Overall Owner:** {objective_owner}")
|
180 |
-
md_parts.append("\n---")
|
181 |
-
|
182 |
-
key_results_list = okr_data.get('key_results', [])
|
183 |
-
displayed_kr_count = 0
|
184 |
-
|
185 |
-
if not isinstance(key_results_list, list) or not key_results_list:
|
186 |
-
md_parts.append("\n*No Key Results defined for this objective.*")
|
187 |
-
else:
|
188 |
-
for kr_idx, kr_data in enumerate(key_results_list):
|
189 |
-
if accepted_kr_indices is not None and kr_idx not in accepted_kr_indices:
|
190 |
-
continue # Skip this KR if a filter is applied and it's not in the accepted list
|
191 |
-
|
192 |
-
displayed_kr_count +=1
|
193 |
-
|
194 |
-
if not isinstance(kr_data, dict):
|
195 |
-
md_parts.append(f"\n**Key Result {kr_idx+1}:** *Invalid data format for this Key Result.*")
|
196 |
-
continue
|
197 |
-
|
198 |
-
kr_desc = kr_data.get('key_result_description') or kr_data.get('description') or f"Key Result {kr_idx+1} (No description)"
|
199 |
-
logger.info(f"KR data desccr {kr_desc}")
|
200 |
-
target_metric = kr_data.get('target_metric')
|
201 |
-
target_value = kr_data.get('target_value')
|
202 |
-
kr_data_subj = kr_data.get('data_subject')
|
203 |
-
kr_type = kr_data.get('key_result_type')
|
204 |
-
|
205 |
-
md_parts.append(f"\n#### Key Result {displayed_kr_count} (Original Index: {kr_idx+1}): {kr_desc}")
|
206 |
-
if target_metric and target_value:
|
207 |
-
md_parts.append(f" - **Target:** Measure `{target_metric}` to achieve/reach `{target_value}`")
|
208 |
-
if kr_type and kr_data_subj:
|
209 |
-
md_parts.append(f" **Key result type**: {kr_type}, for **data subject** {kr_data_subj}")
|
210 |
-
|
211 |
-
|
212 |
-
tasks_list = kr_data.get('tasks', [])
|
213 |
-
if tasks_list and isinstance(tasks_list, list):
|
214 |
-
md_parts.append(" **Associated Tasks:**")
|
215 |
-
for task_idx, task_data in enumerate(tasks_list):
|
216 |
-
if not isinstance(task_data, dict):
|
217 |
-
md_parts.append(f" - Task {task_idx+1}: *Invalid data format for this task.*")
|
218 |
-
continue
|
219 |
-
|
220 |
-
task_desc = task_data.get('task_description') or task_data.get('description') or f"Task {task_idx+1} (No description)"
|
221 |
-
logger.info(f"task data desccr {task_desc}")
|
222 |
-
task_cat = task_data.get('task_category') or task_data.get('category') or 'N/A'
|
223 |
-
task_effort = task_data.get('effort', 'N/A')
|
224 |
-
task_timeline = task_data.get('timeline', 'N/A')
|
225 |
-
task_priority = task_data.get('priority', 'N/A')
|
226 |
-
task_responsible = task_data.get('responsible_party', 'N/A')
|
227 |
-
task_type = task_data.get('task_type', 'N/A')
|
228 |
-
data_subject_val = task_data.get('data_subject')
|
229 |
-
data_subject_str = f", Data Subject: `{data_subject_val}`" if data_subject_val and task_type == 'tracking' else ""
|
230 |
-
|
231 |
-
md_parts.append(f" - **{task_idx+1}. {task_desc}**")
|
232 |
-
md_parts.append(f" - *Category:* {task_cat} | *Type:* {task_type}{data_subject_str}")
|
233 |
-
md_parts.append(f" - *Priority:* **{task_priority}** | *Effort:* {task_effort} | *Timeline:* {task_timeline}")
|
234 |
-
md_parts.append(f" - *Responsible:* {task_responsible}")
|
235 |
-
|
236 |
-
obj_deliv = task_data.get('objective_deliverable')
|
237 |
-
if obj_deliv: md_parts.append(f" - *Objective/Deliverable:* {obj_deliv}")
|
238 |
-
|
239 |
-
success_crit = task_data.get('success_criteria_metrics')
|
240 |
-
if success_crit: md_parts.append(f" - *Success Metrics:* {success_crit}")
|
241 |
-
|
242 |
-
why_prop = task_data.get('why_proposed')
|
243 |
-
if why_prop: md_parts.append(f" - *Rationale:* {why_prop}")
|
244 |
-
|
245 |
-
priority_just = task_data.get('priority_justification')
|
246 |
-
if priority_just: md_parts.append(f" - *Priority Justification:* {priority_just}")
|
247 |
-
|
248 |
-
dependencies = task_data.get('dependencies_prerequisites')
|
249 |
-
if dependencies: md_parts.append(f" - *Dependencies:* {dependencies}")
|
250 |
-
md_parts.append("") # Extra newline for spacing between tasks details
|
251 |
-
else:
|
252 |
-
md_parts.append(" *No tasks defined for this Key Result.*")
|
253 |
-
md_parts.append("\n---\n") # Separator between Key Results
|
254 |
-
|
255 |
-
if displayed_kr_count == 0 and accepted_kr_indices is not None:
|
256 |
-
md_parts.append("\n*No Key Results matching the 'accepted' filter for this objective.*")
|
257 |
-
|
258 |
-
return "\n".join(md_parts)
|
259 |
-
|
|
|
71 |
|
72 |
return {'header_html': header_html, 'body_markdown': body_markdown}
|
73 |
|
74 |
+
# The following functions (extract_key_results_for_selection, format_single_okr_for_display)
|
75 |
+
# are no longer needed as their functionality is now handled by the new `ui/okr_ui_generator.py`
|
76 |
+
# which directly formats the full OKR structure into HTML.
|
77 |
+
# The 'format_report_to_markdown' function was also removed as it was redundant and not used.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|