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-08-19 01:41:44 +00:00
include HtmlSanitize
html_sanitize strict : [ :name , :description ]
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-10-12 07:49:31 +00:00
UNSTARTED_INTERVAL_DAYS_SKIP = '14' # days past scheduled start to skip in query
UNSTARTED_INTERVAL_DAYS_PURGE = '28' # days past scheduled start to purge session
2014-10-14 03:54:58 +00:00
UNSTARTED_INTERVAL_DAYS_PURGE_RECUR = '28' # days past scheduled start to purge recurddingsession
2014-10-12 07:49:31 +00:00
2014-10-21 07:27:26 +00:00
CREATE_TYPE_START_SCHEDULED = 'start-scheduled'
CREATE_TYPE_SCHEDULE_FUTURE = 'schedule-future'
CREATE_TYPE_RSVP = 'rsvp'
CREATE_TYPE_IMMEDIATE = 'immediately'
CREATE_TYPE_QUICK_START = 'quick-start'
2016-04-06 02:23:15 +00:00
CREATE_TYPE_LESSON = 'lesson'
2016-04-22 19:59:48 +00:00
CREATE_TYPE_QUICK_PUBLIC = 'quick-public'
2014-10-21 07:27:26 +00:00
2014-07-30 20:11:03 +00:00
attr_accessor :legal_terms , :language_description , :access_description , :scheduling_info_changed
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
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
2015-12-08 02:25:43 +00:00
belongs_to :session_controller , :class_name = > 'JamRuby::User' , :foreign_key = > :session_controller_id , :inverse_of = > :controlled_sessions
2016-04-06 02:23:15 +00:00
belongs_to :lesson_session , :class_name = > " JamRuby::LessonSession "
2015-12-08 02:25:43 +00:00
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
2014-07-14 21:43:46 +00:00
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'
2015-07-16 15:58:03 +00:00
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 "
2014-05-06 13:34:38 +00:00
has_many :invited_musicians , :through = > :invitations , :class_name = > " JamRuby::User " , :foreign_key = > " receiver_id " , :source = > :receiver
2015-07-16 15:58:03 +00:00
has_many :fan_invitations , :foreign_key = > " music_session_id " , :inverse_of = > :music_session , :class_name = > " JamRuby::FanInvitation "
2014-05-06 13:34:38 +00:00
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
2020-04-29 20:51:50 +00:00
has_many :rsvp_requests , :class_name = > " JamRuby::RsvpRequest " , :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 "
2016-01-04 03:38:30 +00:00
has_many :jam_track_session , :class_name = > " JamRuby::JamTrackSession "
2017-04-14 19:34:18 +00:00
has_many :broadcasts , :class_name = > " JamRuby::Broadcast "
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 ] }
2020-05-06 19:42:52 +00:00
validates :friends_can_join , :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
2014-07-11 22:21:55 +00:00
validates :timezone , presence : true , if : Proc . new { | session | session . scheduled_start }
validates :scheduled_duration , presence : true , if : Proc . new { | session | session . scheduled_start }
2012-11-30 15:23:43 +00:00
validate :creator_is_musician
2014-07-11 22:21:55 +00:00
validate :validate_timezone
2012-11-30 15:23:43 +00:00
2014-05-06 13:34:38 +00:00
before_create :generate_share_token
2014-07-11 22:21:55 +00:00
#before_save :update_scheduled_start
2014-07-30 20:11:03 +00:00
before_save :check_scheduling_info_changed
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
2017-04-14 19:34:18 +00:00
def current_broadcast
Broadcast . current_broadcast ( self )
end
2017-06-10 20:34:08 +00:00
def unlink_broadcast
Broadcast . unlink_broadcast ( self )
end
def create_broadcast ( user , broadcast_options , google_client = GoogleClient . new )
2017-04-14 19:34:18 +00:00
broadcast = current_broadcast
if broadcast . nil?
2017-06-10 20:34:08 +00:00
broadcast = create_youtube_broadcast ( user , broadcast_options , google_client )
2017-04-14 19:34:18 +00:00
else
2017-06-10 20:34:08 +00:00
result = refresh_youtube_broadcast ( user , broadcast , broadcast_options , google_client )
2017-04-14 19:34:18 +00:00
# check against Youtube the real state of broadcast, to see if we need a new one?
2017-06-10 20:34:08 +00:00
if result . nil?
unlink_broadcast # user probably deleted it, or marked it complete.
broadcast = create_youtube_broadcast ( user , broadcast_options , google_client )
end
2017-04-14 19:34:18 +00:00
end
broadcast
end
2017-06-23 23:39:22 +00:00
def create_stream ( user , broadcast_options , google_client = JamRuby :: GoogleClient . new )
2017-04-14 19:34:18 +00:00
2017-06-10 20:34:08 +00:00
broadcast = create_broadcast ( user , broadcast_options , google_client )
2017-04-14 19:34:18 +00:00
stream = current_stream ( broadcast )
if stream . nil?
2017-06-10 20:34:08 +00:00
create_youtube_stream ( user , broadcast , broadcast_options , google_client )
bind_broadcast ( user , broadcast , google_client )
2017-04-14 19:34:18 +00:00
else
2017-06-10 20:34:08 +00:00
bind_broadcast ( user , broadcast , google_client )
2017-04-14 19:34:18 +00:00
end
end
def current_stream ( broadcast )
broadcast . stream_id
end
2017-06-23 23:39:22 +00:00
def refresh_stream ( user , broadcast )
stream_data = get_livestream ( user )
broadcast . stream_id = stream_data [ " id " ]
broadcast . stream_status = stream_data [ " status " ] [ " streamStatus " ]
broadcast . stream_name = stream_data [ " cdn " ] [ " ingestionInfo " ] [ " streamName " ]
broadcast . stream_address = stream_data [ " cdn " ] [ " ingestionInfo " ] [ " ingestionAddress " ]
broadcast . stream_data = stream_data . to_json
broadcast . save
end
2017-06-10 20:34:08 +00:00
def refresh_youtube_broadcast ( user , broadcast , broadcast_data = nil , google_client = GoogleClient . new )
if broadcast_data . nil?
broadcast_data = google_client . get_broadcast ( user , broadcast . broadcast_id )
end
if broadcast_data
broadcast . update_broadcast_data ( broadcast_data )
broadcast . save
true
else
# this path makes sense if the user deleted the video on the server, but we do not yet know it
nil
end
end
def get_livestream ( user , google_client = GoogleClient . new )
broadcast = current_broadcast
if broadcast . nil?
nil
else
stream_id = current_stream ( broadcast )
if stream_id . nil?
nil
else
return google_client . get_livestream ( user , stream_id )
end
end
2017-04-14 19:34:18 +00:00
end
2017-06-10 20:34:08 +00:00
def get_broadcast ( user , google_client = UserManager . new . get_google_client )
broadcast = current_broadcast
if broadcast . nil?
nil
else
broadcast_data = google_client . get_broadcast ( user , broadcast . broadcast_id )
broadcast . update_broadcast_data ( broadcast_data )
broadcast . save
broadcast
end
end
def set_livestream_live ( user , google_client = GoogleClient . new )
livestream = get_livestream ( user , google_client )
if livestream
2017-06-23 23:39:22 +00:00
# if livestream["status"]["streamStatus"] == "active"
2017-06-10 20:34:08 +00:00
transition_broadcast ( user , broadcast , 'live' , google_client )
2017-06-23 23:39:22 +00:00
# end
2017-06-10 20:34:08 +00:00
else
end
end
2017-04-14 19:34:18 +00:00
# https://developers.google.com/youtube/v3/live/docs/liveStreams#resource
2017-06-10 20:34:08 +00:00
def create_youtube_stream ( user , broadcast , broadcast_options , google_client = GoogleClient . new )
2017-04-14 19:34:18 +00:00
# https://developers.google.com/youtube/v3/live/docs/liveStreams/insert
# required
# snippet.title
# cdn.format
# cdn.ingestionType (deprecated - use resolution/framerate)
stream_options = { }
stream_options [ :snippet ] || = { }
stream_options [ :snippet ] [ :title ] || = name
2017-06-10 20:34:08 +00:00
stream_options [ :snippet ] [ :isDefaultStream ] || = false
2017-04-14 19:34:18 +00:00
#broadcast_options[:snippet][:scheduledEndTime] = end_time.utc.iso8601
stream_options [ :cdn ] || = { }
stream_options [ :cdn ] [ :frameRate ] || = '30fps'
stream_options [ :cdn ] [ :resolution ] || = '360p'
stream_options [ :cdn ] [ :ingestionType ] || = 'rtmp'
stream_options [ :contentDetails ] || = { }
2017-06-10 20:34:08 +00:00
stream_options [ :contentDetails ] [ :isReusable ] || = false
stream_options [ :contentDetails ] [ :monitorStream ] || = { }
stream_options [ :contentDetails ] [ :monitorStream ] [ :enableMonitorStream ] || = false
2017-04-14 19:34:18 +00:00
stream_options = google_client . create_stream ( user , stream_options )
broadcast . stream_id = stream_options [ " id " ]
broadcast . stream_status = stream_options [ " status " ] [ " streamStatus " ]
broadcast . stream_name = stream_options [ " cdn " ] [ " ingestionInfo " ] [ " streamName " ]
broadcast . stream_address = stream_options [ " cdn " ] [ " ingestionInfo " ] [ " ingestionAddress " ]
broadcast . stream_data = stream_options . to_json
broadcast . save!
broadcast
end
2017-06-10 20:34:08 +00:00
def create_youtube_broadcast ( user , broadcast_options , google_client = GoogleClient . new )
2017-04-14 19:34:18 +00:00
start_time , end_time = youtube_times
broadcast_options || = { }
broadcast_options [ :snippet ] || = { }
broadcast_options [ :snippet ] [ :title ] || = name
broadcast_options [ :snippet ] [ :description ] || = description
broadcast_options [ :snippet ] [ :scheduledStartTime ] = start_time . utc . iso8601
#broadcast_options[:snippet][:scheduledEndTime] = end_time.utc.iso8601
broadcast_options [ :status ] || = { }
broadcast_options [ :status ] [ :privacyStatus ] || = ( fan_access ? 'public' : 'private' )
broadcast_options [ :contentDetails ] || = { }
# if false, this causes a 'request not authorized error'
# From: https://developers.google.com/youtube/v3/live/docs/liveBroadcasts
# If your channel does not have permission to disable recordings, and you attempt to insert a broadcast with the recordFromStart property set to false, the API will return a Forbidden error.
#broadcast_options[:contentDetails][:recordFromStart] ||= false
broadcast_data = google_client . create_broadcast ( user , broadcast_options )
broadcast = Broadcast . new
broadcast . music_session_id = self . id
broadcast . user_id = user . id
broadcast . broadcast_id = broadcast_data [ " id " ]
2017-06-10 20:34:08 +00:00
broadcast . update_broadcast_data ( broadcast_data )
2017-04-14 19:34:18 +00:00
broadcast . save!
broadcast
end
2017-06-10 20:34:08 +00:00
def bind_broadcast ( user , broadcast , google_client = GoogleClient . new )
2017-04-14 19:34:18 +00:00
bind_data = google_client . bind_broadcast ( user , broadcast . broadcast_id , broadcast . stream_id )
2017-06-10 20:34:08 +00:00
broadcast . update_broadcast_data ( bind_data )
broadcast . save!
broadcast
end
# broadcastStatus one of complete, live, testing
def transition_broadcast ( user , broadcast , broadcastStatus , google_client = GoogleClient . new )
2017-06-23 23:39:22 +00:00
if broadcastStatus == 'testing' && ( broadcast . broadcast_status == 'testing' || broadcast . broadcast_status == 'live' || broadcast . broadcast_status == 'testStarting' || broadcast . broadcast_status == 'liveStarting' )
# short cut out; this in unnecessary
puts " SHORT CUT OUT OF TRANSITION TESTING TESTING "
return broadcast
end
if broadcastStatus == 'live' && broadcast . broadcast_status == 'live'
# short cut out; this in unnecessary
puts " SHORT CUT OUT OF TRANSITION TESTING LIVE "
return broadcast
end
2017-06-10 20:34:08 +00:00
bind_data = google_client . transition_broadcast ( user , broadcast . broadcast_id , broadcastStatus )
broadcast . update_broadcast_data ( bind_data )
2017-04-14 19:34:18 +00:00
broadcast . save!
broadcast
end
def youtube_times
start = scheduled_start_time
if start < Time . now
start = Time . now
end_time = start + safe_scheduled_duration
return [ start , end_time ]
else
return [ start , scheduled_end_time ]
end
end
2014-07-30 20:11:03 +00:00
def check_scheduling_info_changed
@scheduling_info_changed = scheduled_start_changed?
true
end
2014-07-11 22:21:55 +00:00
def update_scheduled_start
# it's very important that this only run if timezone changes, or scheduled_start changes
if self . scheduled_start && ( self . scheduled_start_changed? || self . timezone_changed? )
self . scheduled_start = MusicSession . parse_scheduled_start ( self . scheduled_start , self . timezone )
end
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
2014-07-11 06:23:10 +00:00
new_session . scheduled_start = self . scheduled_start + 1 . week
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
2020-05-06 19:42:52 +00:00
new_session . friends_can_join = self . friends_can_join
2014-06-03 05:10:25 +00:00
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-07-11 06:23:10 +00:00
new_session . is_unstructured_rsvp = self . is_unstructured_rsvp
new_session . legal_terms = true
2015-12-08 02:25:43 +00:00
new_session . session_controller = self . session_controller
2020-10-14 02:05:08 +00:00
new_session . school_id = self . school_id
new_session . is_platform_instructor = self . is_platform_instructor
2021-08-24 13:54:16 +00:00
new_session . use_video_conferencing_server = self . use_video_conferencing_server
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
2016-07-17 15:16:27 +00:00
RsvpSlot . where ( " music_session_id = ' #{ self . id } ' " ) . find_each do | slot |
2014-06-03 05:10:25 +00:00
new_slot = RsvpSlot . new
new_slot . instrument_id = slot . instrument_id
new_slot . proficiency_level = slot . proficiency_level
2014-07-30 01:52:08 +00:00
new_slot . is_unstructured_rsvp = slot . is_unstructured_rsvp
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
2020-04-29 20:51:50 +00:00
new_rsvp . music_session_id = rsvp . music_session_id
2014-06-04 05:47:28 +00:00
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-08-22 06:14:07 +00:00
# .last => new_slot above
new_session . rsvp_slots . last . rsvp_requests_rsvp_slots << new_rsvp_req_slot
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
2014-08-24 19:12:38 +00:00
rejected_req_slots = RsvpRequestRsvpSlot . where ( " (chosen is null OR chosen = FALSE) AND rsvp_slot_id = ? " , slot . id ) . order ( " created_at ASC " )
2014-06-05 03:17:45 +00:00
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
2020-04-29 20:51:50 +00:00
new_rsvp . music_session_id = rsvp . music_session_id
2014-06-05 03:17:45 +00:00
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-08-22 06:14:07 +00:00
new_rsvp_req_slot . chosen = nil
# .last => new_slot above
new_session . rsvp_slots . last . rsvp_requests_rsvp_slots << new_rsvp_req_slot
2014-06-05 03:17:45 +00:00
end
2014-06-04 05:47:28 +00:00
end
end
2014-06-03 05:10:25 +00:00
end
# copy music_notations
2016-07-17 15:16:27 +00:00
MusicNotation . where ( " music_session_id = ' #{ self . id } ' " ) . find_each do | notation |
2014-06-03 05:10:25 +00:00
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-07-11 06:23:10 +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-07-11 06:23:10 +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
2014-08-22 06:14:07 +00:00
return new_session
end
2014-06-03 05:10:25 +00:00
end
2016-04-06 02:23:15 +00:00
def is_lesson?
! ! lesson_session
end
2018-07-29 18:26:09 +00:00
# checks if this is a lesson and if the person indicated is a teacher or student
def is_lesson_member? ( user )
is_lesson? && ( lesson_session . teacher . id == user . id || lesson_session . student . id == user . id )
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 )
2014-09-14 19:52:17 +00:00
# see if user already exists in array
t = tracks . select { | track | track . musician . id == user . id } . first
if t . blank?
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
# this handles case where user is duplicated in MSUH for the same session
else
unless msuh . instruments . blank?
instruments = msuh . instruments . split ( SEPARATOR )
instruments . each do | instrument |
if ! t . instrument_ids . include? instrument
t . instrument_ids << instrument
end
2014-05-06 13:34:38 +00:00
end
end
end
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
2020-05-06 19:42:52 +00:00
# is the viewer 'friensd with the session?'
# practically, to be a friend with the session, you have to be a friend with the creator
# we could loosen to be friend of friends or friends with anyone in the session
def friends_with_session ( user )
self . creator . friends? ( user )
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
2016-07-17 15:16:27 +00:00
return self . invited_musicians . exists? ( user . id ) || self . approved_rsvps . include? ( user )
2014-05-24 07:58:47 +00:00
else
return true
end
else
# the creator can always join, and the invited users can join
2020-05-06 19:42:52 +00:00
return self . creator == user || self . invited_musicians . exists? ( user . id ) || self . approved_rsvps . include? ( user ) || ( self . friends_can_join && self . friends_with_session ( user ) )
2014-05-24 07:58:47 +00:00
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-09-08 21:51:28 +00:00
def can_see? user
if self . musician_access || self . fan_access
true
else
2020-05-06 19:42:52 +00:00
self . creator == user || self . invited_musicians . exists? ( user . id ) || self . approved_rsvps . include? ( user ) || self . creator . friends? ( user ) || self . has_lesson_access? ( user )
2014-09-08 21:51:28 +00:00
end
end
2017-10-18 02:36:53 +00:00
def has_lesson_access? ( user )
if is_lesson?
puts " HAS LESSON ACCESS "
result = lesson_session . has_access? ( user )
puts " HAS LESSON ACCESS #{ result } "
result
else
false
end
end
2015-12-08 02:25:43 +00:00
def set_session_controller ( current_user , user )
# only allow update of session controller by the creator or the currently marked user
should_tick = false
if current_user != creator && current_user != self . session_controller
return should_tick
end
if active_music_session
if user
if active_music_session . users . exists? ( user )
self . session_controller = user
should_tick = save
end
else
self . session_controller = nil
should_tick = save
end
end
should_tick
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
2024-11-07 13:35:51 +00:00
# list all unique participants in a session, for any session that the current_user was once a part of
2024-11-06 02:04:28 +00:00
def self . history ( current_user , options )
2024-05-03 02:55:51 +00:00
offset = options [ :offset ] || 0
limit = options [ :limit ] || 10
2024-11-06 02:04:28 +00:00
2024-11-07 13:35:51 +00:00
#This results in
# SELECT DISTINCT ON(music_sessions.created_at, music_sessions.id, users.id ) music_sessions.id AS session_id, music_sessions_user_history.id AS session_history_id, music_sessions.created_at, music_sessions.name, music_sessions.description, music_sessions.musician_access, music_sessions.approval_required, music_sessions_user_history.instruments, users.first_name, users.last_name, users.photo_url, users.id AS user_id FROM "music_sessions" INNER JOIN
# music_sessions_user_history
# ON
# music_sessions.id = music_sessions_user_history.music_session_id
# INNER JOIN
# users
# ON
# music_sessions_user_history.user_id = users.id
# WHERE music_sessions.id IN ( SELECT DISTINCT music_sessions_user_history.music_session_id FROM music_sessions_user_history where user_id = '1886af5c-b76e-466b-b8d1-30d98e549521) ORDER BY music_sessions.created_at DESC LIMIT 100 OFFSET 1
#
2024-05-05 13:08:29 +00:00
MusicSession . joins (
%Q{
INNER JOIN
music_sessions_user_history
ON
music_sessions . id = music_sessions_user_history . music_session_id
INNER JOIN
users
ON
music_sessions_user_history . user_id = users . id
2024-11-07 13:35:51 +00:00
WHERE music_sessions . id IN ( SELECT DISTINCT music_sessions_user_history . music_session_id FROM music_sessions_user_history where user_id = '#{current_user.id}' )
2024-05-05 13:08:29 +00:00
}
2024-11-07 13:35:51 +00:00
) . order ( 'music_sessions.created_at DESC' ) . select ( " DISTINCT ON(music_sessions.created_at, music_sessions.id, users.id ) music_sessions.id AS session_id, music_sessions_user_history.id AS session_history_id, music_sessions.created_at, music_sessions.name, music_sessions.description, music_sessions.musician_access, music_sessions.approval_required, music_sessions_user_history.instruments, users.first_name, users.last_name, users.photo_url, users.id AS user_id " ) . offset ( offset ) . limit ( limit )
2024-05-03 02:55:51 +00:00
end
2015-05-28 13:20:14 +00:00
def self . scheduled user , only_public = false
2014-09-19 04:24:38 +00:00
# keep unstarted sessions around for 12 hours after scheduled_start
session_not_started = " (music_sessions.scheduled_start > NOW() - '12 hour'::INTERVAL AND music_sessions.started_at IS NULL) "
2015-01-01 19:29:18 +00:00
# keep started sessions that are not finished yet
session_started_not_finished = " (music_sessions.started_at IS NOT NULL AND music_sessions.session_removed_at IS NULL) "
2014-09-19 04:24:38 +00:00
# let session be restarted for up to 2 hours after finishing
session_finished = " (music_sessions.session_removed_at > NOW() - '2 hour'::INTERVAL) "
2017-10-12 16:13:54 +00:00
query = MusicSession . select ( 'distinct music_sessions.*' )
query = query . joins (
2016-04-22 19:59:48 +00:00
%Q{
2017-10-12 16:13:54 +00:00
LEFT OUTER JOIN
rsvp_requests
2020-12-28 05:44:17 +00:00
ON rsvp_requests . music_session_id = music_sessions . id AND rsvp_requests . user_id = '#{user.id}' AND rsvp_requests . chosen = TRUE
2017-10-12 16:13:54 +00:00
2016-04-22 19:59:48 +00:00
LEFT OUTER JOIN
invitations
ON
music_sessions . id = invitations . music_session_id AND invitations . receiver_id = '#{user.id}'
}
)
2017-10-16 22:30:35 +00:00
query = query . where ( " music_sessions.old = FALSE " )
2016-04-22 19:59:48 +00:00
query = query . where ( " music_sessions.canceled = FALSE " )
2015-05-28 13:20:14 +00:00
query = query . where ( 'music_sessions.fan_access = TRUE or music_sessions.musician_access = TRUE' ) if only_public
2016-04-22 19:59:48 +00:00
#query = query.where("music_sessions.user_id = '#{user.id}' OR invitations.id IS NOT NULL")
2020-12-28 05:44:17 +00:00
query = query . where ( " (rsvp_requests.id IS NOT NULL) OR (invitations.id IS NOT NULL) OR (music_sessions.user_id = ' #{ user . id } ') " )
2017-10-12 16:13:54 +00:00
2020-10-14 02:05:08 +00:00
query = Search . scope_schools_together_sessions ( query , user , 'music_sessions' )
2015-01-01 19:29:18 +00:00
query = query . where ( " music_sessions.scheduled_start IS NULL OR #{ session_not_started } OR #{ session_finished } OR #{ session_started_not_finished } " )
2016-04-22 19:59:48 +00:00
query = query . where ( " music_sessions.create_type IS NULL OR (music_sessions.create_type != ' #{ CREATE_TYPE_QUICK_START } ' AND music_sessions.create_type != ' #{ CREATE_TYPE_QUICK_PUBLIC } ') " )
2014-05-21 19:11:50 +00:00
query = query . order ( " music_sessions.scheduled_start ASC " )
2014-10-21 07:27:26 +00:00
2014-06-24 15:33:31 +00:00
query
2014-05-21 19:11:50 +00:00
end
2014-07-12 13:55:58 +00:00
# if only_approved is set, then only return sessions where the current user has been chosen
def self . scheduled_rsvp ( user , only_approved = false )
2017-10-12 16:13:54 +00:00
filter_approved = only_approved ? 'AND rsvp_requests_rsvp_slots.chosen = true' : ''
2014-07-12 13:55:58 +00:00
2017-10-12 16:13:54 +00:00
query = MusicSession . joins (
%Q{
INNER JOIN
rsvp_slots
ON
music_sessions . id = rsvp_slots . music_session_id
INNER JOIN
rsvp_requests_rsvp_slots
ON
rsvp_requests_rsvp_slots . rsvp_slot_id = rsvp_slots . id
INNER JOIN
rsvp_requests
ON rsvp_requests . id = rsvp_requests_rsvp_slots . rsvp_request_id
}
)
2020-10-14 02:05:08 +00:00
query = Search . scope_schools_together_sessions ( query , user , 'music_sessions' )
2017-10-16 22:30:35 +00:00
query = query . where ( %Q{ music_sessions.old = FALSE AND music_sessions.canceled = FALSE AND
2016-04-22 19:59:48 +00:00
( music_sessions . create_type is NULL OR ( music_sessions . create_type != '#{CREATE_TYPE_QUICK_START}' AND music_sessions . create_type != '#{CREATE_TYPE_QUICK_PUBLIC}' ) ) AND
2017-10-12 16:13:54 +00:00
( music_sessions . scheduled_start is NULL OR music_sessions . scheduled_start > NOW ( ) - '4 hour' :: INTERVAL ) AND rsvp_requests . user_id = '#{user.id}' #{filter_approved}}
2014-07-11 22:21:55 +00:00
) . order ( :scheduled_start )
2017-10-12 16:13:54 +00:00
query
2014-06-29 13:54:51 +00:00
end
2014-05-19 15:35:38 +00:00
def self . create user , options
2014-12-03 07:08:26 +00:00
band = Band . where ( id : options [ :band ] ) . first if options [ :band ] . present?
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 ]
2020-12-27 23:58:31 +00:00
ms . friends_can_join = options [ :friends_can_join ] || false
2014-05-19 15:35:38 +00:00
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_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
2015-12-08 02:25:43 +00:00
ms . session_controller = user
2014-10-21 07:27:26 +00:00
ms . create_type = options [ :create_type ]
2014-07-08 18:34:03 +00:00
ms . is_unstructured_rsvp = options [ :isUnstructuredRsvp ] if options [ :isUnstructuredRsvp ]
2014-07-11 22:21:55 +00:00
ms . scheduled_start = parse_scheduled_start ( options [ :start ] , options [ :timezone ] ) if options [ :start ] && options [ :timezone ]
2016-04-06 02:23:15 +00:00
ms . scheduled_start = options [ :scheduled_start ] if options [ :scheduled_start ]
2020-10-14 02:05:08 +00:00
ms . school_id = user . school_id
ms . is_platform_instructor = user . is_platform_instructor
2016-04-06 02:23:15 +00:00
if options [ :lesson_session ]
ms . lesson_session = options [ :lesson_session ]
end
2021-08-24 13:54:16 +00:00
ms . use_video_conferencing_server = user . use_video_conferencing_server
2016-04-06 02:23:15 +00:00
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
2015-01-07 02:20:41 +00:00
# send scheduled session notification or join session notification
case ms . create_type
when CREATE_TYPE_RSVP , CREATE_TYPE_SCHEDULE_FUTURE
2014-10-13 12:40:10 +00:00
Notification . send_scheduled_session_invitation ( ms , receiver )
2016-04-06 02:23:15 +00:00
when CREATE_TYPE_LESSON
if ms . lesson_session . is_active?
Notification . send_jamclass_invitation_teacher ( ms , receiver )
end
2014-10-13 12:40:10 +00:00
else
Notification . send_session_invitation ( receiver , user , ms . id )
end
2015-01-07 02:20:41 +00:00
2014-05-19 15:35:38 +00:00
end if options [ :invitations ]
2014-09-06 07:30:29 +00:00
options [ :music_notations ] . each do | notation |
notation = MusicNotation . find ( notation [ :id ] )
2014-05-19 15:35:38 +00:00
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
2015-01-20 07:21:25 +00:00
2015-01-22 03:40:31 +00:00
# only get the active users if the session is in progress
2015-01-20 07:21:25 +00:00
user_filter = " music_sessions_user_history.session_removed_at is null " if self . session_removed_at . nil?
2014-05-06 13:34:38 +00:00
MusicSessionUserHistory
2015-01-20 07:21:25 +00:00
. 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 } ' } )
. where ( user_filter )
2012-11-02 06:51:52 +00:00
end
2014-05-06 13:34:38 +00:00
def duration_minutes
2014-09-14 23:17:16 +00:00
end_time = self . session_removed_at || Time . now . utc
2014-05-06 13:34:38 +00:00
( 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
2016-07-17 15:16:27 +00:00
user == self . creator || ( active_music_session ? active_music_session . users . exists? ( user . id ) : 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-07-30 02:42:35 +00:00
! active_music_session . nil? && ! active_music_session . mount . nil?
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-07-21 05:05:07 +00:00
case legal_policy . downcase
2014-05-24 07:58:47 +00:00
when " standard "
2014-07-21 05:05:07 +00:00
return " session-legal-policies/standard "
2014-05-24 07:58:47 +00:00
when " creative "
2014-07-21 05:05:07 +00:00
return " session-legal-policies/creativecommons "
2014-05-24 07:58:47 +00:00
when " offline "
2014-07-21 05:05:07 +00:00
return " session-legal-policies/offline "
2014-05-24 07:58:47 +00:00
when " jamtracks "
2014-07-21 05:05:07 +00:00
return " session-legal-policies/jamtracks "
2014-05-24 07:58:47 +00:00
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-07-01 04:36:06 +00:00
def scheduled_end_time
2017-04-14 19:34:18 +00:00
start = scheduled_start_time
duration = safe_scheduled_duration
start + duration
2014-07-01 04:36:06 +00:00
end
2014-07-12 01:54:54 +00:00
def timezone_id
MusicSession . split_timezone ( timezone ) [ 0 ]
end
2014-06-09 02:43:07 +00:00
def timezone_description
2014-07-12 01:54:54 +00:00
MusicSession . split_timezone ( timezone ) [ 1 ]
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-08-19 19:23:41 +00:00
User . find_by_sql ( %Q{ select distinct ON(u.id) u.id, u.photo_url, u.first_name, u.last_name, u.last_jam_audio_latency, 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
2016-07-17 15:16:27 +00:00
User . find_by_sql ( %Q{ select distinct u.id, u.email, u.photo_url, u.first_name, u.last_name, u.online
2014-05-28 02:35:26 +00:00
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
2017-10-18 02:36:53 +00:00
def running_recordings
recordings . where ( duration : nil )
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
2017-10-16 01:56:25 +00:00
def self . cleanup_old_sessions
2017-10-16 12:41:30 +00:00
old_scheduled_start = " (create_type is NOT NULL AND music_sessions.scheduled_start < NOW() - '24 hour'::INTERVAL) "
2017-10-16 01:56:25 +00:00
2017-10-16 12:41:30 +00:00
old_adhoc_sessions = " (create_type IS NULL and music_sessions.created_at < NOW() - '24 hour'::INTERVAL) "
2017-10-16 01:56:25 +00:00
2017-10-19 14:42:08 +00:00
MusicSession . where ( " #{ old_scheduled_start } OR #{ old_adhoc_sessions } " ) . where ( old : false ) . update_all ( old : true )
2017-10-16 01:56:25 +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
2025-04-02 01:15:23 +00:00
if music_session_user_history . duration_minutes > 15 && music_session_user_history . max_concurrent_connections . to_i > = 2
2014-05-06 13:34:38 +00:00
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
2014-09-29 02:37:52 +00:00
def self . sms_init ( current_user , options = { } , include_pending = false )
2014-08-18 15:37:55 +00:00
session_id = options [ :session_id ] || 'any'
2014-06-17 19:10:24 +00:00
2014-08-18 15:37:55 +00:00
my_locidispid = current_user . last_jam_locidispid
2014-06-17 19:10:24 +00:00
# 13 is an average audio gear value we use if they have not qualified any gear
2014-08-18 15:37:55 +00:00
my_audio_latency = current_user . last_jam_audio_latency || 13
2014-07-23 20:11:02 +00:00
locidispid_expr = my_locidispid ? " #{ my_locidispid } ::bigint " : '0::bigint' # Have to pass in zero; NULL fails silently in the stored proc
2014-06-17 19:10:24 +00:00
2014-09-29 02:37:52 +00:00
self . connection . execute ( " SELECT sms_index(' #{ current_user . id } '::varchar, #{ locidispid_expr } , #{ my_audio_latency } ::integer, #{ ActiveRecord :: Base . connection . quote ( session_id ) } ::varchar, #{ include_pending } ::boolean) " ) . check
2014-06-17 19:10:24 +00:00
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 )
2016-04-22 19:59:48 +00:00
query = query . where ( " music_sessions.create_type IS NULL OR (music_sessions.create_type != ? AND music_sessions.create_type != ? AND music_sessions.create_type != ?) " , MusicSession :: CREATE_TYPE_QUICK_START , MusicSession :: CREATE_TYPE_IMMEDIATE , MusicSession :: CREATE_TYPE_QUICK_PUBLIC )
2014-06-17 22:16:49 +00:00
query = query . where ( " music_sessions.genre_id = ? " , genre ) unless genre . blank?
query = query . where ( 'music_sessions.language = ?' , lang ) unless lang . blank?
2014-08-15 02:28:30 +00:00
query = query . where ( " (description_tsv @@ to_tsquery('jamenglish', ?)) " , ActiveRecord :: Base . connection . quote ( keyword ) + ':*' ) unless keyword . blank?
2014-06-17 22:16:49 +00:00
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
2014-10-12 07:49:31 +00:00
else
2014-10-14 03:54:58 +00:00
sql = <<SQL
music_sessions . started_at IS NULL AND
( music_sessions . created_at > NOW ( ) - interval '#{UNSTARTED_INTERVAL_DAYS_SKIP} days' OR
music_sessions . scheduled_start > NOW ( ) - interval '#{UNSTARTED_INTERVAL_DAYS_SKIP} days' )
SQL
query = query . where ( sql )
2014-06-17 19:10:24 +00:00
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
2014-08-18 15:37:55 +00:00
return User . select ( 'users.*, sms_users_tmp.music_session_id, sms_users_tmp.full_score, sms_users_tmp.audio_latency, sms_users_tmp.internet_score' )
2014-06-17 19:10:24 +00:00
. 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
2021-02-01 17:58:25 +00:00
# NOTE: Unused anymore!!
#
2014-06-17 19:10:24 +00:00
# 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 |
2014-08-18 15:37:55 +00:00
user_scores [ user . id ] = { full_score : user . full_score , audio_latency : user . audio_latency , internet_score : user . internet_score }
2014-06-17 19:10:24 +00:00
end
2014-08-18 15:37:55 +00:00
2014-06-17 19:10:24 +00:00
[ music_sessions , user_scores ]
end
2020-05-06 19:42:52 +00:00
def self . scheduled_index ( user , options )
session_id = options [ :session_id ]
genre = options [ :genre ]
lang = options [ :lang ]
keyword = options [ :keyword ]
offset = options [ :offset ]
limit = options [ :limit ]
day = options [ :day ]
timezone_offset = options [ :timezone_offset ]
query = MusicSession . select ( 'music_sessions.*' )
query = query . where ( " old = FALSE " )
query = query . where ( " scheduled_start IS NULL OR scheduled_start > (NOW() - (interval '15 minute')) " )
query = query . where ( " music_sessions.canceled = FALSE " )
query = query . where ( " description != 'Jam Track Session' " )
query = query . where ( " music_sessions.id NOT IN (SELECT id FROM active_music_sessions) " )
# one flaw in the join below is that we only consider if the creator of the session has asked you to be your friend, but you have not accepted. While this means you may always see a session by someone you haven't friended, the goal is to match up new users more quickly
query = query . joins (
%Q{
LEFT OUTER JOIN
rsvp_requests
ON rsvp_requests . music_session_id = music_sessions . id and rsvp_requests . user_id = '#{user.id}' AND rsvp_requests . chosen = true
LEFT OUTER JOIN
invitations
ON
music_sessions . id = invitations . music_session_id AND invitations . receiver_id = '#{user.id}'
LEFT OUTER JOIN
friendships
ON
music_sessions . user_id = friendships . user_id AND friendships . friend_id = '#{user.id}'
LEFT OUTER JOIN
friendships as friendships_2
ON
music_sessions . user_id = friendships_2 . friend_id AND friendships_2 . user_id = '#{user.id}'
}
)
# keep only rsvp/invitation/friend results. Nice tailored active list now!
query = query . where ( " open_rsvps = TRUE OR rsvp_requests.id IS NOT NULL OR invitations.id IS NOT NULL or music_sessions.user_id = ' #{ user . id } ' OR (friendships.id IS NOT NULL AND friendships_2.id IS NOT NULL) " )
2020-10-14 02:05:08 +00:00
query = Search . scope_schools_together_sessions ( query , user , 'music_sessions' )
2020-05-06 19:42:52 +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 ( 'music_sessions.id = ?' , session_id ) unless session_id . blank?
query = query . where ( " (description_tsv @@ to_tsquery('jamenglish', ?)) " , ActiveRecord :: Base . connection . quote ( keyword ) + ':*' ) unless keyword . blank?
query = query . group ( " music_sessions.id, music_sessions.scheduled_start " )
query = query . order ( " music_sessions.scheduled_start DESC " )
if ! day . blank? && ! timezone_offset . blank?
begin
day = Date . parse ( day )
next_day = day + 1
timezone_offset = timezone_offset . to_i
if timezone_offset == 0
timezone_offset = '' # no offset to specify in this case
elsif timezone_offset > 0
timezone_offset = " + #{ timezone_offset } "
end
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 } " )
end
else
sql = <<SQL
music_sessions . started_at IS NULL AND
( music_sessions . created_at > NOW ( ) - interval '#{UNSTARTED_INTERVAL_DAYS_SKIP} days' OR
music_sessions . scheduled_start > NOW ( ) - interval '#{UNSTARTED_INTERVAL_DAYS_SKIP} days' )
SQL
query = query . where ( sql )
end
#FROM music_sessions
# WHERE old = FALSE AND (scheduled_start IS NULL OR scheduled_start > (NOW() - (interval '15 minute')))
# AND canceled = FALSE AND description != 'Jam Track Session'
# AND id NOT IN (SELECT id FROM active_music_sessions);
query
end
2014-08-18 15:37:55 +00:00
# returns a single session, but populates any other user info with latency scores, so that show_history.rabl can do it's business
2014-09-29 02:37:52 +00:00
def self . session_with_scores ( current_user , music_session_id , include_pending = false )
MusicSession . sms_init ( current_user , { session_id : music_session_id } , include_pending )
2014-08-18 15:37:55 +00:00
music_session = MusicSession . find ( music_session_id )
music_session_users = MusicSession . sms_users . all
user_scores = { }
music_session_users . each do | user |
user_scores [ user . id ] = { full_score : user . full_score , audio_latency : user . audio_latency , internet_score : user . internet_score }
end
[ music_session , user_scores ]
end
2014-07-12 13:55:58 +00:00
def self . upcoming_sessions
end
2014-07-11 22:21:55 +00:00
# converts the passed scheduled_start into the database timezone using the specified timezone offset.
2014-07-11 03:08:03 +00:00
# timezone comes in as TIMEZONE DISPLAY, TIMEZONE ID
def self . parse_scheduled_start ( scheduled_start , timezone_param )
result = scheduled_start
2014-07-12 01:54:54 +00:00
tz_identifier = split_timezone ( timezone_param ) [ 0 ]
begin
timezone = ActiveSupport :: TimeZone . new ( tz_identifier )
rescue Exception = > e
@@log . error ( " unable to find timezone= #{ tz_identifier } , e= #{ e } " )
2016-07-17 15:16:27 +00:00
puts " unable to find timezone= #{ tz_identifier } , e= #{ e } "
2014-07-12 01:54:54 +00:00
end
2014-07-11 22:21:55 +00:00
2014-07-12 01:54:54 +00:00
if timezone
begin
# first convert the time provided, and convert to the specified timezone (local_to_utc)
# then, convert that to the system timezone, under the ASSUMPTION that the database is configured to use the system timezone
# you can get into trouble if your dev database is not using the system timezone of the web machine
2014-07-11 22:21:55 +00:00
2014-07-12 01:54:54 +00:00
result = timezone . parse ( scheduled_start )
rescue Exception = > e
@@log . error ( " unable to convert #{ scheduled_start } to #{ timezone } , e= #{ e } " )
puts " unable to convert #{ scheduled_start } to #{ timezone } , e= #{ e } "
2014-07-11 03:08:03 +00:00
end
end
result
end
2014-06-17 19:10:24 +00:00
2016-04-21 14:23:29 +00:00
def scheduled_start_date
2014-07-14 18:40:00 +00:00
if self . scheduled_start_time . blank?
" "
else
self . scheduled_start . strftime " %a %e %B %Y "
end
end
2014-07-11 22:21:55 +00:00
def scheduled_start_time
2014-07-14 18:40:00 +00:00
if scheduled_start && scheduled_duration
start_time = scheduled_start
tz_identifier , tz_display = MusicSession . split_timezone ( timezone )
begin
tz = TZInfo :: Timezone . get ( tz_identifier )
rescue Exception = > e
@@log . error ( " unable to find timezone= #{ tz_identifier } , e= #{ e } " )
end
if tz
begin
start_time = tz . utc_to_local ( scheduled_start . utc )
rescue Exception = > e
@@log . error ( " unable to convert #{ scheduled_start } to #{ tz } , e= #{ e } " )
puts " unable to convert #{ e } "
end
end
start_time
2014-07-11 22:21:55 +00:00
else
" "
end
end
2014-07-12 01:54:54 +00:00
# takes our stored timezone which is DISPLAY,ID and returns an array of the two values (id first, then description)
def self . split_timezone ( tz )
result = [ nil , nil ]
if tz
index = tz . rindex ( ',' )
if index
tz_display = tz [ 0 , index ]
tz_identifier = tz [ ( index + 1 ) .. - 1 ]
result = [ tz_identifier , tz_display ]
end
end
result
end
2015-07-06 20:34:27 +00:00
def safe_scheduled_duration
2016-07-17 15:16:27 +00:00
duration = scheduled_duration
2015-07-06 20:34:27 +00:00
# you can put seconds into the scheduled_duration field, but once stored, it comes back out as a string
if scheduled_duration . class == String
2016-07-17 15:16:27 +00:00
duration = scheduled_duration . to_i . seconds
2015-07-06 20:34:27 +00:00
end
2017-02-13 02:49:33 +00:00
if duration == 0
duration = 30 * 60
end
2015-07-06 20:34:27 +00:00
duration
end
2016-04-21 14:23:29 +00:00
2018-02-15 04:16:32 +00:00
def pretty_scheduled_start ( with_timezone = true , shorter = false , user_tz = nil )
2014-07-12 02:11:34 +00:00
2014-07-12 01:54:54 +00:00
if scheduled_start && scheduled_duration
2014-07-11 22:21:55 +00:00
start_time = scheduled_start
timezone_display = 'UTC'
2016-04-21 14:23:29 +00:00
utc_offset_display = '00:00'
2014-07-12 01:54:54 +00:00
tz_identifier , tz_display = MusicSession . split_timezone ( timezone )
2016-04-21 14:23:29 +00:00
short_tz = 'GMT'
2018-02-15 04:16:32 +00:00
if user_tz
tz_identifier = user_tz
end
2014-07-12 01:54:54 +00:00
begin
tz = TZInfo :: Timezone . get ( tz_identifier )
rescue Exception = > e
@@log . error ( " unable to find timezone= #{ tz_identifier } , e= #{ e } " )
end
if tz
2014-07-11 22:21:55 +00:00
begin
2014-07-12 01:54:54 +00:00
start_time = tz . utc_to_local ( scheduled_start . utc )
2016-05-19 19:12:43 +00:00
timezone_display = tz . pretty_name
2016-04-21 14:23:29 +00:00
utc_offset_hours = tz . current_period . utc_total_offset / ( 60 * 60 )
hour = sprintf '%02d' , utc_offset_hours . abs
minutes = sprintf '%02d' , ( ( tz . current_period . utc_total_offset . abs % 3600 ) / 3600 ) * 60
utc_offset_display = " #{ utc_offset_hours < 0 ? '-' : ' ' } #{ hour } : #{ minutes } "
short_tz = start_time . strftime ( " %Z " )
if short_tz == 'UTC'
short_tz = 'GMT'
end
2014-07-11 22:21:55 +00:00
rescue Exception = > e
2014-07-12 01:54:54 +00:00
@@log . error ( " unable to convert #{ scheduled_start } to #{ tz } , e= #{ e } " )
puts " unable to convert #{ e } "
2014-07-11 22:21:55 +00:00
end
end
2014-07-12 01:54:54 +00:00
2016-05-18 03:11:20 +00:00
2015-07-06 20:34:27 +00:00
duration = safe_scheduled_duration
2014-07-11 22:21:55 +00:00
end_time = start_time + duration
if with_timezone
2016-04-21 14:23:29 +00:00
if shorter
2016-05-18 03:11:20 +00:00
#"#{start_time.strftime("%a, %b %e %Y")}, #{start_time.strftime("%l:%M").strip}-#{end_time.strftime("%l:%M %p").strip} (#{short_tz}#{utc_offset_display})"
" #{ start_time . strftime ( " %a, %b %e %Y " ) } , #{ start_time . strftime ( " %l:%M " ) . strip } - #{ end_time . strftime ( " %l:%M %p " ) . strip } ( #{ timezone_display } ) "
2016-04-21 14:23:29 +00:00
else
" #{ start_time . strftime ( " %A, %B %e " ) } , #{ start_time . strftime ( " %l:%M " ) . strip } - #{ end_time . strftime ( " %l:%M %p " ) . strip } #{ timezone_display } "
end
2014-07-11 22:21:55 +00:00
else
2017-02-13 02:49:33 +00:00
" #{ start_time . strftime ( " %A, %B %e " ) } - #{ end_time . strftime ( " %l:%M%P " ) . strip } "
2014-07-11 22:21:55 +00:00
end
else
" Date and time TBD "
end
end
2014-10-12 07:49:31 +00:00
def self . purgeable_sessions
sessions = [ ]
sql = <<SQL
2016-05-24 19:25:50 +00:00
( started_at IS NULL AND
2014-10-14 03:54:58 +00:00
( created_at < = NOW ( ) - interval '#{UNSTARTED_INTERVAL_DAYS_PURGE} days' OR
scheduled_start < = NOW ( ) - interval '#{UNSTARTED_INTERVAL_DAYS_PURGE} days' ) )
2014-10-12 07:49:31 +00:00
OR
( recurring_mode = '#{NO_RECURRING}' AND
scheduled_start < = NOW ( ) - interval '#{UNSTARTED_INTERVAL_DAYS_PURGE} days' )
2014-10-14 03:54:58 +00:00
OR
( recurring_mode = '#{RECURRING_WEEKLY}' AND
scheduled_start < = NOW ( ) - interval '#{UNSTARTED_INTERVAL_DAYS_PURGE_RECUR} days' )
2014-10-12 07:49:31 +00:00
SQL
2016-05-24 19:25:50 +00:00
self . where ( " lesson_session_id is NULL " ) . where ( " started_at IS NULL " ) . where ( sql ) . find_each do | ms |
2014-10-12 07:49:31 +00:00
block_given? ? yield ( ms ) : sessions << ms
end
sessions
end
2017-07-10 02:21:29 +00:00
def admin_url
APP_CONFIG . admin_root_url + " /admin/music_sessions/ " + id
end
2014-07-11 22:21:55 +00:00
2014-06-17 19:10:24 +00:00
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
2014-07-11 22:21:55 +00:00
def validate_timezone
if timezone
index = timezone . rindex ( ',' )
if index
tz_identifier = timezone [ ( index + 1 ) .. - 1 ]
begin
TZInfo :: Timezone . get ( tz_identifier )
rescue Exception = > e
@@log . error ( " unable to find timezone= #{ tz_identifier } , e= #{ e } " )
errors . add ( :timezone , ValidationMessages :: MUST_BE_KNOWN_TIMEZONE )
end
end
end
end
2012-10-03 03:50:23 +00:00
end
end