# frozen_string_literal: true class StreamConcurrencyViolation < ApplicationRecord LOOKBACK = 30.days belongs_to :user, optional: true belongs_to :occupying_session, class_name: "StreamSession", optional: true belongs_to :attempted_session, class_name: "StreamSession", optional: true belongs_to :occupying_club, class_name: "Club", optional: true belongs_to :attempted_club, class_name: "Club", optional: true validates :user_email, :occupying_match_label, :attempted_match_label, presence: true validates :attempt_action, inclusion: { in: %w[start resume] } scope :recent, -> { order(created_at: :desc) } scope :since, ->(time) { where("created_at >= ?", time) } scope :lookback, -> { since(LOOKBACK.ago) } scope :two_devices, -> { where(devices_differ: true) } scope :for_club, lambda { |club_id| where("occupying_club_id = :id OR attempted_club_id = :id", id: club_id) } scope :for_session, lambda { |session_id| where("occupying_session_id = :id OR attempted_session_id = :id", id: session_id) } def self.record!(attempted:, occupying:, action: "start") user = attempted.user || occupying.user occupying_club = occupying.match&.team&.club attempted_club = attempted.match&.team&.club occupying_device = occupying.client_device_label attempted_device = attempted.client_device_label create!( user: user, occupying_session: occupying, attempted_session: attempted, occupying_club: occupying_club, attempted_club: attempted_club, attempt_action: action.to_s, user_email: user&.email.presence || "unknown", user_name: user&.name, occupying_club_name: occupying_club&.name, attempted_club_name: attempted_club&.name, occupying_match_label: occupying.match_label, attempted_match_label: attempted.match_label, occupying_status: occupying.status, occupying_device: occupying_device, attempted_device: attempted_device, devices_differ: StreamSession.devices_differ?(occupying, attempted), metadata: { occupying_session_id: occupying.id, attempted_session_id: attempted.id, occupying_client_os: occupying.client_os, attempted_client_os: attempted.client_os, occupying_app_version: occupying.app_version, attempted_app_version: attempted.app_version }.compact ) rescue StandardError => e Rails.logger.error("[StreamConcurrencyViolation] record failed: #{e.class} #{e.message}") nil end def club_name attempted_club_name.presence || occupying_club_name end def operator_label [user_name.presence, user_email].compact.join(" ยท ") end end