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 :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::MusicNotations", :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 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 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