2014-05-27 03:52:59 +00:00
require 'iso-639'
2012-10-03 03:50:23 +00:00
module JamRuby
class MusicSession < ActiveRecord :: Base
2014-01-21 14:51:03 +00:00
2014-06-17 22:16:49 +00:00
@@log = Logging . logger [ MusicSession ]
2014-05-15 17:22:21 +00:00
NO_RECURRING = 'once'
RECURRING_WEEKLY = 'weekly'
RECURRING_MODES = [ NO_RECURRING , RECURRING_WEEKLY ]
2014-06-06 07:04:52 +00:00
attr_accessor :legal_terms , :language_description , :scheduled_start_time , :access_description
2014-05-29 16:32:22 +00:00
2014-06-15 19:49:43 +00:00
attr_accessor :approved_rsvps , :open_slots , :pending_invitations
2014-05-06 22:50:41 +00:00
self . table_name = " music_sessions "
2014-03-09 13:38:46 +00:00
2014-05-06 13:34:38 +00:00
self . primary_key = 'id'
2012-10-25 12:11:10 +00:00
2014-05-06 21:17:26 +00:00
belongs_to :creator , :class_name = > 'JamRuby::User' , :foreign_key = > :user_id , :inverse_of = > :music_session_histories
2014-05-06 13:34:38 +00:00
2014-05-06 21:17:26 +00:00
belongs_to :band , :class_name = > 'JamRuby::Band' , :foreign_key = > :band_id , :inverse_of = > :music_sessions
2014-05-06 13:34:38 +00:00
2014-05-06 21:17:26 +00:00
belongs_to :active_music_session , :class_name = > 'JamRuby::ActiveMusicSession' , foreign_key : :music_session_id
2014-05-06 13:34:38 +00:00
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 "
2014-05-24 07:58:47 +00:00
has_many :session_info_comments , :class_name = > " JamRuby::SessionInfoComment " , :foreign_key = > " music_session_id "
2014-05-06 13:34:38 +00:00
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
2014-05-06 21:17:26 +00:00
belongs_to :genre , :class_name = > " JamRuby::Genre " , :inverse_of = > :music_sessions , :foreign_key = > 'genre_id'
2014-05-06 13:34:38 +00:00
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
2014-05-19 15:35:38 +00:00
has_many :rsvp_slots , :class_name = > " JamRuby::RsvpSlot " , :foreign_key = > " music_session_id " , :dependent = > :destroy
2014-05-26 05:35:57 +00:00
has_many :music_notations , :class_name = > " JamRuby::MusicNotation " , :foreign_key = > " music_session_id "
2013-07-23 20:20:39 +00:00
2014-05-06 21:17:26 +00:00
validates :genre , :presence = > true
2013-07-26 08:07:24 +00:00
validates :description , :presence = > true , :no_profanity = > true
2012-11-16 02:50:03 +00:00
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 ] }
2014-07-08 18:34:03 +00:00
validates :is_unstructured_rsvp , :inclusion = > { :in = > [ true , false ] }
2013-06-11 01:25:42 +00:00
validates :legal_terms , :inclusion = > { :in = > [ true ] } , :on = > :create
2012-11-30 15:23:43 +00:00
validates :creator , :presence = > true
validate :creator_is_musician
2014-05-06 13:34:38 +00:00
before_create :generate_share_token
before_create :add_to_feed
2014-03-06 02:30:40 +00:00
2014-05-06 13:34:38 +00:00
SHARE_TOKEN_LENGTH = 8
2014-03-06 04:26:17 +00:00
2014-05-06 13:34:38 +00:00
SEPARATOR = '|'
2014-03-06 04:26:17 +00:00
2014-05-06 13:34:38 +00:00
def add_to_feed
feed = Feed . new
feed . music_session = self
2014-02-06 21:02:04 +00:00
end
2014-05-06 13:34:38 +00:00
def comment_count
self . comments . size
2014-01-21 14:51:03 +00:00
end
2014-01-12 17:51:06 +00:00
2014-06-03 05:10:25 +00:00
# copies all relevant info for the recurring session
def copy
MusicSession . transaction do
# copy base music_session data
new_session = MusicSession . new
new_session . description = self . description
new_session . user_id = self . user_id
new_session . band_id = self . band_id
new_session . fan_access = self . fan_access
# TODO: confirm this logic
2014-06-05 04:55:15 +00:00
# new_session.scheduled_start = self.scheduled_start + self.scheduled_duration
2014-06-03 05:10:25 +00:00
new_session . scheduled_duration = self . scheduled_duration
new_session . musician_access = self . musician_access
new_session . approval_required = self . approval_required
new_session . fan_chat = self . fan_chat
new_session . genre_id = self . genre_id
new_session . legal_policy = self . legal_policy
new_session . language = self . language
new_session . name = self . name
new_session . recurring_mode = self . recurring_mode
new_session . timezone = self . timezone
new_session . open_rsvps = self . open_rsvps
2014-06-05 03:17:45 +00:00
2014-06-04 05:47:28 +00:00
# copy rsvp_slots, rsvp_requests, and rsvp_requests_rsvp_slots
2014-06-03 05:10:25 +00:00
RsvpSlot . find_each ( :conditions = > " music_session_id = ' #{ self . id } ' " ) do | slot |
new_slot = RsvpSlot . new
new_slot . instrument_id = slot . instrument_id
new_slot . proficiency_level = slot . proficiency_level
2014-06-05 04:55:15 +00:00
new_session . rsvp_slots << new_slot
2014-06-04 05:47:28 +00:00
# get the request for this slot that was approved (should only be ONE)
rsvp_request_slot = RsvpRequestRsvpSlot . where ( " chosen = true AND rsvp_slot_id = ? " , slot . id ) . first
unless rsvp_request_slot . nil?
rsvp = RsvpRequest . find_by_id ( rsvp_request_slot . rsvp_request_id )
new_rsvp = RsvpRequest . new
new_rsvp . user_id = rsvp . user_id
new_rsvp_req_slot = RsvpRequestRsvpSlot . new
2014-06-05 04:55:15 +00:00
new_rsvp_req_slot . rsvp_request = new_rsvp
new_rsvp_req_slot . rsvp_slot = new_slot
2014-06-04 05:47:28 +00:00
new_rsvp_req_slot . chosen = true
2014-06-05 03:17:45 +00:00
# if this slot was not chosen, try to get any RSVPs that were 1-time cancellations and copy those
else
rejected_req_slots = RsvpRequestRsvpSlot . where ( " (chosen = false OR chosen is null) AND rsvp_slot_id = ? " , slot . id ) . order ( " created_at ASC " )
rejected_req_slots . each do | req_slot |
# get RsvpRequest corresponding to this RsvpRequestRsvpSlot
rsvp = RsvpRequest . find_by_id ( req_slot . rsvp_request_id )
# if the RSVP was canceled (but all future sessions were NOT canceled), then copy this one and break
if rsvp . canceled && ! rsvp . cancel_all
new_rsvp = RsvpRequest . new
new_rsvp . user_id = rsvp . user_id
new_rsvp_req_slot = RsvpRequestRsvpSlot . new
2014-06-05 04:55:15 +00:00
new_rsvp_req_slot . rsvp_request = new_rsvp
new_rsvp_req_slot . rsvp_slot = new_slot
2014-06-05 03:17:45 +00:00
new_rsvp_req_slot . chosen = true
break
end
2014-06-04 05:47:28 +00:00
end
end
2014-06-03 05:10:25 +00:00
end
# copy music_notations
MusicNotation . find_each ( :conditions = > " music_session_id = ' #{ self . id } ' " ) do | notation |
new_notation = MusicNotation . new
new_notation . user_id = notation . user_id
2014-06-05 04:55:15 +00:00
new_notation . music_session = new_session
2014-06-03 05:10:25 +00:00
new_notation . file_url = notation . file_url
2014-06-04 05:47:28 +00:00
# new_notation.file_name = notation.file_name
2014-06-03 05:10:25 +00:00
new_notation . size = notation . size
2014-06-05 04:55:15 +00:00
new_session . music_notations << new_notation
2014-06-03 05:10:25 +00:00
end
2014-06-05 04:55:15 +00:00
new_session . save
2014-06-03 05:10:25 +00:00
# mark the next session as scheduled
self . next_session_scheduled = true
self . save
end
end
2014-05-06 13:34:38 +00:00
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
2014-01-05 03:47:23 +00:00
end
2014-05-06 13:34:38 +00:00
tracks
2014-01-05 03:47:23 +00:00
end
2014-05-24 07:58:47 +00:00
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
2014-05-06 13:34:38 +00:00
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
2012-11-30 15:23:43 +00:00
end
2012-12-06 01:56:12 +00:00
query = MusicSession
2014-05-06 13:34:38 +00:00
. joins (
2012-12-05 19:10:58 +00:00
%Q{
2012-12-06 01:56:12 +00:00
LEFT OUTER JOIN
2014-05-06 13:34:38 +00:00
music_sessions_user_history
2012-12-06 01:56:12 +00:00
ON
2014-05-06 22:50:41 +00:00
music_sessions . id = music_sessions_user_history . music_session_id
2012-12-06 01:56:12 +00:00
}
)
2014-05-06 13:34:38 +00:00
. where (
2014-03-06 02:30:40 +00:00
%Q{
2014-05-06 22:50:41 +00:00
music_sessions . user_id = '#{user_id}'
2012-12-06 01:56:12 +00:00
}
)
2014-03-06 02:30:40 +00:00
2014-05-06 13:34:38 +00:00
#query = query.where("public = false") unless !hide_private
2014-05-06 22:50:41 +00:00
query = query . where ( " music_sessions.band_id = ' #{ band_id } " ) unless band_id . nil?
2014-07-02 13:57:18 +00:00
query = query . where ( " music_sessions.genres like '% #{ genre } %' " ) unless genre . nil?
2012-12-06 01:56:12 +00:00
return query
2012-11-30 15:23:43 +00:00
end
2012-11-02 06:51:52 +00:00
2014-05-21 19:11:50 +00:00
def self . scheduled user
2014-07-10 16:27:26 +00:00
query = MusicSession . where ( " music_sessions.canceled = FALSE " )
query = query . where ( " music_sessions.user_id = ' #{ user . id } ' " )
2014-06-24 15:33:31 +00:00
query = query . where ( " music_sessions.scheduled_start > NOW() - '12 hour'::INTERVAL " )
2014-06-04 19:19:45 +00:00
query = query . where ( " music_session_id IS NULL " )
2014-05-21 19:11:50 +00:00
query = query . order ( " music_sessions.scheduled_start ASC " )
2014-06-24 15:33:31 +00:00
query
2014-05-21 19:11:50 +00:00
end
2014-06-29 13:54:51 +00:00
def self . scheduled_rsvp user
2014-07-10 16:27:26 +00:00
MusicSession . where ( %Q{ music_sessions.canceled = FALSE AND music_sessions.id in (
2014-06-29 13:54:51 +00:00
select distinct ( rs . music_session_id )
from rsvp_slots rs
where rs . id in (
select rrrs . rsvp_slot_id
from rsvp_requests rr
inner join rsvp_requests_rsvp_slots rrrs on rr . id = rrrs . rsvp_request_id
where rr . user_id = '#{user.id}'
)
) }
)
end
2014-05-19 15:35:38 +00:00
def self . create user , options
band = Band . find ( options [ :band ] ) unless options [ :band ] . nil?
2014-05-15 15:49:06 +00:00
ms = MusicSession . new
2014-05-19 15:35:38 +00:00
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 ]
2014-05-15 15:49:06 +00:00
ms . band = band
2014-05-19 15:35:38 +00:00
ms . legal_policy = options [ :legal_policy ]
ms . language = options [ :language ]
2014-06-18 22:15:56 +00:00
ms . scheduled_start = options [ :start ]
ms . scheduled_duration = options [ :duration ] . to_i * 1 . minutes if options [ :duration ]
2014-05-19 15:35:38 +00:00
ms . recurring_mode = options [ :recurring_mode ] if options [ :recurring_mode ]
2014-06-19 18:43:21 +00:00
ms . timezone = options [ :timezone ] if options [ :timezone ]
2014-05-15 15:49:06 +00:00
ms . legal_terms = true
2014-07-03 04:51:54 +00:00
ms . open_rsvps = options [ :open_rsvps ] if options [ :open_rsvps ]
2014-05-15 15:49:06 +00:00
ms . creator = user
2014-07-08 18:34:03 +00:00
ms . is_unstructured_rsvp = options [ :isUnstructuredRsvp ] if options [ :isUnstructuredRsvp ]
2014-05-15 15:49:06 +00:00
ms . save
2014-05-19 15:35:38 +00:00
unless ms . errors . any?
ms . reload
2014-06-18 22:15:56 +00:00
rsvp_slot_ids = [ ]
self_rsvp_slot_ids = [ ]
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
2014-06-19 18:43:21 +00:00
if rs [ :approve ] == true
2014-06-18 22:15:56 +00:00
self_rsvp_slot_ids . push rsvp . id
else
rsvp_slot_ids . push rsvp . id
end
end if options [ :rsvp_slots ]
2014-06-19 18:43:21 +00:00
RsvpRequest . create ( { session_id : ms . id , rsvp_slots : self_rsvp_slot_ids , :autoapprove = > true } , user )
2014-05-19 15:35:38 +00:00
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
2014-06-18 22:15:56 +00:00
Notification . send_scheduled_session_invitation ( ms , receiver )
2014-05-19 15:35:38 +00:00
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
2014-05-15 15:49:06 +00:00
end
2014-05-22 09:49:35 +00:00
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
2014-05-06 13:34:38 +00:00
def unique_users
User
. joins ( :music_session_user_histories )
. group ( " users.id " )
. order ( " users.id " )
2014-05-06 21:17:26 +00:00
. where ( %Q{ music_sessions_user_history.music_session_id = ' #{ id } ' } )
2012-11-02 06:51:52 +00:00
end
2014-05-06 13:34:38 +00:00
# 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 " )
2014-05-06 21:17:26 +00:00
. where ( %Q{ music_sessions_user_history.music_session_id = ' #{ id } ' } )
2012-11-02 06:51:52 +00:00
end
2014-05-06 13:34:38 +00:00
def duration_minutes
end_time = self . session_removed_at || Time . now
( end_time - self . created_at ) / 60 . 0
2012-11-02 06:51:52 +00:00
end
2014-05-06 13:34:38 +00:00
def music_session_user_histories
@msuh || = JamRuby :: MusicSessionUserHistory
2014-05-06 21:17:26 +00:00
. where ( :music_session_id = > self . id )
2014-05-06 13:34:38 +00:00
. order ( 'created_at DESC' )
2012-10-03 03:50:23 +00:00
end
2013-11-03 20:55:55 +00:00
2014-05-06 13:34:38 +00:00
def comments
@comments || = JamRuby :: MusicSessionComment
2014-05-06 21:17:26 +00:00
. where ( :music_session_id = > self . id )
2014-05-06 13:34:38 +00:00
. order ( 'created_at DESC' )
2014-01-06 20:35:35 +00:00
end
2014-05-06 13:34:38 +00:00
def likes
@likes || = JamRuby :: MusicSessionLiker
. where ( :music_session_id = > self . music_session_id )
2013-11-03 20:55:55 +00:00
end
2014-05-06 13:34:38 +00:00
# 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
2014-05-06 21:17:26 +00:00
user == self . creator || ( active_music_session ? active_music_session . users . exists? ( user ) : false )
2014-01-05 03:47:23 +00:00
end
2014-05-06 13:34:38 +00:00
def is_over?
2014-05-06 21:17:26 +00:00
active_music_session . nil?
2013-11-03 20:55:55 +00:00
end
2014-05-06 13:34:38 +00:00
def has_mount?
2014-05-06 21:17:26 +00:00
active_music_session && active_music_session . mount
2013-11-03 20:55:55 +00:00
end
2014-07-10 16:38:17 +00:00
def can_cancel? user
self . creator == user
2014-05-24 07:58:47 +00:00
end
def legal_policy_url
2014-06-30 21:24:56 +00:00
# TODO: move to DB or config file or helper
2014-05-24 07:58:47 +00:00
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 " "
2014-05-26 05:35:57 +00:00
end
end
2014-05-27 03:52:59 +00:00
def language_description
2014-05-29 05:28:40 +00:00
if self . language . blank?
2014-07-02 13:57:18 +00:00
self . language = " en "
2014-05-29 05:28:40 +00:00
end
iso639Details = ISO_639 . find_by_code ( self . language )
unless iso639Details . blank?
return iso639Details . english_name
else
return " English "
end
2014-05-27 03:52:59 +00:00
end
2014-06-18 22:15:56 +00:00
def get_timezone
tz = nil
if timezone . blank?
tz = ActiveSupport :: TimeZone [ " Central Time (US & Canada) " ]
self . timezone = tz . name + ',' + tz . tzinfo . name
else
tz = ActiveSupport :: TimeZone [ self . timezone . split ( ',' ) [ 0 ] ]
end
tz
end
2014-05-27 03:52:59 +00:00
def scheduled_start_time
unless self . scheduled_start . nil?
2014-07-02 13:57:18 +00:00
self . scheduled_start . utc . strftime " %a %e %B %Y "
2014-05-27 03:52:59 +00:00
else
" "
end
end
2014-07-01 04:36:06 +00:00
def scheduled_end_time
end
2014-06-09 02:43:07 +00:00
def timezone_description
2014-06-18 22:15:56 +00:00
self . get_timezone . to_s
2014-06-09 02:43:07 +00:00
end
def musician_access_description
if self . musician_access && self . approval_required
" Musicians may join by approval "
elsif self . musician_access && ! self . approval_required
" Musicians may join at will "
elsif ! self . musician_access && ! self . approval_required
" Only RSVP musicians may join "
2014-05-27 04:32:14 +00:00
end
2014-06-09 02:43:07 +00:00
end
2014-05-27 04:32:14 +00:00
2014-06-09 02:43:07 +00:00
def fan_access_description
if self . fan_access && self . fan_chat
" Fans may listen, chat with the band "
elsif self . fan_access && ! self . fan_chat
" Fans may listen, chat with each other "
elsif ! self . fan_access && ! self . fan_chat
" Fans may not listen to session "
2014-05-27 04:32:14 +00:00
end
2014-06-09 02:43:07 +00:00
end
2014-05-27 04:32:14 +00:00
2014-06-09 02:43:07 +00:00
def access_description
2014-07-10 20:22:00 +00:00
" #{ musician_access_description } . #{ fan_access_description } . "
2014-05-27 04:32:14 +00:00
end
2014-05-28 02:35:26 +00:00
# retrieve users that have approved RSVPs
2014-05-26 05:35:57 +00:00
def approved_rsvps
2014-07-10 20:22:00 +00:00
User . find_by_sql ( %Q{ select distinct ON(u.id) u.id, u.photo_url, u.first_name, u.last_name, json_agg(ii.id) as instrument_ids, json_agg(ii.description) as instrument_descriptions, json_agg(rs.proficiency_level) as instrument_proficiencies, json_agg(rr.id) as rsvp_request_ids
2014-06-19 18:43:21 +00:00
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
2014-07-10 20:22:00 +00:00
left join instruments ii on ii . id = rs . instrument_id
2014-06-19 18:43:21 +00:00
inner join users u on u . id = rr . user_id
2014-07-10 20:22:00 +00:00
where rrrs . chosen = true AND rs . music_session_id = '#{self.id}' AND rr . canceled != TRUE
group by u . id order by u . id }
2014-06-19 18:43:21 +00:00
)
end
2014-05-28 02:35:26 +00:00
# get all slots for this session and perform a set difference with all chosen slots;
# this will return those that are not filled yet
2014-07-08 18:34:03 +00:00
# this method excludes rsvp_slots marked as 'is_unstructured_rsvp = true'
2014-05-26 05:35:57 +00:00
def open_slots
2014-06-19 18:43:21 +00:00
RsvpSlot . find_by_sql ( %Q{ select rs.*, ii.description
from rsvp_slots rs
inner join instruments ii on ii . id = rs . instrument_id
where rs . music_session_id = '#{self.id}'
2014-07-08 18:34:03 +00:00
and rs . is_unstructured_rsvp = false
2014-06-19 18:43:21 +00:00
except
select distinct rs . * , iii . description
from rsvp_slots rs
inner join instruments iii on iii . id = rs . instrument_id
inner join rsvp_requests_rsvp_slots rrrs on rrrs . rsvp_slot_id = rs . id
where rs . music_session_id = '#{self.id}'
2014-05-26 18:00:34 +00:00
and rrrs . chosen = true
}
)
2014-05-26 05:35:57 +00:00
end
2014-05-28 02:35:26 +00:00
# retrieve users that have invitations but have not submitted an RSVP request for this session
2014-05-26 05:35:57 +00:00
def pending_invitations
2014-05-28 02:35:26 +00:00
User . find_by_sql ( %Q{ select u.id, u.email, u.photo_url, u.first_name, u.last_name
from users u
inner join invitations i on u . id = i . receiver_id
2014-05-26 05:35:57 +00:00
left join rsvp_requests rr on rr . user_id = i . receiver_id
where i . music_session_id = '#{self.id}'
2014-06-14 17:13:45 +00:00
and rr . user_id is null }
)
2014-05-24 07:58:47 +00:00
end
2014-06-29 13:54:51 +00:00
# retrieve pending RsvpRequests
def pending_rsvp_requests
RsvpRequest . index ( self , nil , { status : 'pending' } )
end
2014-05-06 13:34:38 +00:00
def recordings
Recording . where ( music_session_id : self . id )
2014-01-05 03:47:23 +00:00
end
2014-05-06 13:34:38 +00:00
def end_history
self . update_attribute ( :session_removed_at , Time . now )
2014-01-05 03:47:23 +00:00
2012-10-25 12:11:10 +00:00
2014-05-06 13:34:38 +00:00
# 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
2014-03-03 22:13:23 +00:00
end
2014-05-06 13:34:38 +00:00
def self . removed_music_session ( session_id )
hist = self
2014-05-06 21:17:26 +00:00
. where ( :id = > session_id )
2014-05-06 13:34:38 +00:00
. limit ( 1 )
. first
hist . end_history if hist
Notification . send_session_ended ( session_id )
2014-03-09 13:38:46 +00:00
end
2014-05-06 13:34:38 +00:00
def remove_non_alpha_num ( token )
token . gsub ( / [^0-9A-Za-z] / , '' )
2014-03-09 13:38:46 +00:00
end
2014-06-15 02:27:34 +00:00
def tag
nil unless has_attribute? ( :tag )
a = read_attribute ( :tag )
a . nil? ? nil : a . to_i
end
def latency
nil unless has_attribute? ( :latency )
a = read_attribute ( :latency )
a . nil? ? nil : a . to_i
end
2012-10-25 12:11:10 +00:00
2014-06-17 19:10:24 +00:00
# initialize the two temporary tables we use to drive sms_index and sms_users
def self . sms_init ( current_user , options = { } )
client_id = options [ :client_id ]
connection = Connection . where ( user_id : current_user . id , client_id : client_id ) . first!
my_locidispid = connection . locidispid
# 13 is an average audio gear value we use if they have not qualified any gear
my_audio_latency = connection . last_jam_audio_latency || current_user . last_jam_audio_latency || 13
self . connection . execute ( " select sms_index(' #{ current_user . id } '::varchar, #{ my_locidispid } ::bigint, #{ my_audio_latency } ::integer) " ) ;
end
# Generate a list of music sessions (that are active) filtered by genre, language, keyword, and sorted
# (and tagged) by rsvp'd (1st), invited (2nd), and musician can join (3rd). within a group tagged the
# same, sorted by score. date seems irrelevant as these are active sessions. sms_init must be called
# first.
def self . sms_query ( current_user , options = { } )
client_id = options [ :client_id ]
genre = options [ :genre ]
lang = options [ :lang ]
keyword = options [ :keyword ]
offset = options [ :offset ]
limit = options [ :limit ]
2014-06-17 22:16:49 +00:00
day = options [ :day ]
timezone_offset = options [ :timezone_offset ]
2014-06-17 19:10:24 +00:00
query = MusicSession
. select ( 'music_sessions.*' )
# this is not really needed when sms_music_session_tmp is joined
# unless there is something specific we need out of active_music_sessions
# query = query.joins(
# %Q{
# INNER JOIN
# active_music_sessions
# ON
# active_music_sessions.id = music_sessions.id
# }
# )
# .select('1::integer as tag, 15::integer as latency')
# integrate sms_music_session_tmp into the processing
# then we can join sms_music_session_tmp and not join active_music_sessions
query = query . joins (
%Q{
INNER JOIN
sms_music_session_tmp
ON
sms_music_session_tmp . music_session_id = music_sessions . id
}
)
. select ( 'sms_music_session_tmp.tag, sms_music_session_tmp.latency' )
query = query . order (
%Q{
tag , latency , music_sessions . id
}
)
. group (
%Q{
tag , latency , music_sessions . id
}
)
2014-06-17 22:16:49 +00:00
# if not specified, default offset to 0
offset || = 0
offset = offset . to_i
# if not specified, default limit to 20
limit || = 20
limit = limit . to_i
query = query . offset ( offset )
query = query . limit ( limit )
query = query . where ( " music_sessions.genre_id = ? " , genre ) unless genre . blank?
query = query . where ( 'music_sessions.language = ?' , lang ) unless lang . blank?
query = query . where ( " (description_tsv @@ to_tsquery('jamenglish', ?)) " , keyword + ':*' ) unless keyword . blank?
if ! day . blank? && ! timezone_offset . blank?
begin
day = Date . parse ( day )
next_day = day + 1
timezone_offset = timezone_offset . to_i
2014-07-02 13:57:18 +00:00
if timezone_offset == 0
timezone_offset = '' # no offset to specify in this case
elsif timezone_offset > 0
2014-06-25 15:21:36 +00:00
timezone_offset = " + #{ timezone_offset } "
end
2014-06-17 22:16:49 +00:00
query = query . where ( " scheduled_start BETWEEN TIMESTAMP WITH TIME ZONE ' #{ day } 00:00:00 #{ timezone_offset } '
AND TIMESTAMP WITH TIME ZONE '#{next_day} 00:00:00#{timezone_offset}' " )
rescue Exception = > e
# do nothing. bad date probably
@@log . warn ( " unable to parse day= #{ day } , timezone_offset= #{ timezone_offset } , e= #{ e } " )
2014-06-17 19:10:24 +00:00
end
end
return query
end
# returns the set of users in a music_sessions and the music_session they are in and their latency.
# sms_init must be called first.
# user.audio_latency / 2 , + other_user.audio_latency of them / 2, + network latency /2
def self . sms_users
return User . select ( 'users.*, sms_users_tmp.music_session_id, sms_users_tmp.latency' )
. joins (
%Q{
INNER JOIN
sms_users_tmp
ON
sms_users_tmp . user_id = users . id
}
)
. order ( 'sms_users_tmp.music_session_id, sms_users_tmp.user_id' )
end
# wrap me in a transaction!
# note that these queries must be actualized before the end of the transaction
# else the temporary tables created by sms_init will be gone.
def self . sms_index ( current_user , params )
MusicSession . sms_init ( current_user , params )
2014-06-17 22:16:49 +00:00
music_sessions = MusicSession . sms_query ( current_user , params ) . all
2014-06-17 19:10:24 +00:00
music_session_users = MusicSession . sms_users . all
user_scores = { }
music_session_users . each do | user |
user_scores [ user . id ] = { latency : user . latency }
end
[ music_sessions , user_scores ]
end
private
2014-05-06 13:34:38 +00:00
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 "
2014-02-20 07:45:51 +00:00
end
2014-05-06 13:34:38 +00:00
2014-05-06 21:17:26 +00:00
def creator_is_musician
unless creator && creator . musician?
errors . add ( :creator , ValidationMessages :: MUST_BE_A_MUSICIAN )
end
end
2012-10-03 03:50:23 +00:00
end
end