SamanthaStorm commited on
Commit
4fcf6cf
·
verified ·
1 Parent(s): f7d2bee

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +321 -488
app.py CHANGED
@@ -1,524 +1,357 @@
1
  import gradio as gr
2
  import pandas as pd
 
 
3
  import numpy as np
4
- import os
5
- import json
6
- import logging
7
- import traceback
8
  from datetime import datetime
9
- import plotly.graph_objects as go
10
- import plotly.express as px
11
- from models import ModelManager
12
- from analyzer import MessageAnalyzer
13
- from utils import (
14
- parse_chat_data, generate_timeline_chart, generate_pattern_frequency_chart,
15
- generate_sender_comparison_chart, generate_time_of_day_heatmap
16
- )
17
-
18
- # Set up logging
19
- logging.basicConfig(level=logging.INFO)
20
- logger = logging.getLogger(__name__)
21
-
22
- # Initialize models
23
- model_manager = ModelManager()
24
- model_manager.load_models()
25
-
26
- # Initialize analyzer
27
- analyzer = MessageAnalyzer(model_manager)
28
-
29
- def analyze_file(file_path):
30
- """Analyze uploaded chat file"""
31
- try:
32
- # Parse chat data
33
- df = parse_chat_data(file_path)
34
-
35
- # Analyze chat history
36
- results_df, summary = analyzer.analyze_chat_history(df)
37
-
38
- return results_df, summary
39
- except Exception as e:
40
- logger.error(f"Error analyzing file: {e}")
41
- logger.error(traceback.format_exc())
42
- return None, {"error": str(e)}
43
 
44
- def analyze_single_message(message):
45
- """Analyze a single message"""
46
- try:
47
- if not message.strip():
48
- return {
49
- "abuse_score": 0,
50
- "detected_patterns": [],
51
- "sentiment": "neutral",
52
- "emotional_tone": "neutral",
53
- "boundary_assessment": {"assessment": "neutral"},
54
- "risk_level": "Low"
55
- }
56
-
57
- # Analyze message
58
- analysis = analyzer.analyze_message(message)
59
 
60
- return analysis
61
- except Exception as e:
62
- logger.error(f"Error analyzing message: {e}")
63
- logger.error(traceback.format_exc())
64
- return {
65
- "error": str(e),
66
- "abuse_score": 0,
67
- "detected_patterns": [],
68
- "sentiment": "error",
69
- "emotional_tone": "error",
70
- "boundary_assessment": {"assessment": "error"},
71
- "risk_level": "Unknown"
72
- }
73
-
74
- def get_risk_color(risk_level):
75
- """Get color for risk level"""
76
- colors = {
77
- "Critical": "#ef4444",
78
- "High": "#f97316",
79
- "Moderate": "#f59e0b",
80
- "Low": "#10b981",
81
- "Unknown": "#6b7280"
82
- }
83
- return colors.get(risk_level, "#6b7280")
84
-
85
- def format_analysis_results(analysis):
86
- """Format analysis results for display"""
87
- try:
88
- # Format abuse score
89
- abuse_score_html = f"""
90
- <div style="margin-bottom: 20px;">
91
- <h3 style="color: #1f2937;">Abuse Score: {analysis['abuse_score']:.1f}%</h3>
92
- <div style="background: #f0f0f0; height: 20px; width: 100%; border-radius: 10px;">
93
- <div style="background: {get_risk_color(analysis['risk_level'])};
94
- height: 100%;
95
- width: {min(100, max(0, analysis['abuse_score']))}%;
96
- border-radius: 10px;">
97
- </div>
98
- </div>
99
- <p style="color: #1f2937;">Risk Level: <strong style="color: #1f2937;">{analysis['risk_level']}</strong></p>
100
- </div>
101
- """
102
-
103
- # Format detected patterns
104
- patterns_html = "<h3 style='color: #1f2937;'>Detected Patterns</h3>"
105
- if analysis['detected_patterns']:
106
- patterns_html += "<ul>"
107
- for pattern in analysis['detected_patterns']:
108
- patterns_html += f"<li style='color: #1f2937;'>{pattern}</li>"
109
- patterns_html += "</ul>"
110
- else:
111
- patterns_html += "<p style='color: #1f2937;'>No concerning patterns detected.</p>"
112
-
113
- # Format sentiment and emotional tone
114
- sentiment_html = f"""
115
- <h3 style="color: #1f2937;">Communication Analysis</h3>
116
- <p style="color: #1f2937;"><strong style="color: #1f2937;">Sentiment:</strong> {analysis['sentiment']}</p>
117
- <p style="color: #1f2937;"><strong style="color: #1f2937;">Emotional Tone:</strong> {analysis['emotional_tone']}</p>
118
- <p style="color: #1f2937;"><strong style="color: #1f2937;">DARVO Score:</strong> {analysis['darvo_score']:.3f}</p>
119
- """
120
-
121
- # Format boundary assessment
122
- boundary_html = f"""
123
- <h3 style="color: #1f2937;">Boundary Health</h3>
124
- <p style="color: #1f2937;"><strong style="color: #1f2937;">Assessment:</strong> {analysis['boundary_assessment']['assessment']}</p>
125
- <p style="color: #1f2937;"><strong style="color: #1f2937;">Description:</strong> {analysis['boundary_assessment']['description']}</p>
126
- """
127
-
128
- # Combine all sections
129
- html = f"""
130
- <div style="padding: 20px; border: 1px solid #ddd; border-radius: 10px; background-color: #ffffff;">
131
- {abuse_score_html}
132
- {patterns_html}
133
- {sentiment_html}
134
- {boundary_html}
135
- </div>
136
- """
137
-
138
- return html
139
- except Exception as e:
140
- logger.error(f"Error formatting analysis results: {e}")
141
- return f"<p style='color: #1f2937;'>Error formatting results: {str(e)}</p>"
142
-
143
- def format_summary_results(summary):
144
- """Format summary results for display"""
145
- try:
146
- # Format basic info
147
- basic_info = f"""
148
- <h2 style="color: #1f2937;">Chat Analysis Summary</h2>
149
- <p style="color: #1f2937;"><strong style="color: #1f2937;">Messages Analyzed:</strong> {summary['message_count']}</p>
150
- <p style="color: #1f2937;"><strong style="color: #1f2937;">Date Range:</strong> {summary['date_range']['start']} to {summary['date_range']['end']}</p>
151
- <p style="color: #1f2937;"><strong style="color: #1f2937;">Overall Risk Level:</strong> <span style="color: {get_risk_color(summary['overall_risk_level'])};
152
- font-weight: bold;">
153
- {summary['overall_risk_level']}</span></p>
154
- """
155
-
156
- # Format sender stats
157
- sender_stats = "<h3 style='color: #1f2937;'>Sender Analysis</h3>"
158
- if summary['sender_stats']:
159
- sender_stats += "<table style='width: 100%; border-collapse: collapse;'>"
160
- sender_stats += "<tr><th style='text-align: left; padding: 8px; border-bottom: 1px solid #ddd; color: #1f2937;'>Sender</th>"
161
- sender_stats += "<th style='text-align: left; padding: 8px; border-bottom: 1px solid #ddd; color: #1f2937;'>Messages</th>"
162
- sender_stats += "<th style='text-align: left; padding: 8px; border-bottom: 1px solid #ddd; color: #1f2937;'>Avg. Abuse Score</th>"
163
- sender_stats += "<th style='text-align: left; padding: 8px; border-bottom: 1px solid #ddd; color: #1f2937;'>Common Patterns</th></tr>"
164
 
165
- for sender, stats in summary['sender_stats'].items():
166
- common_patterns = ", ".join([p[0] for p in stats['common_patterns'][:2]]) if stats['common_patterns'] else "None"
167
- sender_stats += f"<tr><td style='padding: 8px; border-bottom: 1px solid #ddd; color: #1f2937;'>{sender}</td>"
168
- sender_stats += f"<td style='padding: 8px; border-bottom: 1px solid #ddd; color: #1f2937;'>{stats['message_count']}</td>"
169
- sender_stats += f"<td style='padding: 8px; border-bottom: 1px solid #ddd; color: #1f2937;'>{stats['avg_abuse_score']:.1f}%</td>"
170
- sender_stats += f"<td style='padding: 8px; border-bottom: 1px solid #ddd; color: #1f2937;'>{common_patterns}</td></tr>"
171
 
172
- sender_stats += "</table>"
173
- else:
174
- sender_stats += "<p style='color: #1f2937;'>No sender statistics available.</p>"
175
-
176
- # Format primary abuser section
177
- primary_abuser_html = "<h3 style='color: #1f2937;'>Primary Concern</h3>"
178
- if summary['primary_abuser']:
179
- primary_abuser_html += f"<p style='color: #1f2937;'>The analysis indicates that <strong style='color: #1f2937;'>{summary['primary_abuser']}</strong> "
180
- primary_abuser_html += "shows the highest percentage of concerning communication patterns.</p>"
181
 
182
- # Add detailed primary abuser analysis if available
183
- if summary.get('primary_abuser_analysis'):
184
- analysis = summary['primary_abuser_analysis']
185
-
186
- # Add pattern section with collapsible details
187
- primary_abuser_html += "<div style='margin-top: 20px; border: 1px solid #e5e7eb; border-radius: 8px; padding: 15px; background-color: #f9fafb;'>"
188
- primary_abuser_html += "<h4 style='color: #1f2937; margin-top: 0;'>Most Common Patterns</h4>"
189
-
190
- # Get patterns from sender stats
191
- if summary['sender_stats'].get(summary['primary_abuser']):
192
- abuser_stats = summary['sender_stats'][summary['primary_abuser']]
193
- if abuser_stats.get('common_patterns'):
194
- primary_abuser_html += "<ul style='color: #1f2937;'>"
195
- for pattern, count in abuser_stats['common_patterns'][:5]: # Top 5 patterns
196
- primary_abuser_html += f"<li style='color: #1f2937; margin-bottom: 8px;'><strong>{pattern}</strong> ({count} occurrences)</li>"
197
- primary_abuser_html += "</ul>"
198
- else:
199
- primary_abuser_html += "<p style='color: #1f2937;'>No specific patterns detected.</p>"
200
-
201
- # Add emotional tones section
202
- primary_abuser_html += "<h4 style='color: #1f2937; margin-top: 20px;'>Emotional Tone Analysis</h4>"
203
- if analysis.get('emotional_tones'):
204
- primary_abuser_html += "<ul style='color: #1f2937;'>"
205
- for tone, count in analysis['emotional_tones']:
206
- if tone != "neutral":
207
- primary_abuser_html += f"<li style='color: #1f2937;'><strong>{tone}</strong> ({count} messages)</li>"
208
- primary_abuser_html += "</ul>"
209
- else:
210
- primary_abuser_html += "<p style='color: #1f2937;'>No significant emotional tones detected.</p>"
211
-
212
- # Add DARVO score
213
- darvo_level = "High" if analysis.get('darvo_score', 0) >= 0.65 else "Moderate" if analysis.get('darvo_score', 0) >= 0.25 else "Low"
214
- darvo_color = "#ef4444" if darvo_level == "High" else "#f59e0b" if darvo_level == "Moderate" else "#10b981"
215
-
216
- primary_abuser_html += f"""
217
- <h4 style='color: #1f2937; margin-top: 20px;'>DARVO Analysis</h4>
218
- <p style='color: #1f2937;'>DARVO Score: <strong style='color: {darvo_color};'>{analysis.get('darvo_score', 0):.3f}</strong> ({darvo_level})</p>
219
- <p style='color: #1f2937; font-size: 0.9em;'>DARVO (Deny, Attack, Reverse Victim & Offender) indicates attempts to shift blame and manipulate the narrative.</p>
220
- """
221
-
222
- # Add time patterns section
223
- primary_abuser_html += "<h4 style='color: #1f2937; margin-top: 20px;'>Communication Timing Patterns</h4>"
224
-
225
- # Peak hours
226
- primary_abuser_html += "<p style='color: #1f2937;'><strong>Peak Hours:</strong> "
227
- if analysis.get('peak_hours'):
228
- peak_hours_formatted = [f"{h}:00" for h in analysis['peak_hours']]
229
- primary_abuser_html += f"{', '.join(peak_hours_formatted)}</p>"
230
- else:
231
- primary_abuser_html += "No clear pattern</p>"
232
-
233
- # Peak days
234
- primary_abuser_html += "<p style='color: #1f2937;'><strong>Peak Days:</strong> "
235
- if analysis.get('peak_days'):
236
- primary_abuser_html += f"{', '.join(analysis['peak_days'])}</p>"
237
- else:
238
- primary_abuser_html += "No clear pattern</p>"
239
-
240
- primary_abuser_html += "</div>"
241
- else:
242
- primary_abuser_html += "<p style='color: #1f2937;'>No clear pattern of concerning communication from a specific sender.</p>"
243
-
244
- # Format escalation data
245
- escalation = "<h3 style='color: #1f2937;'>Escalation Analysis</h3>"
246
- if summary['escalation_data']:
247
- trend = summary['escalation_data'].get('trend_direction', 'unknown')
248
- trend_strength = summary['escalation_data'].get('trend_strength', 0)
249
- recent_avg = summary['escalation_data'].get('recent_average', 0)
250
- future_prediction = summary['escalation_data'].get('future_prediction')
251
- cyclic_pattern = summary['escalation_data'].get('cyclic_pattern', False)
252
 
253
- # Add color coding for trend direction
254
- trend_color = "#ef4444" if trend == "escalating" else "#10b981" if trend == "de-escalating" else "#6b7280"
 
255
 
256
- escalation += f"<p style='color: #1f2937;'><strong style='color: #1f2937;'>Trend Direction:</strong> <span style='color: {trend_color};'>{trend.title()}</span></p>"
257
- escalation += f"<p style='color: #1f2937;'><strong style='color: #1f2937;'>Trend Strength:</strong> {trend_strength:.2f}</p>"
258
- escalation += f"<p style='color: #1f2937;'><strong style='color: #1f2937;'>Recent Average:</strong> {recent_avg:.1f}%</p>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
 
260
- if future_prediction is not None:
261
- future_color = "#ef4444" if future_prediction > 70 else "#f59e0b" if future_prediction > 50 else "#10b981"
262
- escalation += f"<p style='color: #1f2937;'><strong style='color: #1f2937;'>7-Day Prediction:</strong> <span style='color: {future_color};'>{future_prediction:.1f}%</span></p>"
 
263
 
264
- if cyclic_pattern:
265
- cycle_period = summary['escalation_data'].get('cycle_period', 'unknown')
266
- escalation += f"""
267
- <div style='margin-top: 15px; padding: 10px; background-color: #fffbeb; border-left: 4px solid #f59e0b; border-radius: 4px;'>
268
- <p style='color: #1f2937; margin: 0;'><strong style='color: #1f2937;'>⚠️ Cyclic Abuse Pattern Detected</strong></p>
269
- <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>
270
- </div>
271
- """
272
- else:
273
- escalation += "<p style='color: #1f2937;'>No escalation data available.</p>"
274
-
275
- # Format recommendations
276
- recommendations = "<h3 style='color: #1f2937;'>Professional Recommendations</h3>"
277
- if summary['recommendations']:
278
- recommendations += "<div style='max-height: 300px; overflow-y: auto;'>"
279
- for rec in summary['recommendations']:
280
- recommendations += f"<div style='margin-bottom: 15px; padding: 15px; background: #f8f9fa; border-radius: 8px; border-left: 4px solid #3b82f6;'>"
281
- recommendations += f"<h4 style='color: #1f2937; margin-top: 0;'>{rec['title']}</h4>"
282
- recommendations += f"<p style='color: #1f2937;'>{rec['description']}</p>"
283
-
284
- if rec['actions']:
285
- recommendations += "<ul style='color: #1f2937;'>"
286
- for action in rec['actions']:
287
- recommendations += f"<li style='color: #1f2937; margin-bottom: 5px;'>{action}</li>"
288
- recommendations += "</ul>"
289
-
290
- recommendations += "</div>"
291
- recommendations += "</div>"
292
- else:
293
- recommendations += "<p style='color: #1f2937;'>No recommendations available.</p>"
294
-
295
- # Combine all sections
296
- html = f"""
297
- <div style="padding: 20px; border: 1px solid #ddd; border-radius: 10px; background-color: #ffffff;">
298
- {basic_info}
299
- {sender_stats}
300
- {primary_abuser_html}
301
- {escalation}
302
- {recommendations}
303
- </div>
304
- """
305
-
306
- return html
307
- except Exception as e:
308
- logger.error(f"Error formatting summary results: {e}")
309
- logger.error(traceback.format_exc())
310
- return f"<p style='color: #1f2937;'>Error formatting summary: {str(e)}</p>"
311
-
312
- def format_safety_plan(safety_plan):
313
- """Format safety plan for display"""
314
- try:
315
- # Convert markdown-like formatting to HTML
316
- html = safety_plan.replace("**", "<strong>").replace("**", "</strong>")
317
- html = html.replace("\n\n", "<br><br>")
318
- html = html.replace("\n•", "<br>•")
319
-
320
- # Wrap in styled div
321
- html = f"""
322
- <div style="padding: 20px; border: 1px solid #ddd; border-radius: 10px; background-color: #ffffff;">
323
- <h2 style="color: #1f2937;">Safety Plan</h2>
324
- <div style="white-space: pre-wrap; color: #1f2937;">
325
- {html}
326
- </div>
327
- </div>
328
- """
329
-
330
- return html
331
- except Exception as e:
332
- logger.error(f"Error formatting safety plan: {e}")
333
- return f"<p style='color: #1f2937;'>Error formatting safety plan: {str(e)}</p>"
334
-
335
- def handle_file_analysis(file_path):
336
- if not file_path:
337
- return (
338
- "<p style='color: #1f2937;'>Please upload a file first.</p>",
339
- None, None, None, None,
340
- "<p style='color: #1f2937;'>Please upload a file first.</p>"
341
- )
342
 
343
- try:
344
- # Analyze file
345
- results_df, summary = analyze_file(file_path)
346
-
347
- if "error" in summary:
348
- return (
349
- f"<p style='color: #1f2937;'>Error: {summary['error']}</p>",
350
- None, None, None, None,
351
- "<p style='color: #1f2937;'>Error analyzing file.</p>"
352
- )
353
-
354
- # Format summary
355
- summary_html = format_summary_results(summary)
356
-
357
- # Generate plots
358
- timeline = generate_timeline_chart(results_df)
359
- pattern_freq = generate_pattern_frequency_chart(results_df)
360
- sender_comp = generate_sender_comparison_chart(results_df)
361
- time_heatmap = generate_time_of_day_heatmap(results_df)
362
-
363
- # Format safety plan
364
- safety_plan_html = format_safety_plan(summary['safety_plan'])
365
-
366
- # Debug output
367
- logger.info(f"Summary HTML length: {len(summary_html)}")
368
- logger.info(f"Generated {len(results_df)} results")
369
- logger.info(f"Timeline chart generated: {timeline is not None}")
370
- logger.info(f"Pattern frequency chart generated: {pattern_freq is not None}")
371
- logger.info(f"Sender comparison chart generated: {sender_comp is not None}")
372
- logger.info(f"Time heatmap generated: {time_heatmap is not None}")
373
-
374
- return (
375
- summary_html,
376
- timeline,
377
- pattern_freq,
378
- sender_comp,
379
- time_heatmap,
380
- safety_plan_html
381
- )
382
- except Exception as e:
383
- logger.error(f"Error in handle_file_analysis: {e}")
384
- logger.error(traceback.format_exc())
385
- return (
386
- f"<p style='color: #1f2937;'>Error analyzing file: {str(e)}</p>",
387
- None, None, None, None,
388
- "<p style='color: #1f2937;'>Error analyzing file.</p>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389
  )
 
 
 
 
390
 
391
- def handle_message_analysis(message):
392
- if not message.strip():
393
- return "<p style='color: #1f2937;'>Please enter a message first.</p>"
 
394
 
395
  try:
396
- # Analyze message
397
- analysis = analyze_single_message(message)
 
 
 
 
 
 
 
 
 
 
398
 
399
- if "error" in analysis:
400
- return f"<p style='color: #1f2937;'>Error: {analysis['error']}</p>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401
 
402
- # Format analysis
403
- html = format_analysis_results(analysis)
 
 
 
 
404
 
405
- return html
406
  except Exception as e:
407
- logger.error(f"Error in handle_message_analysis: {e}")
408
- logger.error(traceback.format_exc())
409
- return f"<p style='color: #1f2937;'>Error analyzing message: {str(e)}</p>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
410
 
 
411
  def create_interface():
412
- """Create Gradio interface"""
413
- with gr.Blocks(title="Relationship Pattern Analyzer Pro") as demo:
414
- gr.HTML("""
415
- <div style="text-align: center; padding: 20px;">
416
- <h1>Relationship Pattern Analyzer Pro</h1>
417
- <p>Upload a chat history file to analyze communication patterns and identify concerning behaviors.</p>
418
- </div>
 
 
 
 
419
  """)
420
 
421
- with gr.Tab("Chat Analysis"):
422
- with gr.Row():
423
- with gr.Column(scale=1):
424
- file_input = gr.File(
425
- label="Upload Chat File (CSV, XLSX, or JSON)",
426
- file_types=["csv", "xlsx", "json"]
427
- )
428
- analyze_button = gr.Button("Analyze Chat History")
429
-
430
- with gr.Column(scale=2):
431
- summary_output = gr.HTML(
432
- label="Analysis Summary",
433
- value="<p style='color: #1f2937;'>Upload a file and click 'Analyze Chat History' to see results.</p>",
434
- elem_id="summary_output"
435
- )
436
-
437
- with gr.Row():
438
- with gr.Column():
439
- timeline_plot = gr.Plot(label="Abuse Score Timeline")
440
-
441
- with gr.Column():
442
- pattern_plot = gr.Plot(label="Pattern Frequency")
443
-
444
- with gr.Row():
445
- with gr.Column():
446
- sender_plot = gr.Plot(label="Sender Comparison")
447
-
448
- with gr.Column():
449
- heatmap_plot = gr.Plot(label="Time of Day Analysis")
450
-
451
- with gr.Row():
452
- safety_plan_output = gr.HTML(
453
- label="Safety Plan",
454
- value="<p style='color: #1f2937;'>Safety plan will appear here after analysis.</p>"
455
  )
456
-
457
- with gr.Tab("Single Message Analysis"):
458
- with gr.Row():
459
- with gr.Column(scale=1):
460
- message_input = gr.Textbox(
461
- label="Enter Message",
462
- placeholder="Type or paste a message to analyze...",
463
- lines=5
464
- )
465
- analyze_message_button = gr.Button("Analyze Message")
466
 
467
- with gr.Column(scale=2):
468
- message_analysis_output = gr.HTML(
469
- label="Message Analysis",
470
- value="<p style='color: #1f2937;'>Enter a message and click 'Analyze Message' to see results.</p>"
471
- )
472
-
473
- with gr.Tab("About"):
474
- gr.HTML("""
475
- <div style="padding: 20px;">
476
- <h2>About Relationship Pattern Analyzer Pro</h2>
477
- <p>This tool helps identify potentially concerning communication patterns in relationships.</p>
478
 
479
- <h3>Features</h3>
480
- <ul>
481
- <li><strong>Chat Analysis:</strong> Upload an entire chat history to analyze communication patterns over time</li>
482
- <li><strong>Temporal Analysis:</strong> Detect escalation patterns, cycles of abuse, and time-based trends</li>
483
- <li><strong>Boundary Health:</strong> Assess whether messages demonstrate healthy or unhealthy relational boundaries</li>
484
- <li><strong>Intent Analysis:</strong> Identify manipulative tactics and emotional tone</li>
485
- <li><strong>Safety Planning:</strong> Get personalized safety recommendations based on detected patterns</li>
486
- <li><strong>Professional Insights:</strong> View recommendations for professional intervention</li>
487
- </ul>
488
 
489
- <h3>Privacy Notice</h3>
490
- <p>Your data is processed locally and is not stored or shared. This tool is for educational purposes only and not a substitute for professional counseling or legal advice.</p>
 
 
 
491
 
492
- <h3>Emergency Resources</h3>
493
- <p><strong>National Domestic Violence Hotline:</strong> 1-800-799-7233</p>
494
- <p><strong>Crisis Text Line:</strong> Text START to 88788</p>
495
- <p><strong>Emergency:</strong> Call 911</p>
496
- </div>
497
- """)
498
-
499
- # Connect event handlers
500
- analyze_button.click(
501
- handle_file_analysis,
502
- inputs=[file_input],
503
- outputs=[
504
- summary_output,
505
- timeline_plot,
506
- pattern_plot,
507
- sender_plot,
508
- heatmap_plot,
509
- safety_plan_output
510
- ]
511
- )
512
 
513
- analyze_message_button.click(
514
- handle_message_analysis,
515
- inputs=[message_input],
516
- outputs=[message_analysis_output]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
517
  )
 
 
 
 
 
 
 
 
 
 
 
 
518
 
519
- return demo
520
 
521
  if __name__ == "__main__":
522
- demo = create_interface()
523
- demo.launch(server_name="0.0.0.0", server_port=7860)
524
-
 
1
  import gradio as gr
2
  import pandas as pd
3
+ import torch
4
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification, pipeline
5
  import numpy as np
 
 
 
 
6
  from datetime import datetime
7
+ import io
8
+ import json
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
+ class CommunicationAnalyzer:
11
+ def __init__(self):
12
+ self.load_models()
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
+ def load_models(self):
15
+ """Load all the analysis models"""
16
+ try:
17
+ # DARVO Detection
18
+ self.darvo_tokenizer = AutoTokenizer.from_pretrained("SamanthaStorm/tether-darvo-regressor-v1")
19
+ self.darvo_model = AutoModelForSequenceClassification.from_pretrained("SamanthaStorm/tether-darvo-regressor-v1")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
+ # Sentiment Analysis
22
+ self.sentiment_pipeline = pipeline("text-classification", model="SamanthaStorm/tether-sentiment-v3")
 
 
 
 
23
 
24
+ # Multilabel Classification
25
+ self.multilabel_pipeline = pipeline("text-classification", model="SamanthaStorm/tether-multilabel-v6")
 
 
 
 
 
 
 
26
 
27
+ # Healthy Boundary Detection
28
+ self.boundary_pipeline = pipeline("text-classification", model="SamanthaStorm/healthy-boundary-predictor")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
+ # Fallacy Detection
31
+ self.fallacy_tokenizer = AutoTokenizer.from_pretrained("SamanthaStorm/fallacyfinder")
32
+ self.fallacy_model = AutoModelForSequenceClassification.from_pretrained("SamanthaStorm/fallacyfinder")
33
 
34
+ except Exception as e:
35
+ print(f"Error loading models: {e}")
36
+
37
+ def analyze_darvo(self, text):
38
+ """Analyze DARVO tactics in text"""
39
+ try:
40
+ inputs = self.darvo_tokenizer(text, return_tensors="pt", truncation=True, padding=True, max_length=512)
41
+ with torch.no_grad():
42
+ outputs = self.darvo_model(**inputs)
43
+ darvo_score = torch.sigmoid(outputs.logits).item()
44
+ return darvo_score
45
+ except:
46
+ return 0.0
47
+
48
+ def analyze_fallacy(self, text):
49
+ """Detect logical fallacies in text"""
50
+ try:
51
+ inputs = self.fallacy_tokenizer(text, return_tensors="pt", truncation=True, padding=True, max_length=512)
52
+ with torch.no_grad():
53
+ outputs = self.fallacy_model(**inputs)
54
+ predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)
55
+ predicted_class_id = predictions.argmax().item()
56
+ confidence = predictions.max().item()
57
 
58
+ if hasattr(self.fallacy_model.config, 'id2label'):
59
+ predicted_label = self.fallacy_model.config.id2label[predicted_class_id]
60
+ else:
61
+ predicted_label = f"Fallacy_Type_{predicted_class_id}"
62
 
63
+ return predicted_label, confidence
64
+ except:
65
+ return "No Fallacy", 0.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
+ def analyze_message(self, text):
68
+ """Comprehensive analysis of a single message"""
69
+ if not text or len(text.strip()) < 3:
70
+ return {}
71
+
72
+ analysis = {}
73
+
74
+ # DARVO Analysis
75
+ analysis['darvo_score'] = self.analyze_darvo(text)
76
+
77
+ # Sentiment Analysis
78
+ try:
79
+ sentiment_result = self.sentiment_pipeline(text)
80
+ analysis['sentiment'] = sentiment_result[0]['label']
81
+ analysis['sentiment_confidence'] = sentiment_result[0]['score']
82
+ except:
83
+ analysis['sentiment'] = 'NEUTRAL'
84
+ analysis['sentiment_confidence'] = 0.5
85
+
86
+ # Multilabel Analysis
87
+ try:
88
+ multilabel_result = self.multilabel_pipeline(text)
89
+ analysis['categories'] = multilabel_result
90
+ except:
91
+ analysis['categories'] = []
92
+
93
+ # Boundary Analysis
94
+ try:
95
+ boundary_result = self.boundary_pipeline(text)
96
+ analysis['boundary_type'] = boundary_result[0]['label']
97
+ analysis['boundary_confidence'] = boundary_result[0]['score']
98
+ except:
99
+ analysis['boundary_type'] = 'UNCLEAR'
100
+ analysis['boundary_confidence'] = 0.5
101
+
102
+ # Fallacy Analysis
103
+ fallacy_type, fallacy_confidence = self.analyze_fallacy(text)
104
+ analysis['fallacy_type'] = fallacy_type
105
+ analysis['fallacy_confidence'] = fallacy_confidence
106
+
107
+ return analysis
108
+
109
+ def calculate_abuse_indicators(self, person_data):
110
+ """Calculate abuse indicators for a person based on their messages"""
111
+ if not person_data:
112
+ return {}
113
+
114
+ # Aggregate scores
115
+ darvo_scores = [msg.get('darvo_score', 0) for msg in person_data]
116
+ negative_sentiments = sum(1 for msg in person_data if msg.get('sentiment') == 'NEGATIVE')
117
+ fallacy_count = sum(1 for msg in person_data if msg.get('fallacy_confidence', 0) > 0.7)
118
+
119
+ total_messages = len(person_data)
120
+
121
+ indicators = {
122
+ 'avg_darvo_score': np.mean(darvo_scores) if darvo_scores else 0,
123
+ 'max_darvo_score': max(darvo_scores) if darvo_scores else 0,
124
+ 'negative_sentiment_ratio': negative_sentiments / total_messages if total_messages > 0 else 0,
125
+ 'fallacy_ratio': fallacy_count / total_messages if total_messages > 0 else 0,
126
+ 'total_messages': total_messages,
127
+ 'high_darvo_messages': sum(1 for score in darvo_scores if score > 0.7)
128
+ }
129
+
130
+ # Calculate overall abuse likelihood
131
+ abuse_score = (
132
+ indicators['avg_darvo_score'] * 0.4 +
133
+ indicators['negative_sentiment_ratio'] * 0.2 +
134
+ indicators['fallacy_ratio'] * 0.2 +
135
+ (indicators['high_darvo_messages'] / total_messages if total_messages > 0 else 0) * 0.2
136
  )
137
+
138
+ indicators['abuse_likelihood'] = min(abuse_score, 1.0)
139
+
140
+ return indicators
141
 
142
+ def analyze_csv_file(file, message_col, sender_col, report_type):
143
+ """Main function to analyze uploaded CSV file"""
144
+ if file is None:
145
+ return "Please upload a CSV file.", "", ""
146
 
147
  try:
148
+ # Read CSV file
149
+ df = pd.read_csv(file.name)
150
+
151
+ if message_col not in df.columns or sender_col not in df.columns:
152
+ return f"Columns '{message_col}' or '{sender_col}' not found in CSV.", "", ""
153
+
154
+ # Initialize analyzer
155
+ analyzer = CommunicationAnalyzer()
156
+
157
+ # Analyze each message
158
+ results = []
159
+ person_analyses = {}
160
 
161
+ for idx, row in df.iterrows():
162
+ message = str(row[message_col])
163
+ sender = str(row[sender_col])
164
+
165
+ if sender not in person_analyses:
166
+ person_analyses[sender] = []
167
+
168
+ analysis = analyzer.analyze_message(message)
169
+ analysis['message'] = message
170
+ analysis['sender'] = sender
171
+ analysis['message_id'] = idx
172
+
173
+ results.append(analysis)
174
+ person_analyses[sender].append(analysis)
175
+
176
+ # Calculate abuse indicators for each person
177
+ person_indicators = {}
178
+ for person, messages in person_analyses.items():
179
+ person_indicators[person] = analyzer.calculate_abuse_indicators(messages)
180
 
181
+ # Generate reports
182
+ summary_report = generate_summary_report(person_indicators, results, report_type)
183
+ detailed_report = generate_detailed_report(results, person_indicators, report_type)
184
+ downloadable_report = generate_downloadable_report(results, person_indicators, report_type)
185
+
186
+ return summary_report, detailed_report, downloadable_report
187
 
 
188
  except Exception as e:
189
+ return f"Error processing file: {str(e)}", "", ""
190
+
191
+ def generate_summary_report(person_indicators, results, report_type):
192
+ """Generate summary report identifying potential abuser"""
193
+
194
+ # Sort people by abuse likelihood
195
+ sorted_people = sorted(person_indicators.items(), key=lambda x: x[1]['abuse_likelihood'], reverse=True)
196
+
197
+ report = f"# Communication Analysis Summary Report\n"
198
+ report += f"**Report Type:** {report_type}\n"
199
+ report += f"**Analysis Date:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
200
+ report += f"**Total Messages Analyzed:** {len(results)}\n\n"
201
+
202
+ report += "## Abuse Likelihood Rankings\n\n"
203
+
204
+ for i, (person, indicators) in enumerate(sorted_people, 1):
205
+ risk_level = "HIGH" if indicators['abuse_likelihood'] > 0.7 else "MEDIUM" if indicators['abuse_likelihood'] > 0.4 else "LOW"
206
+
207
+ report += f"### {i}. {person}\n"
208
+ report += f"- **Risk Level:** {risk_level}\n"
209
+ report += f"- **Abuse Likelihood Score:** {indicators['abuse_likelihood']:.3f}\n"
210
+ report += f"- **Average DARVO Score:** {indicators['avg_darvo_score']:.3f}\n"
211
+ report += f"- **Negative Sentiment Ratio:** {indicators['negative_sentiment_ratio']:.3f}\n"
212
+ report += f"- **Fallacy Usage Ratio:** {indicators['fallacy_ratio']:.3f}\n"
213
+ report += f"- **Total Messages:** {indicators['total_messages']}\n\n"
214
+
215
+ if sorted_people:
216
+ primary_concern = sorted_people[0]
217
+ if primary_concern[1]['abuse_likelihood'] > 0.6:
218
+ report += f"## ⚠️ Primary Concern\n"
219
+ report += f"**{primary_concern[0]}** shows the highest indicators of potentially abusive communication patterns.\n\n"
220
+
221
+ return report
222
+
223
+ def generate_detailed_report(results, person_indicators, report_type):
224
+ """Generate detailed analysis report"""
225
+
226
+ report = f"# Detailed Communication Analysis\n\n"
227
+
228
+ # High-risk messages
229
+ high_risk_messages = [r for r in results if r.get('darvo_score', 0) > 0.7]
230
+
231
+ if high_risk_messages:
232
+ report += "## High-Risk Messages (DARVO Score > 0.7)\n\n"
233
+ for msg in high_risk_messages[:10]: # Limit to top 10
234
+ report += f"**Sender:** {msg['sender']}\n"
235
+ report += f"**Message:** \"{msg['message'][:200]}{'...' if len(msg['message']) > 200 else ''}\"\n"
236
+ report += f"**DARVO Score:** {msg['darvo_score']:.3f}\n"
237
+ report += f"**Sentiment:** {msg.get('sentiment', 'N/A')}\n"
238
+ report += f"**Fallacy Type:** {msg.get('fallacy_type', 'N/A')}\n\n"
239
+
240
+ # Pattern analysis
241
+ report += "## Communication Patterns\n\n"
242
+ for person, indicators in person_indicators.items():
243
+ if indicators['abuse_likelihood'] > 0.5:
244
+ report += f"### {person} - Pattern Analysis\n"
245
+ report += f"- Frequently uses DARVO tactics ({indicators['high_darvo_messages']} high-scoring messages)\n"
246
+ report += f"- {indicators['negative_sentiment_ratio']*100:.1f}% of messages have negative sentiment\n"
247
+ report += f"- Uses logical fallacies in {indicators['fallacy_ratio']*100:.1f}% of messages\n\n"
248
+
249
+ return report
250
+
251
+ def generate_downloadable_report(results, person_indicators, report_type):
252
+ """Generate downloadable comprehensive report"""
253
+
254
+ report_data = {
255
+ "analysis_metadata": {
256
+ "report_type": report_type,
257
+ "analysis_date": datetime.now().isoformat(),
258
+ "total_messages": len(results),
259
+ "total_participants": len(person_indicators)
260
+ },
261
+ "person_indicators": person_indicators,
262
+ "high_risk_messages": [r for r in results if r.get('darvo_score', 0) > 0.6],
263
+ "summary": {
264
+ "primary_concern": max(person_indicators.items(), key=lambda x: x[1]['abuse_likelihood']) if person_indicators else None
265
+ }
266
+ }
267
+
268
+ # Convert to JSON string for download
269
+ return json.dumps(report_data, indent=2, default=str)
270
 
271
+ # Create Gradio interface
272
  def create_interface():
273
+ with gr.Blocks(title="Communication Analysis Tool", theme=gr.themes.Soft()) as interface:
274
+ gr.Markdown("""
275
+ # 🔍 Communication Analysis Tool
276
+
277
+ This tool analyzes communication patterns to identify potentially abusive behavior using multiple AI models:
278
+ - **DARVO Detection**: Identifies Deny, Attack, Reverse Victim & Offender tactics
279
+ - **Sentiment Analysis**: Analyzes emotional tone
280
+ - **Fallacy Detection**: Identifies logical fallacies
281
+ - **Boundary Analysis**: Detects healthy vs unhealthy boundaries
282
+
283
+ **For use by therapists, counselors, and law enforcement professionals.**
284
  """)
285
 
286
+ with gr.Row():
287
+ with gr.Column():
288
+ file_input = gr.File(
289
+ label="Upload CSV File",
290
+ file_types=[".csv"],
291
+ type="filepath"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
  )
 
 
 
 
 
 
 
 
 
 
293
 
294
+ message_col = gr.Textbox(
295
+ label="Message Column Name",
296
+ value="message",
297
+ placeholder="Enter the column name containing messages"
298
+ )
 
 
 
 
 
 
299
 
300
+ sender_col = gr.Textbox(
301
+ label="Sender Column Name",
302
+ value="sender",
303
+ placeholder="Enter the column name containing sender names"
304
+ )
 
 
 
 
305
 
306
+ report_type = gr.Radio(
307
+ choices=["Therapeutic Assessment", "Legal Documentation", "General Analysis"],
308
+ label="Report Type",
309
+ value="General Analysis"
310
+ )
311
 
312
+ analyze_btn = gr.Button("Analyze Communication", variant="primary")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
 
314
+ with gr.Row():
315
+ with gr.Column():
316
+ summary_output = gr.Markdown(label="Summary Report")
317
+
318
+ with gr.Column():
319
+ detailed_output = gr.Markdown(label="Detailed Analysis")
320
+
321
+ download_output = gr.File(label="Download Complete Report (JSON)")
322
+
323
+ def process_and_create_download(file, message_col, sender_col, report_type):
324
+ summary, detailed, report_data = analyze_csv_file(file, message_col, sender_col, report_type)
325
+
326
+ # Create downloadable file
327
+ if report_data:
328
+ filename = f"communication_analysis_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
329
+ with open(filename, 'w') as f:
330
+ f.write(report_data)
331
+ return summary, detailed, filename
332
+
333
+ return summary, detailed, None
334
+
335
+ analyze_btn.click(
336
+ fn=process_and_create_download,
337
+ inputs=[file_input, message_col, sender_col, report_type],
338
+ outputs=[summary_output, detailed_output, download_output]
339
  )
340
+
341
+ gr.Markdown("""
342
+ ## Instructions:
343
+ 1. Upload a CSV file containing communication data
344
+ 2. Specify the column names for messages and senders
345
+ 3. Choose the appropriate report type
346
+ 4. Click "Analyze Communication" to generate reports
347
+ 5. Download the complete analysis for your records
348
+
349
+ **Note:** This tool is designed to assist professionals in identifying concerning communication patterns.
350
+ Results should be interpreted by qualified professionals and used as part of a comprehensive assessment.
351
+ """)
352
 
353
+ return interface
354
 
355
  if __name__ == "__main__":
356
+ interface = create_interface()
357
+ interface.launch()