#!/usr/bin/env python3 """ User Interaction Analysis Dashboard A comprehensive UI for viewing and analyzing user interactions across Intercom chats and JustCall meetings with priority-based filtering. """ import gradio as gr from pymongo import MongoClient from typing import List, Dict, Any, Tuple, Optional import pandas as pd from loguru import logger # MongoDB Configuration MONGODB_URI = "mongodb+srv://contextdb:HOqIgSH01CoEiMb1@cluster0.d9cmff.mongodb.net/" DATABASE_NAME = "second_brain_course" COLLECTION_NAME = "user_interaction_analyses" class UserInteractionDashboard: """Dashboard for user interaction analyses.""" def __init__(self): """Initialize dashboard with MongoDB connection.""" self.client = MongoClient(MONGODB_URI) self.db = self.client[DATABASE_NAME] self.collection = self.db[COLLECTION_NAME] logger.info(f"Connected to MongoDB: {DATABASE_NAME}.{COLLECTION_NAME}") def get_summary_stats(self) -> Tuple[int, int, int, int, int, int]: """Get summary statistics for the dashboard.""" total_users = self.collection.count_documents({}) # Count by priority high_priority = self.collection.count_documents({"priority_level": "high"}) medium_priority = self.collection.count_documents({"priority_level": "medium"}) low_priority = self.collection.count_documents({"priority_level": "low"}) # Aggregate total conversations and meetings pipeline = [ { "$group": { "_id": None, "total_conversations": {"$sum": "$total_conversations"}, "total_meetings": {"$sum": "$total_meetings"} } } ] agg_result = list(self.collection.aggregate(pipeline)) total_conversations = agg_result[0]["total_conversations"] if agg_result else 0 total_meetings = agg_result[0]["total_meetings"] if agg_result else 0 return ( total_users, total_conversations, total_meetings, high_priority, medium_priority, low_priority ) def get_users_data(self, priority_filter: Optional[str] = None) -> pd.DataFrame: """Get user data for table display with optional priority filter.""" # Build query query = {} if priority_filter and priority_filter != "All": query["priority_level"] = priority_filter.lower() # Fetch documents users = list(self.collection.find(query)) if not users: return pd.DataFrame(columns=[ "User ID", "Conversations", "Meetings", "Conv Key Findings", "Meeting Key Findings", "Priority" ]) # Transform to table format table_data = [] for user in users: user_id = user.get("user_id", "") # Get conversation IDs conv_ids = user.get("conversation_ids", []) conv_ids_str = ", ".join(conv_ids[:3]) # Show first 3 if len(conv_ids) > 3: conv_ids_str += f" (+{len(conv_ids) - 3} more)" # Get meeting IDs meeting_ids = user.get("meeting_ids", []) meeting_ids_str = ", ".join(meeting_ids[:3]) # Show first 3 if len(meeting_ids) > 3: meeting_ids_str += f" (+{len(meeting_ids) - 3} more)" # Get key findings from conversation level conv_insights = user.get("conversation_level_insights", {}) conv_findings = conv_insights.get("aggregated_marketing_insights", {}).get("key_findings", []) conv_findings_str = f"{len(conv_findings)} findings" # Get key findings from meeting level meeting_insights = user.get("meeting_level_insights", {}) meeting_findings = meeting_insights.get("aggregated_marketing_insights", {}).get("key_findings", []) meeting_findings_str = f"{len(meeting_findings)} findings" priority = user.get("priority_level", "unknown").upper() table_data.append({ "User ID": user_id, "Conversations": conv_ids_str, "Meetings": meeting_ids_str, "Conv Key Findings": conv_findings_str, "Meeting Key Findings": meeting_findings_str, "Priority": priority, "_raw": user # Store raw data for detail view }) df = pd.DataFrame(table_data) return df def get_user_detail(self, df: pd.DataFrame, evt: gr.SelectData) -> str: """Get detailed view of selected user.""" if df is None or len(df) == 0: return "No user selected" try: selected_row = evt.index[0] if isinstance(evt.index, list) else evt.index user_data = df.iloc[selected_row]["_raw"] # Build detailed HTML view html = f"""
Priority Level: {user_data.get('priority_level', 'unknown').upper()}
Analysis Date: {user_data.get('analysis_timestamp', 'N/A')}
Summary: {conv_summary}
" # Conversation quotes conv_marketing = conv_insights.get("aggregated_marketing_insights", {}) conv_quotes = conv_marketing.get("quotes", []) if conv_quotes: html += "Summary: {meeting_summary}
" # Meeting quotes meeting_marketing = meeting_insights.get("aggregated_marketing_insights", {}) meeting_quotes = meeting_marketing.get("quotes", []) if meeting_quotes: html += "Summary: {unified_summary}
" # User journey user_journey = user_data.get("user_journey_summary", "No journey summary available") html += f"{user_journey}
" # Cross-interaction patterns patterns = user_data.get("cross_interaction_patterns", []) if patterns: html += "{recommendations}
" html += "Total Users Analyzed
Intercom Conversations
JustCall Meetings
Select a user from the table above to view detailed insights
" ) # ============================================================ # Event Handlers # ============================================================ def filter_high(): df = dashboard.get_users_data(priority_filter="High") display = df.drop(columns=["_raw"]) if "_raw" in df.columns else df return display, df, df, f"Showing {len(df)} HIGH priority users" def filter_medium(): df = dashboard.get_users_data(priority_filter="Medium") display = df.drop(columns=["_raw"]) if "_raw" in df.columns else df return display, df, df, f"Showing {len(df)} MEDIUM priority users" def filter_low(): df = dashboard.get_users_data(priority_filter="Low") display = df.drop(columns=["_raw"]) if "_raw" in df.columns else df return display, df, df, f"Showing {len(df)} LOW priority users" def filter_all(): df = dashboard.get_users_data(priority_filter=None) display = df.drop(columns=["_raw"]) if "_raw" in df.columns else df return display, df, df, f"Showing all {len(df)} users" def search_users(search_term: str, current_filtered_df: pd.DataFrame): """Search within currently filtered data.""" if not search_term: # Return the current filtered data display = current_filtered_df.drop(columns=["_raw"]) if "_raw" in current_filtered_df.columns else current_filtered_df return display # Search in the filtered data if current_filtered_df is None or len(current_filtered_df) == 0: return pd.DataFrame() # Create a copy for searching search_df = current_filtered_df.copy() # Search across all visible columns (excluding _raw) visible_cols = [col for col in search_df.columns if col != "_raw"] mask = search_df[visible_cols].astype(str).apply( lambda row: row.str.contains(search_term, case=False, na=False).any(), axis=1 ) result_df = search_df[mask] display = result_df.drop(columns=["_raw"]) if "_raw" in result_df.columns else result_df return display def show_detail(evt: gr.SelectData, full_data: pd.DataFrame): """Show detailed view when row is selected.""" return dashboard.get_user_detail(full_data, evt) # Wire up event handlers high_btn.click( fn=filter_high, outputs=[user_table, filtered_data_state, full_data_state, filter_status] ) medium_btn.click( fn=filter_medium, outputs=[user_table, filtered_data_state, full_data_state, filter_status] ) low_btn.click( fn=filter_low, outputs=[user_table, filtered_data_state, full_data_state, filter_status] ) all_btn.click( fn=filter_all, outputs=[user_table, filtered_data_state, full_data_state, filter_status] ) search_box.change( fn=search_users, inputs=[search_box, filtered_data_state], outputs=[user_table] ) user_table.select( fn=show_detail, inputs=[full_data_state], outputs=[user_detail] ) return demo if __name__ == "__main__": logger.info("Starting User Interaction Analysis Dashboard...") demo = create_dashboard() demo.launch( server_name="0.0.0.0", server_port=7861, share=False )