402 lines
14 KiB
Ruby
402 lines
14 KiB
Ruby
module JamRuby
|
|
class MusicSession < ActiveRecord::Base
|
|
|
|
NO_RECURRING = 'once'
|
|
RECURRING_WEEKLY = 'weekly'
|
|
|
|
RECURRING_MODES = [NO_RECURRING, RECURRING_WEEKLY]
|
|
|
|
attr_accessor :legal_terms, :recurring_mode
|
|
|
|
self.table_name = "music_sessions"
|
|
|
|
self.primary_key = 'id'
|
|
|
|
belongs_to :creator,:class_name => 'JamRuby::User', :foreign_key => :user_id, :inverse_of => :music_session_histories
|
|
|
|
belongs_to :band, :class_name => 'JamRuby::Band', :foreign_key => :band_id, :inverse_of => :music_sessions
|
|
|
|
belongs_to :active_music_session, :class_name => 'JamRuby::ActiveMusicSession', foreign_key: :music_session_id
|
|
|
|
has_many :music_session_user_histories, :class_name => "JamRuby::MusicSessionUserHistory", :foreign_key => "music_session_id", :dependent => :delete_all
|
|
has_many :comments, :class_name => "JamRuby::MusicSessionComment", :foreign_key => "music_session_id"
|
|
has_many :session_info_comments, :class_name => "JamRuby::SessionInfoComment", :foreign_key => "music_session_id"
|
|
has_many :likes, :class_name => "JamRuby::MusicSessionLiker", :foreign_key => "session_id"
|
|
has_many :plays, :class_name => "JamRuby::PlayablePlay", :as => :playable, :dependent => :destroy
|
|
has_one :share_token, :class_name => "JamRuby::ShareToken", :inverse_of => :shareable, :foreign_key => 'shareable_id'
|
|
has_one :feed, :class_name => "JamRuby::Feed", :inverse_of => :music_session, :foreign_key => 'music_session_id', :dependent => :destroy
|
|
belongs_to :genre, :class_name => "JamRuby::Genre", :inverse_of => :music_sessions, :foreign_key => 'genre_id'
|
|
has_many :join_requests, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::JoinRequest", :foreign_key => "music_session_id"
|
|
has_many :invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::Invitation", :foreign_key => "music_session_id"
|
|
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", :foreign_key => "music_session_id"
|
|
has_many :invited_fans, :through => :fan_invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id", :source => :receiver
|
|
has_many :rsvp_slots, :class_name => "JamRuby::RsvpSlot", :foreign_key => "music_session_id", :dependent => :destroy
|
|
has_many :music_notations, :class_name => "JamRuby::MusicNotation", :foreign_key => "music_session_id"
|
|
|
|
validates :genre, :presence => true
|
|
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
|
|
|
|
before_create :generate_share_token
|
|
before_create :add_to_feed
|
|
|
|
SHARE_TOKEN_LENGTH = 8
|
|
|
|
SEPARATOR = '|'
|
|
|
|
def add_to_feed
|
|
feed = Feed.new
|
|
feed.music_session = self
|
|
end
|
|
|
|
def comment_count
|
|
self.comments.size
|
|
end
|
|
|
|
def grouped_tracks
|
|
tracks = []
|
|
self.music_session_user_histories.each do |msuh|
|
|
user = User.find(msuh.user_id)
|
|
t = Track.new
|
|
t.musician = user
|
|
t.instrument_ids = []
|
|
# this treats each track as a "user", which has 1 or more instruments in the session
|
|
unless msuh.instruments.blank?
|
|
instruments = msuh.instruments.split(SEPARATOR)
|
|
instruments.each do |instrument|
|
|
if !t.instrument_ids.include? instrument
|
|
t.instrument_ids << instrument
|
|
end
|
|
end
|
|
end
|
|
tracks << t
|
|
end
|
|
tracks
|
|
end
|
|
|
|
def can_join? user, as_musician
|
|
if as_musician
|
|
unless user.musician
|
|
return false # "a fan can not join a music session as a musician"
|
|
end
|
|
|
|
if self.musician_access
|
|
if self.approval_required
|
|
return self.invited_musicians.exists?(user)
|
|
else
|
|
return true
|
|
end
|
|
|
|
else
|
|
# the creator can always join, and the invited users can join
|
|
return self.creator == user || self.invited_musicians.exists?(user)
|
|
end
|
|
else
|
|
# it's a fan, and the only way a fan can join is if fan_access is true
|
|
self.fan_access
|
|
end
|
|
end
|
|
|
|
def self.index(current_user, user_id, band_id = nil, genre = nil)
|
|
hide_private = false
|
|
if current_user.id != user_id
|
|
hide_private = false # TODO: change to true once public flag exists
|
|
end
|
|
|
|
query = MusicSession
|
|
.joins(
|
|
%Q{
|
|
LEFT OUTER JOIN
|
|
music_sessions_user_history
|
|
ON
|
|
music_sessions.id = music_sessions_user_history.music_session_id
|
|
}
|
|
)
|
|
.where(
|
|
%Q{
|
|
music_sessions.user_id = '#{user_id}'
|
|
}
|
|
)
|
|
|
|
#query = query.where("public = false") unless !hide_private
|
|
query = query.where("music_sessions.band_id = '#{band_id}") unless band_id.nil?
|
|
query = query.where("music_sessions.genres like '%#{genre}%'") unless genre.nil?
|
|
return query
|
|
end
|
|
|
|
def self.scheduled user
|
|
current_time = Time.now
|
|
query = MusicSession.where("music_sessions.user_id = '#{user.id}'")
|
|
query = query.where("music_sessions.scheduled_start IS NOT NULL AND music_sessions.scheduled_start < '#{current_time + 12.hours}'")
|
|
query = query.order("music_sessions.scheduled_start ASC")
|
|
|
|
return query
|
|
end
|
|
|
|
def self.create user, options
|
|
band = Band.find(options[:band]) unless options[:band].nil?
|
|
|
|
ms = MusicSession.new
|
|
ms.name = options[:name]
|
|
ms.description = options[:description]
|
|
ms.genre_id = (options[:genres].length > 0 ? options[:genres][0] : nil) if options[:genres]
|
|
ms.musician_access = options[:musician_access]
|
|
ms.approval_required = options[:approval_required]
|
|
ms.fan_access = options[:fan_access]
|
|
ms.fan_chat = options[:fan_chat]
|
|
ms.band = band
|
|
ms.legal_policy = options[:legal_policy]
|
|
ms.language = options[:language]
|
|
ms.scheduled_start = options[:start] if options[:start]
|
|
ms.timezone = options[:timezone]
|
|
ms.recurring_mode = options[:recurring_mode] if options[:recurring_mode]
|
|
ms.legal_terms = true
|
|
ms.creator = user
|
|
|
|
ms.save
|
|
|
|
unless ms.errors.any?
|
|
ms.reload
|
|
|
|
options[:rsvp_slots].each do |rs|
|
|
rsvp = RsvpSlot.new
|
|
rsvp.instrument = Instrument.find(rs[:instrument_id])
|
|
rsvp.proficiency_level = rs[:proficiency_level]
|
|
rsvp.music_session = ms
|
|
rsvp.save
|
|
|
|
ms.rsvp_slots << rsvp
|
|
end if options[:rsvp_slots]
|
|
|
|
options[:invitations].each do |invite_id|
|
|
invitation = Invitation.new
|
|
receiver = User.find(invite_id)
|
|
invitation.sender = user
|
|
invitation.receiver = receiver
|
|
invitation.music_session = ms
|
|
invitation.save
|
|
|
|
ms.invitations << invitation
|
|
|
|
Notification.send_session_invitation(receiver, user, ms.id)
|
|
end if options[:invitations]
|
|
|
|
options[:music_notations].each do |notation_id|
|
|
notation = MusicNotation.find(notation_id)
|
|
notation.music_session = ms
|
|
notation.save
|
|
|
|
ms.music_notations << notation
|
|
end if options[:music_notations]
|
|
|
|
ms.save
|
|
end
|
|
|
|
ms
|
|
end
|
|
|
|
def self.update user, options
|
|
music_session = MusicSession.find(options[:id])
|
|
if music_session.creator == current_user
|
|
Notification.send_scheduled_session_cancelled music_session
|
|
music_session.destroy
|
|
|
|
respond_with responder: ApiResponder, :status => 204
|
|
else
|
|
render :json => { :message => ValidationMessages::PERMISSION_VALIDATION_ERROR }, :status => 404
|
|
end
|
|
end
|
|
|
|
def unique_users
|
|
User
|
|
.joins(:music_session_user_histories)
|
|
.group("users.id")
|
|
.order("users.id")
|
|
.where(%Q{ music_sessions_user_history.music_session_id = '#{id}'})
|
|
end
|
|
|
|
# returns one user history per user, with instruments all crammed together, and with total duration
|
|
def unique_user_histories
|
|
MusicSessionUserHistory
|
|
.joins(:user)
|
|
.select("STRING_AGG(instruments, '|') AS total_instruments,
|
|
SUM(date_part('epoch', COALESCE(music_sessions_user_history.session_removed_at, music_sessions_user_history.created_at) - music_sessions_user_history.created_at)) AS total_duration,
|
|
music_sessions_user_history.user_id, music_sessions_user_history.music_session_id, users.first_name, users.last_name, users.photo_url")
|
|
.group("music_sessions_user_history.user_id, music_sessions_user_history.music_session_id, users.first_name, users.last_name, users.photo_url")
|
|
.order("music_sessions_user_history.user_id")
|
|
.where(%Q{ music_sessions_user_history.music_session_id = '#{id}'})
|
|
end
|
|
|
|
def duration_minutes
|
|
end_time = self.session_removed_at || Time.now
|
|
(end_time - self.created_at) / 60.0
|
|
end
|
|
|
|
def music_session_user_histories
|
|
@msuh ||= JamRuby::MusicSessionUserHistory
|
|
.where(:music_session_id => self.id)
|
|
.order('created_at DESC')
|
|
end
|
|
|
|
def comments
|
|
@comments ||= JamRuby::MusicSessionComment
|
|
.where(:music_session_id => self.id)
|
|
.order('created_at DESC')
|
|
end
|
|
|
|
def likes
|
|
@likes ||= JamRuby::MusicSessionLiker
|
|
.where(:music_session_id => self.music_session_id)
|
|
end
|
|
|
|
# these are 'users that are a part of this session'
|
|
# which means are currently in the music_session, or, rsvp'ed, or creator
|
|
def part_of_session? user
|
|
# XXX check RSVP'ed
|
|
user == self.creator || (active_music_session ? active_music_session.users.exists?(user) : false)
|
|
end
|
|
|
|
def is_over?
|
|
active_music_session.nil?
|
|
end
|
|
|
|
def has_mount?
|
|
active_music_session && active_music_session.mount
|
|
end
|
|
|
|
def can_delete? user
|
|
self.creator == user && self.started_at.nil?
|
|
end
|
|
|
|
def legal_policy_url
|
|
# TODO: move to DB or config file
|
|
case legal_policy
|
|
when "standard"
|
|
return "http://www.jamkazam.com/session-legal-policies/standard"
|
|
when "creative"
|
|
return "http://www.jamkazam.com/session-legal-policies/creativecommons"
|
|
when "offline"
|
|
return "http://www.jamkazam.com/session-legal-policies/offline"
|
|
when "jamtracks"
|
|
return "http://www.jamkazam.com/session-legal-policies/jamtracks"
|
|
else
|
|
return ""
|
|
end
|
|
end
|
|
|
|
# next 3 methods are used for the right sidebar on the session info page
|
|
|
|
def approved_rsvps
|
|
users = User.find_by_sql(%Q{select u.id, u.photo_url, u.first_name, u.last_name, rs.instrument_id
|
|
from rsvp_slots rs
|
|
inner join rsvp_requests_rsvp_slots rrrs on rrrs.rsvp_slot_id = rs.id
|
|
inner join rsvp_requests rr on rrrs.rsvp_request_id = rr.id
|
|
inner join users u on u.id = rr.user_id
|
|
where rrrs.chosen = true and rs.music_session_id = '#{self.id}'
|
|
order by u.id})
|
|
|
|
users_collapsed_instruments = []
|
|
user = User.new
|
|
|
|
# build User array with instruments collapsed
|
|
users.each_with_index do |u, index|
|
|
if index == 0 || users[index].id != users[index-1].id
|
|
user = User.new
|
|
user.id = u.id
|
|
user.photo_url = u.photo_url
|
|
user.first_name = u.first_name
|
|
user.last_name = u.last_name
|
|
user["instruments"] = [u.instrument_id]
|
|
users_collapsed_instruments << user
|
|
else
|
|
user["instruments"] << u.instrument_id
|
|
end
|
|
end
|
|
|
|
users_collapsed_instruments
|
|
end
|
|
|
|
def open_slots
|
|
RsvpSlot.find_by_sql(%Q{select rs.*
|
|
from rsvp_slots rs
|
|
left join rsvp_requests_rsvp_slots rrrs on rrrs.rsvp_slot_id = rs.id
|
|
where (rrrs.rsvp_slot_id is null or rrrs.chosen != true)
|
|
and rs.music_session_id = '#{self.id}'})
|
|
end
|
|
|
|
def pending_invitations
|
|
Invitation.find_by_sql(%Q{select u.id, u.photo_url, u.first_name, u.last_name
|
|
from invitations i
|
|
inner join users u on u.id = i.receiver_id
|
|
left join rsvp_requests rr on rr.user_id = i.receiver_id
|
|
where i.music_session_id = '#{self.id}'
|
|
and rr.user_id is null})
|
|
end
|
|
|
|
def recordings
|
|
Recording.where(music_session_id: self.id)
|
|
end
|
|
|
|
def end_history
|
|
self.update_attribute(:session_removed_at, Time.now)
|
|
|
|
|
|
# ensure all user histories are closed
|
|
music_session_user_histories.each do |music_session_user_history|
|
|
music_session_user_history.end_history
|
|
|
|
# then update any users that need their user progress updated
|
|
if music_session_user_history.duration_minutes > 15 && music_session_user_history.max_concurrent_connections >= 3
|
|
music_session_user_history.user.update_progression_field(:first_real_music_session_at)
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
def self.removed_music_session(session_id)
|
|
hist = self
|
|
.where(:id => session_id)
|
|
.limit(1)
|
|
.first
|
|
|
|
hist.end_history if hist
|
|
|
|
Notification.send_session_ended(session_id)
|
|
end
|
|
|
|
def remove_non_alpha_num(token)
|
|
token.gsub(/[^0-9A-Za-z]/, '')
|
|
end
|
|
|
|
private
|
|
|
|
def generate_share_token
|
|
|
|
token = loop do
|
|
token = SecureRandom.urlsafe_base64(SHARE_TOKEN_LENGTH, false)
|
|
token = remove_non_alpha_num(token)
|
|
token.upcase!
|
|
break token unless ShareToken.exists?(token: token)
|
|
end
|
|
|
|
self.share_token = ShareToken.new
|
|
self.share_token.token = token
|
|
self.share_token.shareable_type = "session"
|
|
end
|
|
|
|
def creator_is_musician
|
|
unless creator && creator.musician?
|
|
errors.add(:creator, ValidationMessages::MUST_BE_A_MUSICIAN)
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|