Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

WhatsApp Relationship Analyzer

notebooks/analysis.livemd

WhatsApp Relationship Analyzer

Mix.install([
  {:nx, "~> 0.9"},
  {:axon, "~> 0.7"},
  {:explorer, "~> 0.10"},
  {:exla, "~> 0.9"},
  {:kino, "~> 0.15"},
  {:kino_vega_lite, "~> 0.1"},
  {:vega_lite, "~> 0.1"},
  {:stemmer, "~> 1.2"},
  {:whatsapp_analyser, github: "zoedsoupe/whatsapp-relationship-analyser", branch: "main"}
])

Setup

> Important: To run this notebook properly: > > 1. Click the Settings (⚙️) button in the top right > 2. Under “Runtime,” select “Mix standalone” > 3. For the “Path to mix project,” enter the path to the root of this repository > 4. Click “Apply changes”

Analysis

# Set up aliases for visualization
alias VegaLite, as: Vl
alias Kino.VegaLite, as: KVl

# Create a form with file input
form = Kino.Control.form(
  [
    file: Kino.Input.file("Upload WhatsApp Chat Export (.txt)")
  ],
  submit: "Analyze Chat"
)

# Render the form
Kino.render(form)

# Create a frame to display results
result_frame = Kino.Frame.new()
Kino.render(result_frame)

# Handle form submission
Kino.listen(form, fn %{data: %{file: file}} ->
  if file do
    # Get file content using the appropriate Kino function
    path = Kino.Input.file_path(file.file_ref)
    
    
    # Run the analysis
    Kino.Frame.render(result_frame, Kino.Markdown.new("Analyzing chat file... This may take a moment."))
    
    try do
      # Run the analysis
      analysis = WhatsAppAnalyzer.analyze_chat(path)
      
      # Display summary
      summary = WhatsAppAnalyzer.summarize_relationship(analysis)
      
      summary_text = """
      # Relationship Analysis Summary
      
      **Classification:** #{summary.classification}
      **Confidence Score:** #{summary.confidence_score}%
      **Total Messages:** #{summary.message_count}
      **Time Span:** #{round(summary.time_span_days)} days
      
      ## Key Indicators
      #{Enum.map(summary.primary_indicators, fn {indicator, score} -> "- #{indicator}: #{score}%" end) |> Enum.join("\n")}
      """
      
      Kino.Frame.render(result_frame, Kino.Markdown.new(summary_text))
      
      # Display visualizations
      Kino.Frame.append(result_frame, Kino.Markdown.new("## Message Frequency Over Time"))
      Kino.Frame.append(result_frame, KVl.new(analysis.visualizations.message_frequency))
      
      Kino.Frame.append(result_frame, Kino.Markdown.new("## Messages by Sender"))
      Kino.Frame.append(result_frame, KVl.new(analysis.visualizations.sender_distribution))
      
      Kino.Frame.append(result_frame, Kino.Markdown.new("## Activity Heatmap by Time of Day"))
      Kino.Frame.append(result_frame, KVl.new(analysis.visualizations.time_heatmap))
      
      Kino.Frame.append(result_frame, Kino.Markdown.new("## Relationship Indicators"))
      Kino.Frame.append(result_frame, KVl.new(analysis.visualizations.relationship_radar))
      
      Kino.Frame.append(result_frame, Kino.Markdown.new("## Classification Confidence"))
      Kino.Frame.append(result_frame, KVl.new(analysis.visualizations.classification))
      
      # Display detailed statistics
      Kino.Frame.append(result_frame, Kino.Markdown.new("## Detailed Statistics"))
      
      # Show top romantic indicators
      romantic_indicators = analysis.analysis.romantic_indicators
      
      romantic_text = """
      ### Romantic Language
      - Total romantic indicators: #{romantic_indicators.total_indicators}
      - Percentage of messages with romantic language: #{romantic_indicators.percentage_of_messages}%
      """
      
      Kino.Frame.append(result_frame, Kino.Markdown.new(romantic_text))
      
      # Show conversation initiation statistics
      initiations = analysis.analysis.conversation_initiation
      
      initiation_text = """
      ### Conversation Patterns
      - Total conversations: #{initiations.total_conversations}
      - Conversation initiations by sender:
      #{Enum.map(initiations.initiation_percentage, fn {sender, percentage} -> "  - #{sender}: #{percentage}%" end) |> Enum.join("\n")}
      """
      
      Kino.Frame.append(result_frame, Kino.Markdown.new(initiation_text))
      
      # Show time-based patterns
      time_patterns = analysis.analysis.time_of_day_patterns
      
      time_text = """
      ### Time Patterns
      - Most active time period: #{Enum.max_by(time_patterns.percentage_by_period, fn {_, v} -> v end) |> elem(0)}
      - Weekend vs Weekday: #{round(analysis.analysis.day_of_week_patterns.weekday_vs_weekend.weekend_percentage)}% weekend, #{round(analysis.analysis.day_of_week_patterns.weekday_vs_weekend.weekday_percentage)}% weekday
      """
      
      Kino.Frame.append(result_frame, Kino.Markdown.new(time_text))
      
    rescue
      e ->
        error_message = """
        Error analyzing chat: #{inspect(e)}
        
        Stack trace:
        #{Exception.format_stacktrace(__STACKTRACE__)}
        """
        
        Kino.Frame.render(result_frame, Kino.Markdown.new(error_message))
    end
  else
    Kino.Frame.render(result_frame, Kino.Markdown.new("Please upload a WhatsApp chat export file first."))
  end
end)