jam-cloud/ruby/lib/jam_ruby/models/connection.rb

226 lines
7.5 KiB
Ruby

require 'aasm'
module JamRuby
class Connection < ActiveRecord::Base
include HtmlSanitize
# client_types
TYPE_CLIENT = 'client'
TYPE_BROWSER = 'browser'
TYPE_LATENCY_TESTER = 'latency_tester'
attr_accessor :joining_session
self.primary_key = 'id'
belongs_to :user, :class_name => "JamRuby::User"
belongs_to :music_session, :class_name => "JamRuby::ActiveMusicSession", foreign_key: :music_session_id
has_one :latency_tester, class_name: 'JamRuby::LatencyTester', foreign_key: :client_id, primary_key: :client_id
has_many :tracks, :class_name => "JamRuby::Track", :inverse_of => :connection, :foreign_key => 'connection_id', :dependent => :delete_all
validates :as_musician, :inclusion => {:in => [true, false, nil]}
validates :client_type, :inclusion => {:in => [TYPE_CLIENT, TYPE_BROWSER, TYPE_LATENCY_TESTER]}
validates_numericality_of :last_jam_audio_latency, greater_than:0, :allow_nil => true
validate :can_join_music_session, :if => :joining_session?
validate :user_or_latency_tester_present
after_save :require_at_least_one_track_when_in_session, :if => :joining_session?
after_create :did_create
after_save :report_add_participant
include AASM
IDLE_STATE = :idle
CONNECT_STATE = :connected
STALE_STATE = :stale
EXPIRED_STATE = :expired
aasm do
state IDLE_STATE, :initial => true
state CONNECT_STATE
state STALE_STATE
state EXPIRED_STATE
event :connect do
transitions :from => IDLE_STATE, :to => CONNECT_STATE
transitions :from => STALE_STATE, :to => CONNECT_STATE
end
event :stale do
transitions :from => CONNECT_STATE, :to => STALE_STATE
transitions :from => IDLE_STATE, :to => STALE_STATE
end
event :expire, :after => :did_expire do
transitions :from => CONNECT_STATE, :to => EXPIRED_STATE
transitions :from => STALE_STATE, :to => EXPIRED_STATE
transitions :from => IDLE_STATE, :to => EXPIRED_STATE
end
end
def state_message
case self.aasm_state.to_sym
when CONNECT_STATE
'Connected'
when STALE_STATE
'Stale'
else
'Idle'
end
end
def in_session?
!music_session_id.nil?
end
def in_scoring_timeout?
scoring_timeout > Time.now
end
def did_expire
self.destroy
end
def joining_session?
joining_session
end
def can_join_music_session
# puts "can_join_music_session: #{music_session_id} was #{music_session_id_was}" if music_session_id_changed?
if music_session_id_changed? and !(music_session_id_was.nil? or music_session_id_was.blank?)
errors.add(:music_session, ValidationMessages::CANT_JOIN_MULTIPLE_SESSIONS)
return false
end
if music_session.nil?
errors.add(:music_session, ValidationMessages::MUSIC_SESSION_MUST_BE_SPECIFIED)
return false
end
if as_musician
unless self.user.musician
errors.add(:as_musician, ValidationMessages::FAN_CAN_NOT_JOIN_AS_MUSICIAN)
return false
end
if music_session.musician_access
if music_session.approval_required
unless music_session.creator == user || music_session.invited_musicians.exists?(user)
errors.add(:approval_required, ValidationMessages::INVITE_REQUIRED)
return false
end
end
else
unless music_session.creator == user || music_session.invited_musicians.exists?(user)
errors.add(:musician_access, ValidationMessages::INVITE_REQUIRED)
return false
end
end
else
unless self.music_session.fan_access
# it's someone joining as a fan, and the only way a fan can join is if fan_access is true
errors.add(:fan_access, ValidationMessages::FANS_CAN_NOT_JOIN)
return false
end
end
if music_session.is_recording?
errors.add(:music_session, ValidationMessages::CANT_JOIN_RECORDING_SESSION)
end
# unless user.admin?
# num_sessions = Connection.where(:user_id => user_id)
# .where(["(music_session_id IS NOT NULL) AND (aasm_state != ?)",EXPIRED_STATE.to_s])
# .count
# if 0 < num_sessions
# errors.add(:music_session, ValidationMessages::CANT_JOIN_MULTIPLE_SESSIONS)
# return false;
# end
# end
return true
end
# decides if a given user can access this client with p2p messaging
# the answer is yes if the user is in the same music session
def access_p2p?(user)
return self.music_session.users.exists?(user)
end
def did_create
# self.user.update_lat_lng(self.ip_address) if self.user && self.ip_address
end
def report_add_participant
if self.music_session_id_changed? &&
self.music_session.present? &&
self.connected? &&
self.as_musician? &&
0 < (count = self.music_session.connected_participant_count)
GoogleAnalyticsEvent.report_session_participant(count)
end
true
end
def join_the_session(music_session, as_musician, tracks, user, audio_latency)
self.music_session_id = music_session.id
self.as_musician = as_musician
self.joining_session = true
self.joined_session_at = Time.now
associate_tracks(tracks) unless tracks.nil?
self.save
# if user joins the session as a musician, update their addr and location
if as_musician
user.update_addr_loc(self, User::JAM_REASON_JOIN)
user.update_audio_latency(self, audio_latency) if audio_latency # try not to let a previously recorded value get nil'ed
end
end
def associate_tracks(tracks)
unless tracks.nil?
self.tracks.clear()
tracks.each do |track|
t = Track.new
t.instrument = Instrument.find(track["instrument_id"])
t.connection = self
t.sound = track["sound"]
t.client_track_id = track["client_track_id"]
t.save # todo what if it fails?
self.tracks << t
end
end
end
def self.update_locidispids(use_copied = true)
# using addr, we can rebuild locidispid
# this will set a connections's _locidispid = 0 if there are no geoiplocations/blocks that match their IP address, or if there are no JamIsps that match the IP address
# otherwise, locidispid will be updated to the correct new value.
# updates all connections's locidispids
table_suffix = use_copied ? '_copied' : ''
Connection.connection.execute("UPDATE connections SET locidispid = COALESCE((SELECT geolocs.locid as geolocid FROM geoipblocks#{table_suffix} as geoblocks INNER JOIN geoiplocations#{table_suffix} as geolocs ON geoblocks.locid = geolocs.locid WHERE geoblocks.geom && ST_MakePoint(addr, 0) AND addr BETWEEN geoblocks.beginip AND geoblocks.endip LIMIT 1) * 1000000::bigint +(SELECT coid FROM jamisp#{table_suffix} as jisp WHERE geom && ST_MakePoint(addr, 0) AND addr BETWEEN beginip AND endip LIMIT 1), 0) ").check
end
def self.after_maxmind_import
update_locidispids
end
private
def require_at_least_one_track_when_in_session
if tracks.count == 0
errors.add(:tracks, ValidationMessages::SELECT_AT_LEAST_ONE)
end
end
def user_or_latency_tester_present
if user.nil? && client_type != TYPE_LATENCY_TESTER
puts client_type
errors.add(:connection, ValidationMessages::USER_OR_LATENCY_TESTER_PRESENT)
end
end
end
end