SamanthaStorm commited on
Commit
96ba2c0
·
verified ·
1 Parent(s): 7580a86

Update analyzer.py

Browse files
Files changed (1) hide show
  1. analyzer.py +207 -120
analyzer.py CHANGED
@@ -188,136 +188,223 @@ class MessageAnalyzer:
188
  }
189
 
190
  def analyze_chat_history(self, df):
191
- """Analyze entire chat history"""
192
- from utils import detect_escalation_patterns, generate_safety_plan, generate_professional_recommendations
 
 
 
 
 
 
193
 
194
- logger.info(f"Analyzing chat history with {len(df)} messages")
 
 
 
 
 
 
 
195
 
196
- try:
197
- # Create results dataframe
198
- results_df = df.copy()
199
-
200
- # Add analysis columns
201
- results_df['abuse_score'] = 0.0
202
- results_df['detected_patterns'] = [[] for _ in range(len(results_df))]
203
- results_df['sentiment'] = "neutral"
204
- results_df['darvo_score'] = 0.0
205
- results_df['emotional_tone'] = "neutral"
206
- results_df['boundary_health'] = "unknown"
207
- results_df['risk_level'] = "Low"
208
-
209
- # Analyze each message
210
- for i, row in results_df.iterrows():
211
- analysis = self.analyze_message(row['message'])
212
-
213
- # Update dataframe with analysis results
214
- results_df.at[i, 'abuse_score'] = analysis['abuse_score']
215
- results_df.at[i, 'detected_patterns'] = analysis['detected_patterns']
216
- results_df.at[i, 'sentiment'] = analysis['sentiment']
217
- results_df.at[i, 'darvo_score'] = analysis['darvo_score']
218
- results_df.at[i, 'emotional_tone'] = analysis['emotional_tone']
219
- results_df.at[i, 'boundary_health'] = analysis['boundary_assessment']['assessment']
220
- results_df.at[i, 'risk_level'] = analysis['risk_level']
221
-
222
- # Calculate sender statistics
223
- sender_stats = {}
224
- for sender in results_df['sender'].unique():
225
- sender_df = results_df[results_df['sender'] == sender]
226
-
227
- # Calculate key metrics
228
- avg_abuse = sender_df['abuse_score'].mean()
229
- max_abuse = sender_df['abuse_score'].max()
230
-
231
- # Get most common patterns
232
- all_patterns = []
233
- for patterns in sender_df['detected_patterns']:
234
- if patterns:
235
- all_patterns.extend(patterns)
236
-
237
- pattern_counts = Counter(all_patterns)
238
- most_common = pattern_counts.most_common(3)
239
-
240
- # Calculate percentage of abusive messages
241
- abusive_count = len(sender_df[sender_df['abuse_score'] >= 50])
242
- abusive_pct = (abusive_count / len(sender_df)) * 100 if len(sender_df) > 0 else 0
243
-
244
- # Store stats
245
- sender_stats[sender] = {
246
- 'message_count': len(sender_df),
247
- 'avg_abuse_score': avg_abuse,
248
- 'max_abuse_score': max_abuse,
249
- 'abusive_message_count': abusive_count,
250
- 'abusive_message_pct': abusive_pct,
251
- 'common_patterns': most_common
252
- }
253
-
254
- # Detect escalation patterns
255
- escalation_data = detect_escalation_patterns(results_df)
256
 
257
- # Determine overall risk level
258
- if results_df['risk_level'].isin(['Critical']).any():
259
- overall_risk = "Critical"
260
- elif results_df['risk_level'].isin(['High']).any():
261
- overall_risk = "High"
262
- elif results_df['risk_level'].isin(['Moderate']).any():
263
- overall_risk = "Moderate"
264
- else:
265
- overall_risk = "Low"
266
 
267
- # Generate safety plan
268
  all_patterns = []
269
- for patterns in results_df['detected_patterns']:
270
  if patterns:
271
  all_patterns.extend(patterns)
272
 
273
- safety_plan = generate_safety_plan(overall_risk, all_patterns, escalation_data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
 
275
- # Generate professional recommendations
276
- recommendations = generate_professional_recommendations(results_df, escalation_data, overall_risk)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
 
278
- # Identify primary abuser (if any)
279
- primary_abuser = None
280
- max_abusive_pct = 0
281
-
282
- for sender, stats in sender_stats.items():
283
- if stats['message_count'] >= 5 and stats['abusive_message_pct'] > max_abusive_pct:
284
- max_abusive_pct = stats['abusive_message_pct']
285
- primary_abuser = sender
286
-
287
- # Only identify primary abuser if they have significant abusive content
288
- if max_abusive_pct < 20:
289
- primary_abuser = None
290
-
291
- # Prepare summary
292
- summary = {
293
- 'message_count': len(results_df),
294
- 'date_range': {
295
- 'start': results_df['timestamp'].min().strftime('%Y-%m-%d'),
296
- 'end': results_df['timestamp'].max().strftime('%Y-%m-%d')
297
- },
298
- 'overall_risk_level': overall_risk,
299
- 'sender_stats': sender_stats,
300
- 'primary_abuser': primary_abuser,
301
- 'escalation_data': escalation_data,
302
- 'safety_plan': safety_plan,
303
- 'recommendations': recommendations
304
- }
305
 
306
- return results_df, summary
 
 
307
 
308
- except Exception as e:
309
- logger.error(f"Error in analyze_chat_history: {e}")
310
- logger.error(traceback.format_exc())
311
- return df, {
312
- 'message_count': len(df),
313
- 'date_range': {
314
- 'start': df['timestamp'].min().strftime('%Y-%m-%d') if not df.empty else 'unknown',
315
- 'end': df['timestamp'].max().strftime('%Y-%m-%d') if not df.empty else 'unknown'
316
- },
317
- 'overall_risk_level': "Unknown",
318
- 'sender_stats': {},
319
- 'primary_abuser': None,
320
- 'escalation_data': {},
321
- 'safety_plan': "Error generating safety plan.",
322
- 'recommendations': []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  }
189
 
190
  def analyze_chat_history(self, df):
191
+ """Analyze entire chat history"""
192
+ from utils import detect_escalation_patterns, generate_safety_plan, generate_professional_recommendations
193
+
194
+ logger.info(f"Analyzing chat history with {len(df)} messages")
195
+
196
+ try:
197
+ # Create results dataframe
198
+ results_df = df.copy()
199
 
200
+ # Add analysis columns
201
+ results_df['abuse_score'] = 0.0
202
+ results_df['detected_patterns'] = [[] for _ in range(len(results_df))]
203
+ results_df['sentiment'] = "neutral"
204
+ results_df['darvo_score'] = 0.0
205
+ results_df['emotional_tone'] = "neutral"
206
+ results_df['boundary_health'] = "unknown"
207
+ results_df['risk_level'] = "Low"
208
 
209
+ # Analyze each message
210
+ for i, row in results_df.iterrows():
211
+ analysis = self.analyze_message(row['message'])
212
+
213
+ # Update dataframe with analysis results
214
+ results_df.at[i, 'abuse_score'] = analysis['abuse_score']
215
+ results_df.at[i, 'detected_patterns'] = analysis['detected_patterns']
216
+ results_df.at[i, 'sentiment'] = analysis['sentiment']
217
+ results_df.at[i, 'darvo_score'] = analysis['darvo_score']
218
+ results_df.at[i, 'emotional_tone'] = analysis['emotional_tone']
219
+ results_df.at[i, 'boundary_health'] = analysis['boundary_assessment']['assessment']
220
+ results_df.at[i, 'risk_level'] = analysis['risk_level']
221
+
222
+ # Calculate sender statistics
223
+ sender_stats = {}
224
+ for sender in results_df['sender'].unique():
225
+ sender_df = results_df[results_df['sender'] == sender]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
 
227
+ # Calculate key metrics
228
+ avg_abuse = sender_df['abuse_score'].mean()
229
+ max_abuse = sender_df['abuse_score'].max()
 
 
 
 
 
 
230
 
231
+ # Get most common patterns
232
  all_patterns = []
233
+ for patterns in sender_df['detected_patterns']:
234
  if patterns:
235
  all_patterns.extend(patterns)
236
 
237
+ pattern_counts = Counter(all_patterns)
238
+ most_common = pattern_counts.most_common(5) # Get top 5 patterns
239
+
240
+ # Calculate percentage of abusive messages
241
+ abusive_count = len(sender_df[sender_df['abuse_score'] >= 50])
242
+ abusive_pct = (abusive_count / len(sender_df)) * 100 if len(sender_df) > 0 else 0
243
+
244
+ # Calculate emotional tone distribution
245
+ tone_counts = Counter(sender_df['emotional_tone'])
246
+ most_common_tones = tone_counts.most_common(3) # Get top 3 emotional tones
247
+
248
+ # Calculate DARVO score statistics
249
+ avg_darvo = sender_df['darvo_score'].mean()
250
+ high_darvo_count = len(sender_df[sender_df['darvo_score'] >= 0.65])
251
+ high_darvo_pct = (high_darvo_count / len(sender_df)) * 100 if len(sender_df) > 0 else 0
252
+
253
+ # Calculate risk level distribution
254
+ risk_counts = Counter(sender_df['risk_level'])
255
+
256
+ # Store stats
257
+ sender_stats[sender] = {
258
+ 'message_count': len(sender_df),
259
+ 'avg_abuse_score': avg_abuse,
260
+ 'max_abuse_score': max_abuse,
261
+ 'abusive_message_count': abusive_count,
262
+ 'abusive_message_pct': abusive_pct,
263
+ 'common_patterns': most_common,
264
+ 'emotional_tones': most_common_tones,
265
+ 'avg_darvo_score': avg_darvo,
266
+ 'high_darvo_count': high_darvo_count,
267
+ 'high_darvo_pct': high_darvo_pct,
268
+ 'risk_levels': risk_counts
269
+ }
270
+
271
+ # Detect escalation patterns
272
+ escalation_data = detect_escalation_patterns(results_df)
273
+
274
+ # Determine overall risk level
275
+ if results_df['risk_level'].isin(['Critical']).any():
276
+ overall_risk = "Critical"
277
+ elif results_df['risk_level'].isin(['High']).any():
278
+ overall_risk = "High"
279
+ elif results_df['risk_level'].isin(['Moderate']).any():
280
+ overall_risk = "Moderate"
281
+ else:
282
+ overall_risk = "Low"
283
+
284
+ # Generate safety plan
285
+ all_patterns = []
286
+ for patterns in results_df['detected_patterns']:
287
+ if patterns:
288
+ all_patterns.extend(patterns)
289
+
290
+ safety_plan = generate_safety_plan(overall_risk, all_patterns, escalation_data)
291
+
292
+ # Identify primary abuser (if any)
293
+ primary_abuser = None
294
+ max_abusive_pct = 0
295
+
296
+ for sender, stats in sender_stats.items():
297
+ if stats['message_count'] >= 5 and stats['abusive_message_pct'] > max_abusive_pct:
298
+ max_abusive_pct = stats['abusive_message_pct']
299
+ primary_abuser = sender
300
+
301
+ # Only identify primary abuser if they have significant abusive content
302
+ if max_abusive_pct < 20:
303
+ primary_abuser = None
304
 
305
+ # Create detailed primary abuser analysis
306
+ primary_abuser_analysis = None
307
+ if primary_abuser:
308
+ abuser_df = results_df[results_df['sender'] == primary_abuser]
309
+
310
+ # Get example messages for each pattern
311
+ pattern_examples = {}
312
+ for pattern in set([p for patterns in abuser_df['detected_patterns'] for p in patterns]):
313
+ # Find messages with this pattern
314
+ pattern_msgs = abuser_df[abuser_df['detected_patterns'].apply(lambda x: pattern in x)]
315
+ if not pattern_msgs.empty:
316
+ # Get highest scoring example
317
+ example = pattern_msgs.iloc[pattern_msgs['abuse_score'].argmax()]
318
+ pattern_examples[pattern] = {
319
+ 'message': example['message'],
320
+ 'abuse_score': example['abuse_score'],
321
+ 'timestamp': example['timestamp'].strftime('%Y-%m-%d %H:%M')
322
+ }
323
 
324
+ # Get time-based patterns
325
+ abuser_df['hour'] = abuser_df['timestamp'].dt.hour
326
+ abuser_df['day_of_week'] = abuser_df['timestamp'].dt.day_name()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
327
 
328
+ # Find peak abuse hours
329
+ hour_abuse = abuser_df.groupby('hour')['abuse_score'].mean()
330
+ peak_hours = hour_abuse.nlargest(3).index.tolist() if not hour_abuse.empty else []
331
 
332
+ # Find peak abuse days
333
+ day_abuse = abuser_df.groupby('day_of_week')['abuse_score'].mean()
334
+ peak_days = day_abuse.nlargest(3).index.tolist() if not day_abuse.empty else []
335
+
336
+ # Calculate response patterns (how they respond to the other person)
337
+ response_patterns = []
338
+ if len(results_df) >= 10: # Need enough messages for meaningful analysis
339
+ # Sort by timestamp
340
+ sorted_df = results_df.sort_values('timestamp')
341
+
342
+ # Find messages where primary abuser responds to other person
343
+ for i in range(1, len(sorted_df)):
344
+ if (sorted_df.iloc[i]['sender'] == primary_abuser and
345
+ sorted_df.iloc[i-1]['sender'] != primary_abuser):
346
+ # This is a response
347
+ trigger_msg = sorted_df.iloc[i-1]
348
+ response_msg = sorted_df.iloc[i]
349
+
350
+ if response_msg['abuse_score'] >= 50: # Only track abusive responses
351
+ response_patterns.append({
352
+ 'trigger': trigger_msg['message'],
353
+ 'response': response_msg['message'],
354
+ 'response_patterns': response_msg['detected_patterns'],
355
+ 'response_score': response_msg['abuse_score'],
356
+ 'timestamp': response_msg['timestamp'].strftime('%Y-%m-%d %H:%M')
357
+ })
358
+
359
+ # Store the detailed analysis
360
+ primary_abuser_analysis = {
361
+ 'sender': primary_abuser,
362
+ 'message_count': len(abuser_df),
363
+ 'avg_abuse_score': abuser_df['abuse_score'].mean(),
364
+ 'pattern_examples': pattern_examples,
365
+ 'peak_hours': peak_hours,
366
+ 'peak_days': peak_days,
367
+ 'response_patterns': response_patterns[:5], # Limit to top 5
368
+ 'emotional_tones': sender_stats[primary_abuser]['emotional_tones'],
369
+ 'darvo_score': sender_stats[primary_abuser]['avg_darvo_score']
370
  }
371
+
372
+ # Generate professional recommendations
373
+ recommendations = generate_professional_recommendations(results_df, escalation_data, overall_risk)
374
+
375
+ # Prepare summary
376
+ summary = {
377
+ 'message_count': len(results_df),
378
+ 'date_range': {
379
+ 'start': results_df['timestamp'].min().strftime('%Y-%m-%d'),
380
+ 'end': results_df['timestamp'].max().strftime('%Y-%m-%d')
381
+ },
382
+ 'overall_risk_level': overall_risk,
383
+ 'sender_stats': sender_stats,
384
+ 'primary_abuser': primary_abuser,
385
+ 'primary_abuser_analysis': primary_abuser_analysis, # Add detailed analysis
386
+ 'escalation_data': escalation_data,
387
+ 'safety_plan': safety_plan,
388
+ 'recommendations': recommendations
389
+ }
390
+
391
+ return results_df, summary
392
+
393
+ except Exception as e:
394
+ logger.error(f"Error in analyze_chat_history: {e}")
395
+ logger.error(traceback.format_exc())
396
+ return df, {
397
+ 'message_count': len(df),
398
+ 'date_range': {
399
+ 'start': df['timestamp'].min().strftime('%Y-%m-%d') if not df.empty else 'unknown',
400
+ 'end': df['timestamp'].max().strftime('%Y-%m-%d') if not df.empty else 'unknown'
401
+ },
402
+ 'overall_risk_level': "Unknown",
403
+ 'sender_stats': {},
404
+ 'primary_abuser': None,
405
+ 'primary_abuser_analysis': None,
406
+ 'escalation_data': {},
407
+ 'safety_plan': "Error generating safety plan.",
408
+ 'recommendations': []
409
+ }
410
+