earlymarket/CoPosition

View on GitHub
app/models/device.rb

Summary

Maintainability
A
2 hrs
Test Coverage
class Device < ApplicationRecord
  include SlackNotifiable, RemoveId, PublicActivity::Common

  belongs_to :user
  has_one :config, dependent: :destroy
  has_one :configurer, through: :config, source: :developer
  has_many :checkins
  has_many :permissions, dependent: :destroy
  has_many :developers, through: :permissions, source: :permissible, source_type: "Developer"
  has_many :permitted_users, through: :permissions, source: :permissible, source_type: "User"
  has_attachment :csv, accept: :raw

  validates :name, uniqueness: { scope: :user_id }, if: :user_id, length: { in: 4..20 },
                   format: { with: /\A[A-Za-z][A-Za-z0-9]*(?:_+[A-Za-z0-9]+)*\z/,
                             message: "only allows alphanumeric and underscores" }

  validates :icon, presence: true

  scope :active_devices, -> { joins(:user).where(users: { is_active: true }) }

  before_create do |dev|
    dev.uuid = SecureRandom.uuid
  end

  def safe_checkin_info_for(args)
    sanitized = filtered_checkins(args)
    sanitize_checkins(sanitized, args)
  end

  def filtered_checkins(args)
    sanitized = args[:copo_app] ? past_checkins : permitted_history_for(args[:permissible])
    sanitized.since_time(args[:time_amount], args[:time_unit])
             .near_to(args[:near])
             .on_date(args[:date])
             .unique_places_only(args[:unique_places])
             .limit_returned_checkins(args)
  end

  def sanitize_checkins(sanitized, args)
    if args[:type] == "address"
      sanitized.map(&:reverse_geocode!) unless args[:action] == "index" && args[:multiple_devices]
    end
    return sanitized if args[:copo_app]
    replace_checkin_attributes(sanitized, args[:permissible])
  end

  def replace_checkin_attributes(sanitized, permissible)
    if can_bypass_fogging?(permissible)
      sanitized.select(:id, :created_at, :updated_at, :device_id, :lat,
        :lng, :address, :city, :postal_code, :country_code, :speed, :altitude)
    elsif fogged
      sanitized.select("id", "created_at", "updated_at", "device_id", "fogged_lat AS lat", "fogged_lng AS lng",
        "fogged_city AS address", "fogged_city AS city", "fogged_country_code AS postal_code",
        "fogged_country_code AS country_code", "null::text AS speed", "null::text AS altitude")
    else
      sanitized.select("id", "created_at", "updated_at", "device_id", "output_lat AS lat", "output_lng AS lng",
        "output_address AS address", "output_city AS city", "output_postal_code AS postal_code",
        "output_country_code AS country_code", "speed", "altitude")
    end
  end

  def permitted_history_for(permissible)
    return Checkin.none if cloaked
    resolve_privilege(delayed_checkins_for(permissible), permissible)
  end

  def resolve_privilege(unresolved_checkins, permissible)
    return Checkin.none if privilege_for(permissible) == "disallowed"
    return unresolved_checkins if unresolved_checkins.empty?
    if privilege_for(permissible) == "last_only"
      unresolved_checkins.where(id: unresolved_checkins.first.id)
    else
      unresolved_checkins
    end
  end

  def privilege_for(permissible)
    permission_for(permissible).privilege
  end

  def delayed_checkins_for(permissible)
    if can_bypass_delay?(permissible)
      past_checkins
    else
      before_delay_checkins
    end
  end

  def before_delay_checkins
    delayed ? checkins.where("checkins.created_at < ?", delayed.minutes.ago) : past_checkins
  end

  def past_checkins
    checkins.where("checkins.created_at < ?", Time.current)
  end

  def permission_for(permissible)
    permissions.find_by(permissible_id: permissible.id, permissible_type: permissible.class.to_s)
  end

  def complete_permissions
    if (approved_ids = user.approved_developers.pluck(:id)).present?
      permissions.where.not(["permissible_type = ? AND permissible_id IN (?)", "Developer", approved_ids])
    else
      permissions
    end
  end

  def can_bypass_fogging?(permissible)
    permission_for(permissible).bypass_fogging
  end

  def can_bypass_delay?(permissible)
    permission_for(permissible).bypass_delay
  end

  def slack_message
    "A new device was created, id: #{id}, name: #{name}, user_id: #{user_id}. There are now #{Device.count} devices"
  end

  def public_info
    # Clears out any potentially sensitive attributes, returns a normal ActiveRecord relation
    # Returns a normal ActiveRecord relation
    Device.select(%i(id user_id name alias published)).find(id)
  end

  def subscriptions(event)
    subs = Subscription.where(event: event).where(subscriber_id: user_id)
    subs if subs.present?
  end

  def notify_subscribers(event, data)
    return unless user.zapier_enabled && (subs = subscriptions(event))
    data = data.as_json
    data.merge!(remove_id.as_json)
    data.merge!(user.public_info.remove_id.as_json) if user
    subs.each { |subscription| subscription.send_data([data]) }
  end

  def self.public_info
    select(%i(id user_id name alias published))
  end

  def self.last_checkins
    all.map { |device| device.past_checkins.first if device.past_checkins.exists? }.compact.sort_by(&:created_at).reverse
  end

  def self.geocode_last_checkins
    all.each { |device| device.past_checkins.first.reverse_geocode! if device.past_checkins.exists? }
  end

  def self.ordered_by_checkins
    device_ids = last_checkins.map(&:device_id)
    ordered_devices = all.index_by(&:id).values_at(*device_ids)
    ordered_devices += all
    ordered_devices.uniq
  end

  def self.inactive(time_length)
    last_checkins_ids = Device.last_checkins.map(&:id)
    old_last_checkins = Checkin.where("id IN (?) AND created_at < ?", last_checkins_ids, time_length)
    device_ids = old_last_checkins.map(&:device_id)
    Device.where(id: device_ids)
  end
end