From 0fad9ce6b80648947fbc53593e5d93007614d983 Mon Sep 17 00:00:00 2001 From: Moreminimore Date: Wed, 19 Aug 2026 12:43:54 +0700 Subject: [PATCH] feat(analytics): add customer daily metrics data foundation Add the migration + model for per-customer-per-day conversation analytics (M2 phase 1b). Complements the per-account ConversationDailyMetric with a per-contact/day view so reports can drill into individual customers and see per-agent attribution, while the per-account table keeps the aggregate totals. - customer_daily_metrics: unique (account_id, contact_id, date); message/ conversation/resolved/unresolved counts, avg session duration, top tags, deal outcome breakdown, and agent_ids (jsonb). - CustomerDailyMetric: belongs_to :account + :contact (Contact convention), validations, JSONB getters defaulting to []/{}. Approved by independent five-key pre-commit review deleg_434fc544 (passed=true, blocking arrays empty). --- app/models/customer_daily_metric.rb | 41 +++++++++++++++++++ ...819000001_create_customer_daily_metrics.rb | 25 +++++++++++ 2 files changed, 66 insertions(+) create mode 100644 app/models/customer_daily_metric.rb create mode 100644 db/migrate/20260819000001_create_customer_daily_metrics.rb diff --git a/app/models/customer_daily_metric.rb b/app/models/customer_daily_metric.rb new file mode 100644 index 000000000..2a4662d8c --- /dev/null +++ b/app/models/customer_daily_metric.rb @@ -0,0 +1,41 @@ +# == Schema Information +# +# Table name: customer_daily_metrics +# +# id :bigint not null, primary key +# account_id :bigint not null +# contact_id :bigint not null +# date :date not null +# timezone :string +# message_count :integer default(0), not null +# conversation_count :integer default(0), not null +# resolved_count :integer default(0), not null +# unresolved_count :integer default(0), not null +# avg_session_duration_seconds :float default(0.0), not null +# top_tags :jsonb default([]), not null +# deal_outcomes :jsonb default({}), not null +# agent_ids :jsonb default([]), not null +# created_at :datetime not null +# updated_at :datetime not null +# +class CustomerDailyMetric < ApplicationRecord + belongs_to :account + belongs_to :contact + + validates :account_id, presence: true + validates :contact_id, presence: true + validates :date, presence: true + validates :date, uniqueness: { scope: %i[account_id contact_id] } + + def top_tags + self[:top_tags] || [] + end + + def deal_outcomes + self[:deal_outcomes] || {} + end + + def agent_ids + self[:agent_ids] || [] + end +end diff --git a/db/migrate/20260819000001_create_customer_daily_metrics.rb b/db/migrate/20260819000001_create_customer_daily_metrics.rb new file mode 100644 index 000000000..4c69c8fa7 --- /dev/null +++ b/db/migrate/20260819000001_create_customer_daily_metrics.rb @@ -0,0 +1,25 @@ +class CreateCustomerDailyMetrics < ActiveRecord::Migration[7.1] + def change + create_table :customer_daily_metrics do |t| + t.bigint :account_id, null: false + t.bigint :contact_id, null: false + t.date :date, null: false + t.string :timezone + t.integer :message_count, default: 0, null: false + t.integer :conversation_count, default: 0, null: false + t.integer :resolved_count, default: 0, null: false + t.integer :unresolved_count, default: 0, null: false + t.float :avg_session_duration_seconds, default: 0.0, null: false + t.jsonb :top_tags, default: [], null: false + t.jsonb :deal_outcomes, default: {}, null: false + t.jsonb :agent_ids, default: [], null: false + t.timestamps + end + + add_index :customer_daily_metrics, [:account_id, :contact_id, :date], unique: true, + name: 'index_cdm_customer_on_account_contact_date' + add_index :customer_daily_metrics, :contact_id + add_index :customer_daily_metrics, :account_id + add_index :customer_daily_metrics, :date + end +end