Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
@@ -163,13 +163,73 @@ def format_summary_results(summary):
|
|
163 |
else:
|
164 |
sender_stats += "<p style='color: #1f2937;'>No sender statistics available.</p>"
|
165 |
|
166 |
-
# Format primary abuser
|
167 |
-
|
168 |
if summary['primary_abuser']:
|
169 |
-
|
170 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
171 |
else:
|
172 |
-
|
173 |
|
174 |
# Format escalation data
|
175 |
escalation = "<h3 style='color: #1f2937;'>Escalation Analysis</h3>"
|
@@ -180,16 +240,25 @@ def format_summary_results(summary):
|
|
180 |
future_prediction = summary['escalation_data'].get('future_prediction')
|
181 |
cyclic_pattern = summary['escalation_data'].get('cyclic_pattern', False)
|
182 |
|
183 |
-
|
|
|
|
|
|
|
184 |
escalation += f"<p style='color: #1f2937;'><strong style='color: #1f2937;'>Trend Strength:</strong> {trend_strength:.2f}</p>"
|
185 |
escalation += f"<p style='color: #1f2937;'><strong style='color: #1f2937;'>Recent Average:</strong> {recent_avg:.1f}%</p>"
|
186 |
|
187 |
if future_prediction is not None:
|
188 |
-
|
|
|
189 |
|
190 |
if cyclic_pattern:
|
191 |
cycle_period = summary['escalation_data'].get('cycle_period', 'unknown')
|
192 |
-
escalation += f"
|
|
|
|
|
|
|
|
|
|
|
193 |
else:
|
194 |
escalation += "<p style='color: #1f2937;'>No escalation data available.</p>"
|
195 |
|
@@ -198,14 +267,14 @@ def format_summary_results(summary):
|
|
198 |
if summary['recommendations']:
|
199 |
recommendations += "<div style='max-height: 300px; overflow-y: auto;'>"
|
200 |
for rec in summary['recommendations']:
|
201 |
-
recommendations += f"<div style='margin-bottom: 15px; padding:
|
202 |
-
recommendations += f"<h4 style='color: #1f2937;'>{rec['title']}</h4>"
|
203 |
recommendations += f"<p style='color: #1f2937;'>{rec['description']}</p>"
|
204 |
|
205 |
if rec['actions']:
|
206 |
-
recommendations += "<ul>"
|
207 |
for action in rec['actions']:
|
208 |
-
recommendations += f"<li style='color: #1f2937;'>{action}</li>"
|
209 |
recommendations += "</ul>"
|
210 |
|
211 |
recommendations += "</div>"
|
@@ -218,7 +287,7 @@ def format_summary_results(summary):
|
|
218 |
<div style="padding: 20px; border: 1px solid #ddd; border-radius: 10px; background-color: #ffffff;">
|
219 |
{basic_info}
|
220 |
{sender_stats}
|
221 |
-
{
|
222 |
{escalation}
|
223 |
{recommendations}
|
224 |
</div>
|
@@ -227,9 +296,11 @@ def format_summary_results(summary):
|
|
227 |
return html
|
228 |
except Exception as e:
|
229 |
logger.error(f"Error formatting summary results: {e}")
|
|
|
230 |
return f"<p style='color: #1f2937;'>Error formatting summary: {str(e)}</p>"
|
231 |
|
232 |
|
|
|
233 |
def format_safety_plan(safety_plan):
|
234 |
"""Format safety plan for display"""
|
235 |
try:
|
@@ -276,8 +347,8 @@ def create_interface():
|
|
276 |
with gr.Column(scale=2):
|
277 |
summary_output = gr.HTML(
|
278 |
label="Analysis Summary",
|
279 |
-
value="<p>Upload a file and click 'Analyze Chat History' to see results.</p>"
|
280 |
-
|
281 |
|
282 |
with gr.Row():
|
283 |
with gr.Column():
|
@@ -341,54 +412,62 @@ def create_interface():
|
|
341 |
</div>
|
342 |
""")
|
343 |
|
344 |
-
# Define event handlers
|
345 |
def handle_file_analysis(file_path):
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
392 |
|
393 |
def handle_message_analysis(message):
|
394 |
if not message.strip():
|
|
|
163 |
else:
|
164 |
sender_stats += "<p style='color: #1f2937;'>No sender statistics available.</p>"
|
165 |
|
166 |
+
# Format primary abuser section
|
167 |
+
primary_abuser_html = "<h3 style='color: #1f2937;'>Primary Concern</h3>"
|
168 |
if summary['primary_abuser']:
|
169 |
+
primary_abuser_html += f"<p style='color: #1f2937;'>The analysis indicates that <strong style='color: #1f2937;'>{summary['primary_abuser']}</strong> "
|
170 |
+
primary_abuser_html += "shows the highest percentage of concerning communication patterns.</p>"
|
171 |
+
|
172 |
+
# Add detailed primary abuser analysis if available
|
173 |
+
if summary.get('primary_abuser_analysis'):
|
174 |
+
analysis = summary['primary_abuser_analysis']
|
175 |
+
|
176 |
+
# Add pattern section with collapsible details
|
177 |
+
primary_abuser_html += "<div style='margin-top: 20px; border: 1px solid #e5e7eb; border-radius: 8px; padding: 15px; background-color: #f9fafb;'>"
|
178 |
+
primary_abuser_html += "<h4 style='color: #1f2937; margin-top: 0;'>Most Common Patterns</h4>"
|
179 |
+
|
180 |
+
# Get patterns from sender stats
|
181 |
+
if summary['sender_stats'].get(summary['primary_abuser']):
|
182 |
+
abuser_stats = summary['sender_stats'][summary['primary_abuser']]
|
183 |
+
if abuser_stats.get('common_patterns'):
|
184 |
+
primary_abuser_html += "<ul style='color: #1f2937;'>"
|
185 |
+
for pattern, count in abuser_stats['common_patterns'][:5]: # Top 5 patterns
|
186 |
+
primary_abuser_html += f"<li style='color: #1f2937; margin-bottom: 8px;'><strong>{pattern}</strong> ({count} occurrences)</li>"
|
187 |
+
primary_abuser_html += "</ul>"
|
188 |
+
else:
|
189 |
+
primary_abuser_html += "<p style='color: #1f2937;'>No specific patterns detected.</p>"
|
190 |
+
|
191 |
+
# Add emotional tones section
|
192 |
+
primary_abuser_html += "<h4 style='color: #1f2937; margin-top: 20px;'>Emotional Tone Analysis</h4>"
|
193 |
+
if analysis.get('emotional_tones'):
|
194 |
+
primary_abuser_html += "<ul style='color: #1f2937;'>"
|
195 |
+
for tone, count in analysis['emotional_tones']:
|
196 |
+
if tone != "neutral":
|
197 |
+
primary_abuser_html += f"<li style='color: #1f2937;'><strong>{tone}</strong> ({count} messages)</li>"
|
198 |
+
primary_abuser_html += "</ul>"
|
199 |
+
else:
|
200 |
+
primary_abuser_html += "<p style='color: #1f2937;'>No significant emotional tones detected.</p>"
|
201 |
+
|
202 |
+
# Add DARVO score
|
203 |
+
darvo_level = "High" if analysis.get('darvo_score', 0) >= 0.65 else "Moderate" if analysis.get('darvo_score', 0) >= 0.25 else "Low"
|
204 |
+
darvo_color = "#ef4444" if darvo_level == "High" else "#f59e0b" if darvo_level == "Moderate" else "#10b981"
|
205 |
+
|
206 |
+
primary_abuser_html += f"""
|
207 |
+
<h4 style='color: #1f2937; margin-top: 20px;'>DARVO Analysis</h4>
|
208 |
+
<p style='color: #1f2937;'>DARVO Score: <strong style='color: {darvo_color};'>{analysis.get('darvo_score', 0):.3f}</strong> ({darvo_level})</p>
|
209 |
+
<p style='color: #1f2937; font-size: 0.9em;'>DARVO (Deny, Attack, Reverse Victim & Offender) indicates attempts to shift blame and manipulate the narrative.</p>
|
210 |
+
"""
|
211 |
+
|
212 |
+
# Add time patterns section
|
213 |
+
primary_abuser_html += "<h4 style='color: #1f2937; margin-top: 20px;'>Communication Timing Patterns</h4>"
|
214 |
+
|
215 |
+
# Peak hours
|
216 |
+
primary_abuser_html += "<p style='color: #1f2937;'><strong>Peak Hours:</strong> "
|
217 |
+
if analysis.get('peak_hours'):
|
218 |
+
peak_hours_formatted = [f"{h}:00" for h in analysis['peak_hours']]
|
219 |
+
primary_abuser_html += f"{', '.join(peak_hours_formatted)}</p>"
|
220 |
+
else:
|
221 |
+
primary_abuser_html += "No clear pattern</p>"
|
222 |
+
|
223 |
+
# Peak days
|
224 |
+
primary_abuser_html += "<p style='color: #1f2937;'><strong>Peak Days:</strong> "
|
225 |
+
if analysis.get('peak_days'):
|
226 |
+
primary_abuser_html += f"{', '.join(analysis['peak_days'])}</p>"
|
227 |
+
else:
|
228 |
+
primary_abuser_html += "No clear pattern</p>"
|
229 |
+
|
230 |
+
primary_abuser_html += "</div>"
|
231 |
else:
|
232 |
+
primary_abuser_html += "<p style='color: #1f2937;'>No clear pattern of concerning communication from a specific sender.</p>"
|
233 |
|
234 |
# Format escalation data
|
235 |
escalation = "<h3 style='color: #1f2937;'>Escalation Analysis</h3>"
|
|
|
240 |
future_prediction = summary['escalation_data'].get('future_prediction')
|
241 |
cyclic_pattern = summary['escalation_data'].get('cyclic_pattern', False)
|
242 |
|
243 |
+
# Add color coding for trend direction
|
244 |
+
trend_color = "#ef4444" if trend == "escalating" else "#10b981" if trend == "de-escalating" else "#6b7280"
|
245 |
+
|
246 |
+
escalation += f"<p style='color: #1f2937;'><strong style='color: #1f2937;'>Trend Direction:</strong> <span style='color: {trend_color};'>{trend.title()}</span></p>"
|
247 |
escalation += f"<p style='color: #1f2937;'><strong style='color: #1f2937;'>Trend Strength:</strong> {trend_strength:.2f}</p>"
|
248 |
escalation += f"<p style='color: #1f2937;'><strong style='color: #1f2937;'>Recent Average:</strong> {recent_avg:.1f}%</p>"
|
249 |
|
250 |
if future_prediction is not None:
|
251 |
+
future_color = "#ef4444" if future_prediction > 70 else "#f59e0b" if future_prediction > 50 else "#10b981"
|
252 |
+
escalation += f"<p style='color: #1f2937;'><strong style='color: #1f2937;'>7-Day Prediction:</strong> <span style='color: {future_color};'>{future_prediction:.1f}%</span></p>"
|
253 |
|
254 |
if cyclic_pattern:
|
255 |
cycle_period = summary['escalation_data'].get('cycle_period', 'unknown')
|
256 |
+
escalation += f"""
|
257 |
+
<div style='margin-top: 15px; padding: 10px; background-color: #fffbeb; border-left: 4px solid #f59e0b; border-radius: 4px;'>
|
258 |
+
<p style='color: #1f2937; margin: 0;'><strong style='color: #1f2937;'>⚠️ Cyclic Abuse Pattern Detected</strong></p>
|
259 |
+
<p style='color: #1f2937; margin-top: 5px;'>The analysis shows a repeating cycle of approximately {cycle_period} days. This is consistent with the cycle of abuse, where periods of tension and abuse are followed by reconciliation and calm before repeating.</p>
|
260 |
+
</div>
|
261 |
+
"""
|
262 |
else:
|
263 |
escalation += "<p style='color: #1f2937;'>No escalation data available.</p>"
|
264 |
|
|
|
267 |
if summary['recommendations']:
|
268 |
recommendations += "<div style='max-height: 300px; overflow-y: auto;'>"
|
269 |
for rec in summary['recommendations']:
|
270 |
+
recommendations += f"<div style='margin-bottom: 15px; padding: 15px; background: #f8f9fa; border-radius: 8px; border-left: 4px solid #3b82f6;'>"
|
271 |
+
recommendations += f"<h4 style='color: #1f2937; margin-top: 0;'>{rec['title']}</h4>"
|
272 |
recommendations += f"<p style='color: #1f2937;'>{rec['description']}</p>"
|
273 |
|
274 |
if rec['actions']:
|
275 |
+
recommendations += "<ul style='color: #1f2937;'>"
|
276 |
for action in rec['actions']:
|
277 |
+
recommendations += f"<li style='color: #1f2937; margin-bottom: 5px;'>{action}</li>"
|
278 |
recommendations += "</ul>"
|
279 |
|
280 |
recommendations += "</div>"
|
|
|
287 |
<div style="padding: 20px; border: 1px solid #ddd; border-radius: 10px; background-color: #ffffff;">
|
288 |
{basic_info}
|
289 |
{sender_stats}
|
290 |
+
{primary_abuser_html}
|
291 |
{escalation}
|
292 |
{recommendations}
|
293 |
</div>
|
|
|
296 |
return html
|
297 |
except Exception as e:
|
298 |
logger.error(f"Error formatting summary results: {e}")
|
299 |
+
logger.error(traceback.format_exc())
|
300 |
return f"<p style='color: #1f2937;'>Error formatting summary: {str(e)}</p>"
|
301 |
|
302 |
|
303 |
+
|
304 |
def format_safety_plan(safety_plan):
|
305 |
"""Format safety plan for display"""
|
306 |
try:
|
|
|
347 |
with gr.Column(scale=2):
|
348 |
summary_output = gr.HTML(
|
349 |
label="Analysis Summary",
|
350 |
+
value="<p style='color: #1f2937;'>Upload a file and click 'Analyze Chat History' to see results.</p>",
|
351 |
+
elem_id="summary_output" # Add an element ID for easier debugging
|
352 |
|
353 |
with gr.Row():
|
354 |
with gr.Column():
|
|
|
412 |
</div>
|
413 |
""")
|
414 |
|
|
|
415 |
def handle_file_analysis(file_path):
|
416 |
+
if not file_path:
|
417 |
+
return (
|
418 |
+
"<p style='color: #1f2937;'>Please upload a file first.</p>",
|
419 |
+
None, None, None, None,
|
420 |
+
"<p style='color: #1f2937;'>Please upload a file first.</p>"
|
421 |
+
)
|
422 |
+
|
423 |
+
try:
|
424 |
+
# Analyze file
|
425 |
+
results_df, summary = analyze_file(file_path)
|
426 |
+
|
427 |
+
if "error" in summary:
|
428 |
+
return (
|
429 |
+
f"<p style='color: #1f2937;'>Error: {summary['error']}</p>",
|
430 |
+
None, None, None, None,
|
431 |
+
"<p style='color: #1f2937;'>Error analyzing file.</p>"
|
432 |
+
)
|
433 |
+
|
434 |
+
# Format summary
|
435 |
+
summary_html = format_summary_results(summary)
|
436 |
+
|
437 |
+
# Generate plots
|
438 |
+
timeline = generate_timeline_chart(results_df)
|
439 |
+
pattern_freq = generate_pattern_frequency_chart(results_df)
|
440 |
+
sender_comp = generate_sender_comparison_chart(results_df)
|
441 |
+
time_heatmap = generate_time_of_day_heatmap(results_df)
|
442 |
+
|
443 |
+
# Format safety plan
|
444 |
+
safety_plan_html = format_safety_plan(summary['safety_plan'])
|
445 |
+
|
446 |
+
# Debug output
|
447 |
+
logger.info(f"Summary HTML length: {len(summary_html)}")
|
448 |
+
logger.info(f"Generated {len(results_df)} results")
|
449 |
+
logger.info(f"Timeline chart generated: {timeline is not None}")
|
450 |
+
logger.info(f"Pattern frequency chart generated: {pattern_freq is not None}")
|
451 |
+
logger.info(f"Sender comparison chart generated: {sender_comp is not None}")
|
452 |
+
logger.info(f"Time heatmap generated: {time_heatmap is not None}")
|
453 |
+
|
454 |
+
return (
|
455 |
+
summary_html,
|
456 |
+
timeline,
|
457 |
+
pattern_freq,
|
458 |
+
sender_comp,
|
459 |
+
time_heatmap,
|
460 |
+
safety_plan_html
|
461 |
+
)
|
462 |
+
except Exception as e:
|
463 |
+
logger.error(f"Error in handle_file_analysis: {e}")
|
464 |
+
logger.error(traceback.format_exc())
|
465 |
+
return (
|
466 |
+
f"<p style='color: #1f2937;'>Error analyzing file: {str(e)}</p>",
|
467 |
+
None, None, None, None,
|
468 |
+
"<p style='color: #1f2937;'>Error analyzing file.</p>"
|
469 |
+
)
|
470 |
+
|
471 |
|
472 |
def handle_message_analysis(message):
|
473 |
if not message.strip():
|