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

425 lines
14 KiB
Ruby
Raw Normal View History

module JamRuby
class MusicSession < ActiveRecord::Base
self.primary_key = 'id'
2012-11-02 06:51:52 +00:00
attr_accessor :legal_terms, :skip_genre_validation, :max_score
2013-06-11 01:37:10 +00:00
attr_accessible :creator, :description, :musician_access, :approval_required, :fan_chat, :fan_access, :genres
belongs_to :creator, :inverse_of => :music_sessions, :class_name => "JamRuby::User", :foreign_key => "user_id"
2014-01-05 03:47:23 +00:00
belongs_to :claimed_recording, :class_name => "JamRuby::ClaimedRecording", :foreign_key => "claimed_recording_id", :inverse_of => :playing_sessions
belongs_to :claimed_recording_initiator, :class_name => "JamRuby::User", :inverse_of => :playing_claimed_recordings, :foreign_key => "claimed_recording_initiator_id"
2014-01-31 06:39:09 +00:00
has_one :music_session_history, :class_name => "JamRuby::MusicSessionHistory"
has_one :mount, :class_name => "JamRuby::IcecastMount", :inverse_of => :music_session, :foreign_key => 'music_session_id'
has_many :connections, :class_name => "JamRuby::Connection"
has_many :users, :through => :connections, :class_name => "JamRuby::User"
has_and_belongs_to_many :genres, :class_name => "::JamRuby::Genre", :join_table => "genres_music_sessions"
has_many :join_requests, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::JoinRequest"
has_many :invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::Invitation"
has_many :invited_musicians, :through => :invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id", :source => :receiver
has_many :fan_invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::FanInvitation"
has_many :invited_fans, :through => :fan_invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id", :source => :receiver
has_many :recordings, :class_name => "JamRuby::Recording", :inverse_of => :music_session
belongs_to :band, :inverse_of => :music_sessions, :class_name => "JamRuby::Band", :foreign_key => "band_id"
validate :require_at_least_one_genre, :limit_max_genres
after_save :sync_music_session_history
after_destroy do |obj|
JamRuby::MusicSessionHistory.removed_music_session(obj.id)
end
2013-07-26 08:07:24 +00:00
validates :description, :presence => true, :no_profanity => true
validates :fan_chat, :inclusion => {:in => [true, false]}
validates :fan_access, :inclusion => {:in => [true, false]}
validates :approval_required, :inclusion => {:in => [true, false]}
validates :musician_access, :inclusion => {:in => [true, false]}
validates :legal_terms, :inclusion => {:in => [true]}, :on => :create
validates :creator, :presence => true
validate :creator_is_musician
2014-01-05 03:47:23 +00:00
validate :no_new_playback_while_playing
#default_scope :select => "*, 0 as score"
def attributes
super.merge('max_score' => self.max_score)
end
def max_score
nil unless has_attribute?(:max_score)
read_attribute(:max_score).to_i
end
before_create :create_uuid
def create_uuid
#self.id = SecureRandom.uuid
end
def before_destroy
self.mount.destroy if self.mount
end
def creator_is_musician
unless creator.musician?
2014-01-05 03:47:23 +00:00
errors.add(:creator, ValidationMessages::MUST_BE_A_MUSICIAN)
end
end
def no_new_playback_while_playing
# if we previous had a claimed recording and are trying to set one
# and if also the previous initiator is different than the current one... it's a no go
if !claimed_recording_id_was.nil? && !claimed_recording_id.nil? &&
claimed_recording_initiator_id_was != claimed_recording_initiator_id
errors.add(:claimed_recording, ValidationMessages::CLAIMED_RECORDING_ALREADY_IN_PROGRESS)
end
end
# returns an array of client_id's that are in this session
# if as_musician is nil, all connections in the session ,regardless if it's a musician or not or not
# you can also exclude a client_id from the returned set by setting exclude_client_id
def get_connection_ids(options = {})
as_musician = options[:as_musician]
exclude_client_id = options[:exclude_client_id]
where = { :music_session_id => self.id }
where[:as_musician] = as_musician unless as_musician.nil?
exclude = "client_id != '#{exclude_client_id}'"unless exclude_client_id.nil?
Connection.select(:client_id).where(where).where(exclude).map(&:client_id)
end
2012-12-06 01:56:12 +00:00
# This is a little confusing. You can specify *BOTH* friends_only and my_bands_only to be true
# If so, then it's an OR condition. If both are false, you can get sessions with anyone.
2014-01-25 20:03:14 +00:00
def self.index(current_user, options = {})
participants = options[:participants]
genres = options[:genres]
keyword = options[:keyword]
friends_only = options[:friends_only].nil? ? false : options[:friends_only]
my_bands_only = options[:my_bands_only].nil? ? false : options[:my_bands_only]
as_musician = options[:as_musician].nil? ? true : options[:as_musician]
2012-12-06 01:56:12 +00:00
query = MusicSession
.joins(
2012-12-05 19:10:58 +00:00
%Q{
INNER JOIN
2012-12-05 19:10:58 +00:00
connections
ON
music_sessions.id = connections.music_session_id
}
)
.joins(
%Q{
LEFT OUTER JOIN
friendships
ON
2012-12-05 19:10:58 +00:00
connections.user_id = friendships.user_id
AND
friendships.friend_id = '#{current_user.id}'
}
)
.joins(
%Q{
LEFT OUTER JOIN
invitations
ON
2012-12-04 07:00:32 +00:00
invitations.music_session_id = music_sessions.id
AND
invitations.receiver_id = '#{current_user.id}'
}
)
.group(
2012-12-05 19:10:58 +00:00
%Q{
music_sessions.id
}
)
.order(
%Q{
2012-12-05 19:10:58 +00:00
SUM(CASE WHEN invitations.id IS NULL THEN 0 ELSE 1 END) DESC,
SUM(CASE WHEN friendships.user_id IS NULL THEN 0 ELSE 1 END) DESC,
music_sessions.created_at DESC
}
)
2014-01-25 20:03:14 +00:00
if as_musician
query = query.where(
%Q{
musician_access = true
OR
invitations.id IS NOT NULL
}
)
2014-01-25 20:03:14 +00:00
else
# if you are trying to join the session as a fan/listener,
# we have to have a mount, fan_access has to be true, and we have to allow for the reload of icecast to have taken effect
query = query.joins('INNER JOIN icecast_mounts ON icecast_mounts.music_session_id = music_sessions.id INNER JOIN icecast_servers ON icecast_mounts.icecast_server_id = icecast_servers.id')
query = query.where(:fan_access => true)
2014-02-02 02:45:58 +00:00
query = query.where("(music_sessions.created_at < icecast_servers.config_updated_at)")
2014-01-25 20:03:14 +00:00
end
2013-02-02 23:55:26 +00:00
query = query.where("music_sessions.description like '%#{keyword}%'") unless keyword.nil?
query = query.where("connections.user_id" => participants.split(',')) unless participants.nil?
query = query.joins(:genres).where("genres.id" => genres.split(',')) unless genres.nil?
2012-12-06 01:56:12 +00:00
if my_bands_only
query = query.joins(
%Q{
2012-12-06 01:56:12 +00:00
LEFT OUTER JOIN
bands_musicians
ON
bands_musicians.user_id = '#{current_user.id}'
}
)
2012-12-05 19:10:58 +00:00
end
2012-12-06 01:56:12 +00:00
if my_bands_only || friends_only
query = query.where(
%Q{
#{friends_only ? "friendships.user_id IS NOT NULL" : "false"}
2012-12-06 01:56:12 +00:00
OR
#{my_bands_only ? "bands_musicians.band_id = music_sessions.band_id" : "false"}
}
)
end
return query
end
# This is a little confusing. You can specify *BOTH* friends_only and my_bands_only to be true
# If so, then it's an OR condition. If both are false, you can get sessions with anyone.
# note, this is mostly the same as above but includes paging through the result and and scores.
# thus it needs the client_id...
def self.nindex(current_user, options = {})
client_id = options[:client_id]
participants = options[:participants]
genres = options[:genres]
keyword = options[:keyword]
friends_only = options[:friends_only].nil? ? false : options[:friends_only]
my_bands_only = options[:my_bands_only].nil? ? false : options[:my_bands_only]
as_musician = options[:as_musician].nil? ? true : options[:as_musician]
offset = options[:offset]
limit = options[:limit]
connection = Connection.where(client_id: client_id).first!
locidispid = connection.locidispid
query = MusicSession
2014-03-09 19:35:42 +00:00
.select("music_sessions.*, max(coalesce(current_scores.score, 1000)) as max_score") # 1000 is higher than the allowed max of 999
.joins(
%Q{
INNER JOIN
connections
ON
music_sessions.id = connections.music_session_id
}
)
.joins(
%Q{
LEFT OUTER JOIN
2014-03-06 05:00:57 +00:00
current_scores
ON
2014-03-06 05:00:57 +00:00
current_scores.alocidispid = connections.locidispid
AND
2014-03-06 05:00:57 +00:00
current_scores.blocidispid = #{locidispid}
}
)
.joins(
%Q{
LEFT OUTER JOIN
friendships
ON
connections.user_id = friendships.user_id
AND
friendships.friend_id = '#{current_user.id}'
}
)
.joins(
%Q{
LEFT OUTER JOIN
invitations
ON
invitations.music_session_id = music_sessions.id
AND
invitations.receiver_id = '#{current_user.id}'
}
)
.group(
%Q{
music_sessions.id
}
)
.order(
%Q{
SUM(CASE WHEN invitations.id IS NULL THEN 0 ELSE 1 END) DESC,
SUM(CASE WHEN friendships.user_id IS NULL THEN 0 ELSE 1 END) DESC,
music_sessions.created_at DESC
}
)
if (offset)
query = query.offset(offset)
end
if (limit)
query = query.limit(limit)
end
if as_musician
query = query.where(
%Q{
musician_access = true
OR
invitations.id IS NOT NULL
}
)
else
# if you are trying to join the session as a fan/listener,
# we have to have a mount, fan_access has to be true, and we have to allow for the reload of icecast to have taken effect
query = query.joins('INNER JOIN icecast_mounts ON icecast_mounts.music_session_id = music_sessions.id INNER JOIN icecast_servers ON icecast_mounts.icecast_server_id = icecast_servers.id')
query = query.where(:fan_access => true)
query = query.where("(music_sessions.created_at < icecast_servers.config_updated_at)")
end
query = query.where("music_sessions.description like '%#{keyword}%'") unless keyword.nil?
query = query.where("connections.user_id" => participants.split(',')) unless participants.nil?
query = query.joins(:genres).where("genres.id" => genres.split(',')) unless genres.nil?
if my_bands_only
query = query.joins(
%Q{
LEFT OUTER JOIN
bands_musicians
ON
bands_musicians.user_id = '#{current_user.id}'
2012-12-06 01:56:12 +00:00
}
)
end
if my_bands_only || friends_only
query = query.where(
%Q{
#{friends_only ? "friendships.user_id IS NOT NULL" : "false"}
OR
#{my_bands_only ? "bands_musicians.band_id = music_sessions.band_id" : "false"}
}
)
end
2012-12-06 01:56:12 +00:00
return query
end
2012-11-02 06:51:52 +00:00
# Verifies that the specified user can join this music session
2012-11-18 03:41:12 +00:00
def can_join? user, as_musician
if as_musician
if !user.musician
return false # "a fan can not join a music session as a musician"
raise PermissionError, "a fan can not join a music session as a musician"
end
2012-11-18 03:41:12 +00:00
if self.musician_access
if self.approval_required
return self.invited_musicians.exists?(user)
else
return true
end
2012-11-18 03:41:12 +00:00
else
# the creator can always join, and the invited users can join
return self.creator == user || self.invited_musicians.exists?(user)
end
2012-11-02 06:51:52 +00:00
else
2012-11-18 03:41:12 +00:00
# it's a fan, and the only way a fan can join is if fan_access is true
return self.fan_access
2012-11-02 06:51:52 +00:00
end
2012-11-18 03:41:12 +00:00
2012-11-02 06:51:52 +00:00
end
# Verifies that the specified user can see this music session
def can_see? user
if self.musician_access || self.fan_access
2012-11-02 06:51:52 +00:00
return true
else
return self.creator == user || self.invited_musicians.exists?(user)
2012-11-02 06:51:52 +00:00
end
end
# Verifies that the specified user can delete this music session
def can_delete? user
# the creator can delete
return self.creator == user
end
def access? user
return self.users.exists? user
end
def most_recent_recording
recordings.where(:music_session_id => self.id).order('created_at desc').limit(1).first
end
# is this music session currently recording?
def is_recording?
recordings.where(:duration => nil).count > 0
end
2014-01-05 03:47:23 +00:00
def is_playing_recording?
!self.claimed_recording.nil?
end
def recording
recordings.where(:duration => nil).first
end
# stops any active recording
def stop_recording
current_recording = self.recording
current_recording.stop unless current_recording.nil?
end
2014-01-05 03:47:23 +00:00
def claimed_recording_start(owner, claimed_recording)
self.claimed_recording = claimed_recording
self.claimed_recording_initiator = owner
self.save
end
def claimed_recording_stop
self.claimed_recording = nil
self.claimed_recording_initiator = nil
self.save
end
def to_s
2014-01-05 03:47:23 +00:00
description
end
def tick_track_changes
self.track_changes_counter += 1
self.save!(:validate => false)
end
private
def require_at_least_one_genre
unless skip_genre_validation
if self.genres.length < Limits::MIN_GENRES_PER_SESSION
errors.add(:genres, ValidationMessages::SESSION_GENRE_MINIMUM_NOT_MET)
end
end
end
2013-06-11 01:37:10 +00:00
def limit_max_genres
unless skip_genre_validation
if self.genres.length > Limits::MAX_GENRES_PER_SESSION
errors.add(:genres, ValidationMessages::SESSION_GENRE_LIMIT_EXCEEDED)
end
end
end
def sync_music_session_history
MusicSessionHistory.save(self)
end
end
end